@onekeyfe/hd-transport-web-device 1.2.0-alpha.22 → 1.2.0-alpha.24
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 +102 -2
- package/__tests__/webusb-protocol-v2-timeout.test.ts +32 -0
- package/dist/electron-ble-transport.d.ts +1 -0
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -23
- package/dist/webusb.d.ts +1 -0
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +16 -10
- package/src/webusb.ts +34 -13
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
|
|
2
2
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
import EventEmitter from 'events';
|
|
3
4
|
|
|
4
5
|
import ElectronBleTransport from '../src/electron-ble-transport';
|
|
5
6
|
|
|
@@ -111,7 +112,10 @@ const createNobleBle = (device = { id: 'flaky-pro2-id', name: 'Unknown BLE Devic
|
|
|
111
112
|
),
|
|
112
113
|
});
|
|
113
114
|
|
|
114
|
-
const configureTransport = (
|
|
115
|
+
const configureTransport = (
|
|
116
|
+
nobleBle: ReturnType<typeof createNobleBle>,
|
|
117
|
+
emitter?: EventEmitter
|
|
118
|
+
) => {
|
|
115
119
|
(global as any).window = {
|
|
116
120
|
desktopApi: {
|
|
117
121
|
nobleBle,
|
|
@@ -119,7 +123,7 @@ const configureTransport = (nobleBle: ReturnType<typeof createNobleBle>) => {
|
|
|
119
123
|
};
|
|
120
124
|
|
|
121
125
|
const transport = new ElectronBleTransport();
|
|
122
|
-
transport.init(createLogger());
|
|
126
|
+
transport.init(createLogger(), emitter);
|
|
123
127
|
transport.configure(protocolV1Schema);
|
|
124
128
|
transport.configureProtocolV2(protocolV2Schema);
|
|
125
129
|
return transport;
|
|
@@ -131,6 +135,54 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
131
135
|
jest.clearAllMocks();
|
|
132
136
|
});
|
|
133
137
|
|
|
138
|
+
test('keeps raw BLE lifecycle payloads off the public device event channel', async () => {
|
|
139
|
+
const device = { id: 'lifecycle-pro2-id', name: 'OneKey Pro 2' };
|
|
140
|
+
const nobleBle = createNobleBle(device);
|
|
141
|
+
const emitter = new EventEmitter();
|
|
142
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
143
|
+
let disconnectHandler: ((device: { id: string; name: string | null }) => void) | undefined;
|
|
144
|
+
let responseSeq = 0;
|
|
145
|
+
|
|
146
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
147
|
+
notificationHandler = handler;
|
|
148
|
+
return jest.fn();
|
|
149
|
+
});
|
|
150
|
+
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
151
|
+
disconnectHandler = handler;
|
|
152
|
+
return jest.fn();
|
|
153
|
+
});
|
|
154
|
+
nobleBle.write.mockImplementation(() => {
|
|
155
|
+
responseSeq += 1;
|
|
156
|
+
const response = ProtocolV2.encodeFrame(
|
|
157
|
+
schemas,
|
|
158
|
+
'Success',
|
|
159
|
+
{ message: 'ok' },
|
|
160
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
161
|
+
);
|
|
162
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const publicConnect = jest.fn();
|
|
167
|
+
const publicDisconnect = jest.fn();
|
|
168
|
+
const transportDisconnect = jest.fn();
|
|
169
|
+
emitter.on('device-connect', publicConnect);
|
|
170
|
+
emitter.on('device-disconnect', publicDisconnect);
|
|
171
|
+
emitter.on('transport-device-disconnect', transportDisconnect);
|
|
172
|
+
const bleTransport = configureTransport(nobleBle, emitter);
|
|
173
|
+
|
|
174
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
175
|
+
disconnectHandler?.(device);
|
|
176
|
+
|
|
177
|
+
expect(publicConnect).not.toHaveBeenCalled();
|
|
178
|
+
expect(publicDisconnect).not.toHaveBeenCalled();
|
|
179
|
+
expect(transportDisconnect).toHaveBeenCalledWith({
|
|
180
|
+
id: device.id,
|
|
181
|
+
connectId: device.id,
|
|
182
|
+
name: device.name,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
134
186
|
test('detects Protocol V2 after Protocol V1 probe timeout', async () => {
|
|
135
187
|
const device = { id: 'unknown-pro2-id', name: 'Unknown BLE Device' };
|
|
136
188
|
const nobleBle = createNobleBle(device);
|
|
@@ -341,4 +393,52 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
341
393
|
await transport.release(device.id);
|
|
342
394
|
}
|
|
343
395
|
});
|
|
396
|
+
|
|
397
|
+
test('preserves the active Protocol V2 link when the same schema is configured again', async () => {
|
|
398
|
+
const device = { id: 'stable-schema-pro2-id', name: 'OneKey Pro 2' };
|
|
399
|
+
const nobleBle = createNobleBle(device);
|
|
400
|
+
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
401
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
402
|
+
notificationHandler = handler;
|
|
403
|
+
return jest.fn();
|
|
404
|
+
});
|
|
405
|
+
let responseSeq = 0;
|
|
406
|
+
nobleBle.write.mockImplementation(() => {
|
|
407
|
+
responseSeq += 1;
|
|
408
|
+
const response = ProtocolV2.encodeFrame(
|
|
409
|
+
schemas,
|
|
410
|
+
'Success',
|
|
411
|
+
{ message: 'ok' },
|
|
412
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
413
|
+
);
|
|
414
|
+
setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
|
|
415
|
+
return Promise.resolve();
|
|
416
|
+
});
|
|
417
|
+
const bleTransport = configureTransport(nobleBle);
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
421
|
+
const invalidateAllLinks = jest.spyOn(
|
|
422
|
+
(bleTransport as any).protocolV2Links,
|
|
423
|
+
'invalidateAllLinks'
|
|
424
|
+
);
|
|
425
|
+
bleTransport.configureProtocolV2(protocolV2Schema);
|
|
426
|
+
await new Promise<void>(resolve => {
|
|
427
|
+
setTimeout(resolve, 0);
|
|
428
|
+
});
|
|
429
|
+
expect(invalidateAllLinks).not.toHaveBeenCalled();
|
|
430
|
+
await expect(
|
|
431
|
+
bleTransport.call(device.id, 'Ping', { message: 'same-schema' })
|
|
432
|
+
).resolves.toEqual({
|
|
433
|
+
type: 'Success',
|
|
434
|
+
message: { message: 'ok' },
|
|
435
|
+
});
|
|
436
|
+
const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
|
|
437
|
+
Number.parseInt(hex.slice(12, 14), 16)
|
|
438
|
+
);
|
|
439
|
+
expect(sentSeqs).toEqual([1, 2]);
|
|
440
|
+
} finally {
|
|
441
|
+
await bleTransport.release(device.id);
|
|
442
|
+
}
|
|
443
|
+
});
|
|
344
444
|
});
|
|
@@ -43,6 +43,38 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
43
43
|
expect(webusb.deviceProtocol.get(path)).toBe('V2');
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
test('retries an expected Protocol V2 probe once after resetting the connection', async () => {
|
|
47
|
+
const webusb = new WebUsbTransport() as any;
|
|
48
|
+
const path = 'pro2-webusb';
|
|
49
|
+
webusb.probeProtocolV1 = jest.fn();
|
|
50
|
+
webusb.probeProtocolV2 = jest.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true);
|
|
51
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
|
|
52
|
+
|
|
53
|
+
await expect(webusb.detectProtocol(path, 'V2')).resolves.toBe('V2');
|
|
54
|
+
|
|
55
|
+
expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
|
|
56
|
+
expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
|
|
57
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
|
|
58
|
+
expect(webusb.deviceProtocol.get(path)).toBe('V2');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('reports a Protocol V2 probe timeout only after the bounded retry is exhausted', async () => {
|
|
62
|
+
const webusb = new WebUsbTransport() as any;
|
|
63
|
+
const path = 'pro2-webusb';
|
|
64
|
+
webusb.probeProtocolV1 = jest.fn();
|
|
65
|
+
webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
|
|
66
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
|
|
67
|
+
|
|
68
|
+
await expect(webusb.detectProtocol(path, 'V2')).rejects.toThrow(
|
|
69
|
+
'Protocol V2 probe timeout after 2 attempts'
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
|
|
73
|
+
expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
|
|
74
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(2);
|
|
75
|
+
expect(webusb.deviceProtocol.has(path)).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
46
78
|
test('invalidates and resets the cached connection before another call can start', async () => {
|
|
47
79
|
const webusb = new WebUsbTransport() as any;
|
|
48
80
|
const path = 'pro2-webusb';
|
|
@@ -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":";AAqBA,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,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,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;IAgB7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAoF9B,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
|
@@ -88,6 +88,7 @@ declare class WebUsbTransport {
|
|
|
88
88
|
* fall back to a Protocol V2 Ping probe.
|
|
89
89
|
*/
|
|
90
90
|
private createProtocolMismatchError;
|
|
91
|
+
private createProtocolProbeTimeoutError;
|
|
91
92
|
private createProtocolDetectionError;
|
|
92
93
|
private detectProtocol;
|
|
93
94
|
/**
|
|
@@ -170,6 +171,7 @@ type BleAcquireInput = {
|
|
|
170
171
|
declare class ElectronBleTransport {
|
|
171
172
|
private _messages;
|
|
172
173
|
private _messagesV2;
|
|
174
|
+
private protocolV2SchemaConfiguration;
|
|
173
175
|
name: string;
|
|
174
176
|
configured: boolean;
|
|
175
177
|
runPromise: Deferred<Uint8Array | string> | null;
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
60
60
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
61
61
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
62
62
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
63
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
63
64
|
function inferProtocolHintFromDeviceName$1(name) {
|
|
64
65
|
return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
|
|
65
66
|
}
|
|
@@ -183,10 +184,14 @@ class WebUsbTransport {
|
|
|
183
184
|
createProtocolMismatchError(expected) {
|
|
184
185
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
|
|
185
186
|
}
|
|
187
|
+
createProtocolProbeTimeoutError(expected, attempts) {
|
|
188
|
+
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
|
|
189
|
+
}
|
|
186
190
|
createProtocolDetectionError() {
|
|
187
191
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
|
|
188
192
|
}
|
|
189
193
|
detectProtocol(path, expectedProtocol, protocolHint) {
|
|
194
|
+
var _a;
|
|
190
195
|
return __awaiter(this, void 0, void 0, function* () {
|
|
191
196
|
if (expectedProtocol === 'V1') {
|
|
192
197
|
if (yield this.probeProtocolV1(path)) {
|
|
@@ -197,11 +202,18 @@ class WebUsbTransport {
|
|
|
197
202
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
198
203
|
}
|
|
199
204
|
if (expectedProtocol === 'V2') {
|
|
200
|
-
|
|
201
|
-
this.
|
|
202
|
-
|
|
205
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
206
|
+
if (yield this.probeProtocolV2(path)) {
|
|
207
|
+
this.deviceProtocol.set(path, 'V2');
|
|
208
|
+
return 'V2';
|
|
209
|
+
}
|
|
210
|
+
yield this.resetConnectionAfterProbe(path);
|
|
211
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
212
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 probe timed out, retrying ${attempt + 1}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`);
|
|
213
|
+
}
|
|
203
214
|
}
|
|
204
|
-
|
|
215
|
+
this.deviceProtocol.delete(path);
|
|
216
|
+
throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
|
|
205
217
|
}
|
|
206
218
|
const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
207
219
|
for (const protocol of probeOrder) {
|
|
@@ -210,9 +222,7 @@ class WebUsbTransport {
|
|
|
210
222
|
this.deviceProtocol.set(path, protocol);
|
|
211
223
|
return protocol;
|
|
212
224
|
}
|
|
213
|
-
|
|
214
|
-
yield this.resetConnectionAfterProbe(path);
|
|
215
|
-
}
|
|
225
|
+
yield this.resetConnectionAfterProbe(path);
|
|
216
226
|
}
|
|
217
227
|
this.deviceProtocol.delete(path);
|
|
218
228
|
throw this.createProtocolDetectionError();
|
|
@@ -574,7 +584,7 @@ class WebUsbTransport {
|
|
|
574
584
|
return false;
|
|
575
585
|
}
|
|
576
586
|
return transport.probeProtocolV2({
|
|
577
|
-
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
587
|
+
call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
|
|
578
588
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
579
589
|
logger: this.Log,
|
|
580
590
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -624,7 +634,7 @@ class WebUsbTransport {
|
|
|
624
634
|
return check$1.call(jsonData);
|
|
625
635
|
});
|
|
626
636
|
}
|
|
627
|
-
callProtocolV2(path, name, data, options) {
|
|
637
|
+
callProtocolV2(path, name, data, options, resetOnError = true) {
|
|
628
638
|
return __awaiter(this, void 0, void 0, function* () {
|
|
629
639
|
const protocolV1Messages = this.messages;
|
|
630
640
|
if (!this.messagesV2) {
|
|
@@ -659,7 +669,7 @@ class WebUsbTransport {
|
|
|
659
669
|
return yield session.call(name, data, options);
|
|
660
670
|
}
|
|
661
671
|
catch (error) {
|
|
662
|
-
if (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
|
|
672
|
+
if (resetOnError && (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
|
|
663
673
|
try {
|
|
664
674
|
yield this.resetConnectionAfterProbe(path);
|
|
665
675
|
}
|
|
@@ -863,10 +873,18 @@ class ElectronBleTransport {
|
|
|
863
873
|
this.configured = true;
|
|
864
874
|
}
|
|
865
875
|
configureProtocolV2(signedData) {
|
|
876
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
877
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
866
881
|
this._messagesV2 = parseConfigure(signedData);
|
|
867
|
-
this.
|
|
868
|
-
|
|
869
|
-
|
|
882
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
883
|
+
if (isReconfiguration) {
|
|
884
|
+
this.protocolV2Links
|
|
885
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
886
|
+
.catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
|
|
887
|
+
}
|
|
870
888
|
}
|
|
871
889
|
listen() {
|
|
872
890
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -898,7 +916,7 @@ class ElectronBleTransport {
|
|
|
898
916
|
});
|
|
899
917
|
}
|
|
900
918
|
acquire(input) {
|
|
901
|
-
var _a, _b, _c, _d, _e
|
|
919
|
+
var _a, _b, _c, _d, _e;
|
|
902
920
|
return __awaiter(this, void 0, void 0, function* () {
|
|
903
921
|
const { uuid, forceCleanRunPromise, expectedProtocol } = input;
|
|
904
922
|
if (!uuid) {
|
|
@@ -943,7 +961,7 @@ class ElectronBleTransport {
|
|
|
943
961
|
var _a;
|
|
944
962
|
if (disconnectedDevice.id === uuid) {
|
|
945
963
|
this.cleanupDeviceState(uuid);
|
|
946
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(
|
|
964
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
947
965
|
name: disconnectedDevice.name,
|
|
948
966
|
id: disconnectedDevice.id,
|
|
949
967
|
connectId: disconnectedDevice.id,
|
|
@@ -952,23 +970,18 @@ class ElectronBleTransport {
|
|
|
952
970
|
});
|
|
953
971
|
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
954
972
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
955
|
-
(_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
|
|
956
|
-
name: device.name,
|
|
957
|
-
id: device.id,
|
|
958
|
-
connectId: device.id,
|
|
959
|
-
});
|
|
960
973
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
961
974
|
}
|
|
962
975
|
catch (error) {
|
|
963
|
-
(
|
|
976
|
+
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] acquire failed:', error);
|
|
964
977
|
try {
|
|
965
|
-
if (((
|
|
978
|
+
if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
|
|
966
979
|
yield window.desktopApi.nobleBle.unsubscribe(uuid);
|
|
967
980
|
yield window.desktopApi.nobleBle.disconnect(uuid);
|
|
968
981
|
}
|
|
969
982
|
}
|
|
970
983
|
catch (cleanupError) {
|
|
971
|
-
(
|
|
984
|
+
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
|
|
972
985
|
}
|
|
973
986
|
this.cleanupDeviceState(uuid);
|
|
974
987
|
throw error;
|
package/dist/webusb.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export default class WebUsbTransport {
|
|
|
35
35
|
getConnectedDevices(): Promise<DeviceInfo[]>;
|
|
36
36
|
acquire(input: AcquireInput): Promise<string | undefined>;
|
|
37
37
|
private createProtocolMismatchError;
|
|
38
|
+
private createProtocolProbeTimeoutError;
|
|
38
39
|
private createProtocolDetectionError;
|
|
39
40
|
private detectProtocol;
|
|
40
41
|
findDevice(path: string): Promise<USBDevice>;
|
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,SAaN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;
|
|
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;AAsBhC,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,+BAA+B;IAOvC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6DtB,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;YA2Dd,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.24",
|
|
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.24",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.24"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.24",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "43663337d94350430cc507c74d6eec5a076bd4e9"
|
|
32
32
|
}
|
|
@@ -3,6 +3,7 @@ import transport, {
|
|
|
3
3
|
PROTOCOL_V2_CHANNEL_BLE_UART,
|
|
4
4
|
ProtocolV2FrameAssembler,
|
|
5
5
|
ProtocolV2LinkManager,
|
|
6
|
+
TRANSPORT_EVENT,
|
|
6
7
|
bytesToHex,
|
|
7
8
|
hexToBytes,
|
|
8
9
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
@@ -76,6 +77,8 @@ export default class ElectronBleTransport {
|
|
|
76
77
|
|
|
77
78
|
private _messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
78
79
|
|
|
80
|
+
private protocolV2SchemaConfiguration: string | undefined;
|
|
81
|
+
|
|
79
82
|
name = 'ElectronBleTransport';
|
|
80
83
|
|
|
81
84
|
configured = false;
|
|
@@ -206,10 +209,19 @@ export default class ElectronBleTransport {
|
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
configureProtocolV2(signedData: any) {
|
|
212
|
+
const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
|
|
213
|
+
if (this.protocolV2SchemaConfiguration === configuration) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
|
|
209
218
|
this._messagesV2 = parseConfigure(signedData);
|
|
210
|
-
this.
|
|
211
|
-
|
|
212
|
-
|
|
219
|
+
this.protocolV2SchemaConfiguration = configuration;
|
|
220
|
+
if (isReconfiguration) {
|
|
221
|
+
this.protocolV2Links
|
|
222
|
+
.invalidateAllLinks('Protocol V2 schema reconfigured')
|
|
223
|
+
.catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
|
|
224
|
+
}
|
|
213
225
|
}
|
|
214
226
|
|
|
215
227
|
async listen() {
|
|
@@ -290,7 +302,7 @@ export default class ElectronBleTransport {
|
|
|
290
302
|
(disconnectedDevice: any) => {
|
|
291
303
|
if (disconnectedDevice.id === uuid) {
|
|
292
304
|
this.cleanupDeviceState(uuid);
|
|
293
|
-
this.emitter?.emit(
|
|
305
|
+
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
294
306
|
name: disconnectedDevice.name,
|
|
295
307
|
id: disconnectedDevice.id,
|
|
296
308
|
connectId: disconnectedDevice.id,
|
|
@@ -302,12 +314,6 @@ export default class ElectronBleTransport {
|
|
|
302
314
|
|
|
303
315
|
const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
304
316
|
|
|
305
|
-
this.emitter?.emit('device-connect', {
|
|
306
|
-
name: device.name,
|
|
307
|
-
id: device.id,
|
|
308
|
-
connectId: device.id,
|
|
309
|
-
});
|
|
310
|
-
|
|
311
317
|
return {
|
|
312
318
|
...toBleDescriptor({ id: device.id, name: device.name }, protocolType),
|
|
313
319
|
uuid,
|
package/src/webusb.ts
CHANGED
|
@@ -43,6 +43,7 @@ const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
|
|
|
43
43
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
44
44
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
45
45
|
const PROTOCOL_PROBE_TIMEOUT = 1000;
|
|
46
|
+
const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
|
|
46
47
|
function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
|
|
47
48
|
return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
|
|
48
49
|
}
|
|
@@ -269,6 +270,13 @@ export default class WebUsbTransport {
|
|
|
269
270
|
);
|
|
270
271
|
}
|
|
271
272
|
|
|
273
|
+
private createProtocolProbeTimeoutError(expected: ProtocolType, attempts: number) {
|
|
274
|
+
return ERRORS.TypedError(
|
|
275
|
+
HardwareErrorCode.RuntimeError,
|
|
276
|
+
`Protocol ${expected} probe timeout after ${attempts} attempts`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
272
280
|
private createProtocolDetectionError() {
|
|
273
281
|
return ERRORS.TypedError(
|
|
274
282
|
HardwareErrorCode.RuntimeError,
|
|
@@ -291,11 +299,25 @@ export default class WebUsbTransport {
|
|
|
291
299
|
}
|
|
292
300
|
|
|
293
301
|
if (expectedProtocol === 'V2') {
|
|
294
|
-
|
|
295
|
-
this.
|
|
296
|
-
|
|
302
|
+
for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
|
|
303
|
+
if (await this.probeProtocolV2(path)) {
|
|
304
|
+
this.deviceProtocol.set(path, 'V2');
|
|
305
|
+
return 'V2';
|
|
306
|
+
}
|
|
307
|
+
await this.resetConnectionAfterProbe(path);
|
|
308
|
+
if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
|
|
309
|
+
this.Log?.debug(
|
|
310
|
+
`[WebUsbTransport] Protocol V2 probe timed out, retrying ${
|
|
311
|
+
attempt + 1
|
|
312
|
+
}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
297
315
|
}
|
|
298
|
-
|
|
316
|
+
this.deviceProtocol.delete(path);
|
|
317
|
+
throw this.createProtocolProbeTimeoutError(
|
|
318
|
+
expectedProtocol,
|
|
319
|
+
EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS
|
|
320
|
+
);
|
|
299
321
|
}
|
|
300
322
|
|
|
301
323
|
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
@@ -310,12 +332,10 @@ export default class WebUsbTransport {
|
|
|
310
332
|
this.deviceProtocol.set(path, protocol);
|
|
311
333
|
return protocol;
|
|
312
334
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
await this.resetConnectionAfterProbe(path);
|
|
318
|
-
}
|
|
335
|
+
// A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
|
|
336
|
+
// reopening the device guarantees the next protocol probe cannot consume a
|
|
337
|
+
// late response from the previous protocol generation.
|
|
338
|
+
await this.resetConnectionAfterProbe(path);
|
|
319
339
|
}
|
|
320
340
|
|
|
321
341
|
this.deviceProtocol.delete(path);
|
|
@@ -716,7 +736,7 @@ export default class WebUsbTransport {
|
|
|
716
736
|
}
|
|
717
737
|
|
|
718
738
|
return probeProtocolV2Helper({
|
|
719
|
-
call: (name, data, options) => this.callProtocolV2(path, name, data, options),
|
|
739
|
+
call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
|
|
720
740
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
721
741
|
logger: this.Log,
|
|
722
742
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
@@ -798,7 +818,8 @@ export default class WebUsbTransport {
|
|
|
798
818
|
path: string,
|
|
799
819
|
name: string,
|
|
800
820
|
data: Record<string, unknown>,
|
|
801
|
-
options?: TransportCallOptions
|
|
821
|
+
options?: TransportCallOptions,
|
|
822
|
+
resetOnError = true
|
|
802
823
|
) {
|
|
803
824
|
const protocolV1Messages = this.messages;
|
|
804
825
|
if (!this.messagesV2) {
|
|
@@ -841,7 +862,7 @@ export default class WebUsbTransport {
|
|
|
841
862
|
try {
|
|
842
863
|
return await session.call(name, data, options);
|
|
843
864
|
} catch (error) {
|
|
844
|
-
if (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
|
|
865
|
+
if (resetOnError && (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
|
|
845
866
|
try {
|
|
846
867
|
await this.resetConnectionAfterProbe(path);
|
|
847
868
|
} catch (resetError) {
|