@onekeyfe/hd-transport-web-device 1.2.0-alpha.19 → 1.2.0-alpha.20
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 +50 -2
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +92 -0
- package/dist/index.js +17 -2
- package/dist/webusb.d.ts +2 -0
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +5 -6
- package/src/webusb.ts +37 -15
|
@@ -16,6 +16,29 @@ const schema = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
19
|
+
test('resets the connection between a failed V1 probe and the V2 probe', async () => {
|
|
20
|
+
const webusb = new WebUsbTransport() as any;
|
|
21
|
+
const path = 'pro2-webusb';
|
|
22
|
+
const events: string[] = [];
|
|
23
|
+
webusb.probeProtocolV1 = jest.fn().mockImplementation(() => {
|
|
24
|
+
events.push('probe-v1');
|
|
25
|
+
return Promise.resolve(false);
|
|
26
|
+
});
|
|
27
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
28
|
+
events.push('reset');
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
});
|
|
31
|
+
webusb.probeProtocolV2 = jest.fn().mockImplementation(() => {
|
|
32
|
+
events.push('probe-v2');
|
|
33
|
+
return Promise.resolve(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await expect(webusb.detectProtocol(path)).resolves.toBe('V2');
|
|
37
|
+
|
|
38
|
+
expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
|
|
39
|
+
expect(webusb.deviceProtocol.get(path)).toBe('V2');
|
|
40
|
+
});
|
|
41
|
+
|
|
19
42
|
test('invalidates and resets the cached connection before another call can start', async () => {
|
|
20
43
|
const webusb = new WebUsbTransport() as any;
|
|
21
44
|
const path = 'pro2-webusb';
|
|
@@ -23,8 +46,8 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
23
46
|
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
24
47
|
webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
|
|
25
48
|
webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
|
|
26
|
-
webusb.receiveProtocolV2Frame = jest.fn(() => new Promise(() =>
|
|
27
|
-
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(
|
|
49
|
+
webusb.receiveProtocolV2Frame = jest.fn(() => new Promise<void>(() => {}));
|
|
50
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
28
51
|
webusb.protocolV2Sessions.delete(path);
|
|
29
52
|
webusb.protocolV2ReadTimeouts.delete(path);
|
|
30
53
|
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
@@ -37,4 +60,29 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
|
|
|
37
60
|
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
38
61
|
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
39
62
|
});
|
|
63
|
+
|
|
64
|
+
test('resets a stalled write while retaining the device sequence cursor', async () => {
|
|
65
|
+
const webusb = new WebUsbTransport() as any;
|
|
66
|
+
const path = 'pro2-webusb';
|
|
67
|
+
webusb.messages = transport.parseConfigure(schema);
|
|
68
|
+
webusb.messagesV2 = transport.parseConfigure(schema);
|
|
69
|
+
webusb.protocolV2WriteTimeoutMs = 10;
|
|
70
|
+
webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
|
|
71
|
+
webusb.transferOutOnce = jest.fn(() => new Promise(() => {}));
|
|
72
|
+
webusb.receiveProtocolV2Frame = jest.fn();
|
|
73
|
+
webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
|
|
74
|
+
webusb.protocolV2Sessions.delete(path);
|
|
75
|
+
webusb.protocolV2ReadTimeouts.delete(path);
|
|
76
|
+
webusb.protocolV2Assemblers.get(path)?.reset();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await expect(
|
|
80
|
+
webusb.callProtocolV2(path, 'Ping', { message: 'timeout' }, { timeoutMs: 10 })
|
|
81
|
+
).rejects.toThrow('Protocol V2 write timeout after 10ms for Ping');
|
|
82
|
+
|
|
83
|
+
expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
|
|
84
|
+
expect(webusb.protocolV2Sequences.has(path)).toBe(true);
|
|
85
|
+
expect(webusb.protocolV2Sessions.has(path)).toBe(false);
|
|
86
|
+
expect(webusb.receiveProtocolV2Frame).not.toHaveBeenCalled();
|
|
87
|
+
});
|
|
40
88
|
});
|
|
@@ -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;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;
|
|
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;IA8C5B,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.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { Deferred } from '@onekeyfe/hd-shared';
|
|
|
4
4
|
import { DesktopAPI } from '@onekeyfe/hd-transport-electron';
|
|
5
5
|
import EventEmitter from 'events';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Device information with path and WebUSB device instance
|
|
9
|
+
*/
|
|
7
10
|
interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
8
11
|
path: string;
|
|
9
12
|
device: USBDevice;
|
|
@@ -11,13 +14,26 @@ interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
|
11
14
|
}
|
|
12
15
|
declare class WebUsbTransport {
|
|
13
16
|
messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
17
|
+
/** Protobuf schema for Protocol V2 transports. */
|
|
14
18
|
messagesV2: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
19
|
+
/** Per-path protocol type detected by active wire-level probe. */
|
|
15
20
|
private deviceProtocol;
|
|
16
21
|
private deviceProtocolHints;
|
|
22
|
+
/** Per-device Protocol V2 assembler that retains extra frames from one read. */
|
|
17
23
|
private protocolV2Assemblers;
|
|
24
|
+
/** Per-device Protocol V2 session that keeps sequence numbers monotonic. */
|
|
18
25
|
private protocolV2Sessions;
|
|
26
|
+
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
27
|
+
private protocolV2Sequences;
|
|
28
|
+
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
19
29
|
private protocolV2ReadTimeouts;
|
|
30
|
+
private protocolV2WriteTimeoutMs;
|
|
31
|
+
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
20
32
|
private deviceEndpoints;
|
|
33
|
+
/**
|
|
34
|
+
* Early Pro2 boards have no USB serial number. Assign a session-stable mock path per
|
|
35
|
+
* USBDevice instance so discovery retains them; reconnecting creates a new instance/path.
|
|
36
|
+
*/
|
|
21
37
|
private mockSerialPaths;
|
|
22
38
|
private mockSerialCounter;
|
|
23
39
|
name: string;
|
|
@@ -25,24 +41,75 @@ declare class WebUsbTransport {
|
|
|
25
41
|
configured: boolean;
|
|
26
42
|
Log?: any;
|
|
27
43
|
usb?: USB;
|
|
44
|
+
/**
|
|
45
|
+
* Cached list of connected devices
|
|
46
|
+
* This is essential for maintaining device references between operations
|
|
47
|
+
*/
|
|
28
48
|
deviceList: Array<DeviceInfo>;
|
|
29
49
|
configurationId: number;
|
|
30
50
|
endpointId: number;
|
|
31
51
|
interfaceId: number;
|
|
52
|
+
/**
|
|
53
|
+
* Initialize WebUSB transport
|
|
54
|
+
*/
|
|
32
55
|
init(logger: any): void;
|
|
56
|
+
/**
|
|
57
|
+
* Configure Protocol V1 protobuf schema (legacy chunked 0x3F framing).
|
|
58
|
+
*/
|
|
33
59
|
configure(signedData: any): void;
|
|
60
|
+
/**
|
|
61
|
+
* Cache the Protocol V2 protobuf schema.
|
|
62
|
+
*/
|
|
34
63
|
configureProtocolV2(signedData: any): void;
|
|
64
|
+
/**
|
|
65
|
+
* Request user to select a device
|
|
66
|
+
* This method must be called in response to a user action
|
|
67
|
+
* to comply with WebUSB security requirements
|
|
68
|
+
*/
|
|
35
69
|
promptDeviceAccess(): Promise<USBDevice | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Enumerate already connected devices
|
|
72
|
+
* This method only returns devices that are already authorized by the browser
|
|
73
|
+
* It does NOT prompt the user to select a device
|
|
74
|
+
*/
|
|
36
75
|
enumerate(): Promise<DeviceInfo[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Use the USB serial as the device path, falling back to a session-stable mock path.
|
|
78
|
+
*/
|
|
37
79
|
private getDevicePath;
|
|
80
|
+
/**
|
|
81
|
+
* Get list of connected devices
|
|
82
|
+
*/
|
|
38
83
|
getConnectedDevices(): Promise<DeviceInfo[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Acquire device control
|
|
86
|
+
*/
|
|
39
87
|
acquire(input: AcquireInput): Promise<string | undefined>;
|
|
88
|
+
/**
|
|
89
|
+
* Determine protocol type after connect.
|
|
90
|
+
* Probe Protocol V1 first with Initialize. If it does not answer in time,
|
|
91
|
+
* fall back to a Protocol V2 Ping probe.
|
|
92
|
+
*/
|
|
40
93
|
private createProtocolMismatchError;
|
|
41
94
|
private createProtocolDetectionError;
|
|
42
95
|
private detectProtocol;
|
|
96
|
+
/**
|
|
97
|
+
* Find device by path
|
|
98
|
+
*/
|
|
43
99
|
findDevice(path: string): Promise<USBDevice>;
|
|
100
|
+
/**
|
|
101
|
+
* Connect to device with retry mechanism
|
|
102
|
+
*/
|
|
44
103
|
connect(path: string, first: boolean): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Discover vendor-class (0xFF) interface and its IN/OUT endpoint numbers from USB descriptors.
|
|
106
|
+
* Falls back to legacy hardcoded values if no vendor interface is found.
|
|
107
|
+
*/
|
|
45
108
|
private discoverEndpoints;
|
|
109
|
+
/**
|
|
110
|
+
* Connect to specific device.
|
|
111
|
+
* Discovers interface/endpoint numbers from USB descriptors on first connection.
|
|
112
|
+
*/
|
|
46
113
|
connectToDevice(path: string, first: boolean): Promise<void>;
|
|
47
114
|
private closeOpenDevice;
|
|
48
115
|
private clearEndpointHalt;
|
|
@@ -59,12 +126,31 @@ declare class WebUsbTransport {
|
|
|
59
126
|
private withProtocolReadTimeout;
|
|
60
127
|
private probeProtocolV1;
|
|
61
128
|
private probeProtocolV2;
|
|
129
|
+
/**
|
|
130
|
+
* Call device method — branches to Protocol V1 or Protocol V2 based on active probe.
|
|
131
|
+
*/
|
|
62
132
|
call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
|
|
63
133
|
private callProtocolV1;
|
|
134
|
+
/**
|
|
135
|
+
* Send/receive a single call over Protocol V2 (0x5A framing).
|
|
136
|
+
*
|
|
137
|
+
* Encoding: protobuf message → 2-byte LE messageTypeId + pb bytes → Protocol V2 frame
|
|
138
|
+
* Decoding: Protocol V2 frame → messageTypeId + pb bytes → protobuf message
|
|
139
|
+
*/
|
|
64
140
|
private callProtocolV2;
|
|
65
141
|
private receiveProtocolV2Frame;
|
|
142
|
+
/**
|
|
143
|
+
* Receive data from device
|
|
144
|
+
*/
|
|
66
145
|
receiveData(path: string, timeoutMs?: number): Promise<string>;
|
|
146
|
+
/**
|
|
147
|
+
* Release device
|
|
148
|
+
*/
|
|
67
149
|
release(path: string): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Expose the detected protocol type for a given device path.
|
|
152
|
+
* Used by upper layers (e.g. TransportManager) to select the correct schema.
|
|
153
|
+
*/
|
|
68
154
|
getProtocolType(path: string): ProtocolType | undefined;
|
|
69
155
|
}
|
|
70
156
|
|
|
@@ -78,6 +164,12 @@ type BleAcquireInput = {
|
|
|
78
164
|
forceCleanRunPromise?: boolean;
|
|
79
165
|
expectedProtocol?: ProtocolType;
|
|
80
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* Desktop Electron BLE transport with automatic Protocol V1/V2 detection.
|
|
169
|
+
*
|
|
170
|
+
* Protocol V1 devices continue using chunked packets. Protocol V2 is detected
|
|
171
|
+
* after a Protocol V1 Initialize timeout by probing Protocol V2 Ping.
|
|
172
|
+
*/
|
|
81
173
|
declare class ElectronBleTransport {
|
|
82
174
|
private _messages;
|
|
83
175
|
private _messagesV2;
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,9 @@ class WebUsbTransport {
|
|
|
69
69
|
this.deviceProtocolHints = new Map();
|
|
70
70
|
this.protocolV2Assemblers = new Map();
|
|
71
71
|
this.protocolV2Sessions = new Map();
|
|
72
|
+
this.protocolV2Sequences = new Map();
|
|
72
73
|
this.protocolV2ReadTimeouts = new Map();
|
|
74
|
+
this.protocolV2WriteTimeoutMs = transport.PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS;
|
|
73
75
|
this.deviceEndpoints = new Map();
|
|
74
76
|
this.mockSerialPaths = new WeakMap();
|
|
75
77
|
this.mockSerialCounter = 0;
|
|
@@ -194,6 +196,7 @@ class WebUsbTransport {
|
|
|
194
196
|
this.deviceProtocol.set(path, 'V1');
|
|
195
197
|
return 'V1';
|
|
196
198
|
}
|
|
199
|
+
yield this.resetConnectionAfterProbe(path);
|
|
197
200
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
198
201
|
}
|
|
199
202
|
if (expectedProtocol === 'V2') {
|
|
@@ -207,6 +210,9 @@ class WebUsbTransport {
|
|
|
207
210
|
this.deviceProtocol.set(path, protocol);
|
|
208
211
|
return protocol;
|
|
209
212
|
}
|
|
213
|
+
if (protocol === 'V1') {
|
|
214
|
+
yield this.resetConnectionAfterProbe(path);
|
|
215
|
+
}
|
|
210
216
|
}
|
|
211
217
|
this.deviceProtocol.delete(path);
|
|
212
218
|
throw this.createProtocolDetectionError();
|
|
@@ -565,7 +571,6 @@ class WebUsbTransport {
|
|
|
565
571
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
566
572
|
logger: this.Log,
|
|
567
573
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
568
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
569
574
|
});
|
|
570
575
|
});
|
|
571
576
|
}
|
|
@@ -624,12 +629,19 @@ class WebUsbTransport {
|
|
|
624
629
|
}
|
|
625
630
|
let session = this.protocolV2Sessions.get(path);
|
|
626
631
|
if (!session) {
|
|
632
|
+
let sequenceCursor = this.protocolV2Sequences.get(path);
|
|
633
|
+
if (!sequenceCursor) {
|
|
634
|
+
sequenceCursor = new transport.ProtocolV2SequenceCursor();
|
|
635
|
+
this.protocolV2Sequences.set(path, sequenceCursor);
|
|
636
|
+
}
|
|
627
637
|
session = new transport.ProtocolV2Session({
|
|
628
638
|
schemas: {
|
|
629
639
|
protocolV1: protocolV1Messages,
|
|
630
640
|
protocolV2: this.messagesV2,
|
|
631
641
|
},
|
|
632
642
|
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
643
|
+
sequenceCursor,
|
|
644
|
+
writeTimeoutMs: this.protocolV2WriteTimeoutMs,
|
|
633
645
|
writeFrame: (frame) => this.transferOutOnce(path, frame),
|
|
634
646
|
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
635
647
|
logger: this.Log,
|
|
@@ -645,7 +657,10 @@ class WebUsbTransport {
|
|
|
645
657
|
}
|
|
646
658
|
catch (error) {
|
|
647
659
|
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
648
|
-
if (message.includes('protocol v2 read timeout') ||
|
|
660
|
+
if (message.includes('protocol v2 read timeout') ||
|
|
661
|
+
message.includes('protocol v2 write timeout') ||
|
|
662
|
+
message.includes('protocol v2 delivery timeout') ||
|
|
663
|
+
message.includes('response timeout')) {
|
|
649
664
|
try {
|
|
650
665
|
yield this.resetConnectionAfterProbe(path);
|
|
651
666
|
}
|
package/dist/webusb.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export default class WebUsbTransport {
|
|
|
13
13
|
private deviceProtocolHints;
|
|
14
14
|
private protocolV2Assemblers;
|
|
15
15
|
private protocolV2Sessions;
|
|
16
|
+
private protocolV2Sequences;
|
|
16
17
|
private protocolV2ReadTimeouts;
|
|
18
|
+
private protocolV2WriteTimeoutMs;
|
|
17
19
|
private deviceEndpoints;
|
|
18
20
|
private mockSerialPaths;
|
|
19
21
|
private mockSerialCounter;
|
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,SAYN,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,sBAAsB,CAA8C;IAE5E,OAAO,CAAC,wBAAwB,CAAyC;IAGzE,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;IAW7B,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;IAgDtB,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;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;YAmEd,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.20",
|
|
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.20",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.20"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
27
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.20",
|
|
28
28
|
"@types/w3c-web-usb": "^1.0.6",
|
|
29
29
|
"@types/web-bluetooth": "^0.0.17"
|
|
30
30
|
},
|
|
31
|
-
"gitHead": "
|
|
31
|
+
"gitHead": "5fbc1ada90fd3cfda7e5ef08be368cb452018733"
|
|
32
32
|
}
|
|
@@ -379,23 +379,22 @@ export default class ElectronBleTransport {
|
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
if (expectedProtocol === 'V2') {
|
|
382
|
-
//
|
|
383
|
-
//
|
|
382
|
+
// Skip probing when the caller explicitly confirms V2, such as reconnect after a
|
|
383
|
+
// firmware reboot where expectedProtocol carries the previously probed result.
|
|
384
384
|
this.deviceProtocol.set(uuid, 'V2');
|
|
385
385
|
this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
|
|
386
386
|
return 'V2';
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
// 不能作为最终结论。
|
|
389
|
+
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
390
|
+
// influence probe order; a V2 hint probes V2 first and falls back to V1.
|
|
392
391
|
const probeOrder: ProtocolType[] =
|
|
393
392
|
protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
394
393
|
|
|
395
394
|
for (let i = 0; i < probeOrder.length; i += 1) {
|
|
396
395
|
const protocol = probeOrder[i];
|
|
397
396
|
if (i > 0) {
|
|
398
|
-
//
|
|
397
|
+
// Reset subscriptions and buffers after a failed probe before trying another protocol.
|
|
399
398
|
await this.resetProbeStateAfterProtocolProbe(uuid, probeOrder[i - 1]);
|
|
400
399
|
}
|
|
401
400
|
const detected =
|
package/src/webusb.ts
CHANGED
|
@@ -6,7 +6,9 @@ import transport, {
|
|
|
6
6
|
PROTOCOL_V1_USB_PACKET_SIZE,
|
|
7
7
|
PROTOCOL_V2_CHANNEL_USB,
|
|
8
8
|
PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
9
|
+
PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS,
|
|
9
10
|
ProtocolV2FrameAssembler,
|
|
11
|
+
ProtocolV2SequenceCursor,
|
|
10
12
|
ProtocolV2Session,
|
|
11
13
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
12
14
|
} from '@onekeyfe/hd-transport';
|
|
@@ -75,22 +77,26 @@ export default class WebUsbTransport {
|
|
|
75
77
|
|
|
76
78
|
private deviceProtocolHints: Map<string, ProtocolType> = new Map();
|
|
77
79
|
|
|
78
|
-
/**
|
|
80
|
+
/** Per-device Protocol V2 assembler that retains extra frames from one read. */
|
|
79
81
|
private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
|
|
80
82
|
|
|
81
|
-
/**
|
|
83
|
+
/** Per-device Protocol V2 session that keeps sequence numbers monotonic. */
|
|
82
84
|
private protocolV2Sessions: Map<string, ProtocolV2Session> = new Map();
|
|
83
85
|
|
|
84
|
-
/**
|
|
86
|
+
/** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
|
|
87
|
+
private protocolV2Sequences: Map<string, ProtocolV2SequenceCursor> = new Map();
|
|
88
|
+
|
|
89
|
+
/** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
|
|
85
90
|
private protocolV2ReadTimeouts: Map<string, number | undefined> = new Map();
|
|
86
91
|
|
|
92
|
+
private protocolV2WriteTimeoutMs = PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS;
|
|
93
|
+
|
|
87
94
|
/** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
|
|
88
95
|
private deviceEndpoints: Map<string, DeviceEndpoints> = new Map();
|
|
89
96
|
|
|
90
97
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* 避免设备因为空 serial 被发现流程整体丢弃。重新插拔后实例变化,path 会重新生成。
|
|
98
|
+
* Early Pro2 boards have no USB serial number. Assign a session-stable mock path per
|
|
99
|
+
* USBDevice instance so discovery retains them; reconnecting creates a new instance/path.
|
|
94
100
|
*/
|
|
95
101
|
private mockSerialPaths: WeakMap<USBDevice, string> = new WeakMap();
|
|
96
102
|
|
|
@@ -182,8 +188,7 @@ export default class WebUsbTransport {
|
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
/**
|
|
185
|
-
*
|
|
186
|
-
* 空 serial(早期工程板)回退到会话内稳定的 mock path。
|
|
191
|
+
* Use the USB serial as the device path, falling back to a session-stable mock path.
|
|
187
192
|
*/
|
|
188
193
|
private getDevicePath(device: USBDevice): string {
|
|
189
194
|
if (typeof device.serialNumber === 'string' && device.serialNumber.length > 0) {
|
|
@@ -286,19 +291,19 @@ export default class WebUsbTransport {
|
|
|
286
291
|
this.deviceProtocol.set(path, 'V1');
|
|
287
292
|
return 'V1';
|
|
288
293
|
}
|
|
294
|
+
await this.resetConnectionAfterProbe(path);
|
|
289
295
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
290
296
|
}
|
|
291
297
|
|
|
292
298
|
if (expectedProtocol === 'V2') {
|
|
293
|
-
//
|
|
294
|
-
//
|
|
299
|
+
// Skip probing when the caller explicitly confirms V2, such as reconnect after a
|
|
300
|
+
// firmware reboot where expectedProtocol carries the previously probed result.
|
|
295
301
|
this.deviceProtocol.set(path, 'V2');
|
|
296
302
|
return 'V2';
|
|
297
303
|
}
|
|
298
304
|
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
// 不能作为最终结论。
|
|
305
|
+
// Protocol must be actively probed after connection. Name, PID, and descriptors only
|
|
306
|
+
// influence probe order; a V2 hint probes V2 first and falls back to V1.
|
|
302
307
|
const probeOrder: ProtocolType[] =
|
|
303
308
|
protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
|
|
304
309
|
|
|
@@ -309,6 +314,12 @@ export default class WebUsbTransport {
|
|
|
309
314
|
this.deviceProtocol.set(path, protocol);
|
|
310
315
|
return protocol;
|
|
311
316
|
}
|
|
317
|
+
if (protocol === 'V1') {
|
|
318
|
+
// A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
|
|
319
|
+
// reopening the device guarantees the next protocol probe cannot consume a
|
|
320
|
+
// late V1 response. V2 timeout recovery is owned by callProtocolV2.
|
|
321
|
+
await this.resetConnectionAfterProbe(path);
|
|
322
|
+
}
|
|
312
323
|
}
|
|
313
324
|
|
|
314
325
|
this.deviceProtocol.delete(path);
|
|
@@ -707,7 +718,6 @@ export default class WebUsbTransport {
|
|
|
707
718
|
timeoutMs: PROTOCOL_PROBE_TIMEOUT,
|
|
708
719
|
logger: this.Log,
|
|
709
720
|
logPrefix: 'ProtocolV2 WebUSB',
|
|
710
|
-
onProbeFailed: () => this.resetConnectionAfterProbe(path),
|
|
711
721
|
});
|
|
712
722
|
}
|
|
713
723
|
|
|
@@ -801,12 +811,19 @@ export default class WebUsbTransport {
|
|
|
801
811
|
|
|
802
812
|
let session = this.protocolV2Sessions.get(path);
|
|
803
813
|
if (!session) {
|
|
814
|
+
let sequenceCursor = this.protocolV2Sequences.get(path);
|
|
815
|
+
if (!sequenceCursor) {
|
|
816
|
+
sequenceCursor = new ProtocolV2SequenceCursor();
|
|
817
|
+
this.protocolV2Sequences.set(path, sequenceCursor);
|
|
818
|
+
}
|
|
804
819
|
session = new ProtocolV2Session({
|
|
805
820
|
schemas: {
|
|
806
821
|
protocolV1: protocolV1Messages,
|
|
807
822
|
protocolV2: this.messagesV2,
|
|
808
823
|
},
|
|
809
824
|
router: PROTOCOL_V2_CHANNEL_USB,
|
|
825
|
+
sequenceCursor,
|
|
826
|
+
writeTimeoutMs: this.protocolV2WriteTimeoutMs,
|
|
810
827
|
writeFrame: (frame: Uint8Array) => this.transferOutOnce(path, frame),
|
|
811
828
|
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
812
829
|
logger: this.Log,
|
|
@@ -824,7 +841,12 @@ export default class WebUsbTransport {
|
|
|
824
841
|
} catch (error) {
|
|
825
842
|
const message =
|
|
826
843
|
error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
827
|
-
if (
|
|
844
|
+
if (
|
|
845
|
+
message.includes('protocol v2 read timeout') ||
|
|
846
|
+
message.includes('protocol v2 write timeout') ||
|
|
847
|
+
message.includes('protocol v2 delivery timeout') ||
|
|
848
|
+
message.includes('response timeout')
|
|
849
|
+
) {
|
|
828
850
|
try {
|
|
829
851
|
await this.resetConnectionAfterProbe(path);
|
|
830
852
|
} catch (resetError) {
|