@onekeyfe/hd-transport-web-device 1.2.0-alpha.11 → 1.2.0-alpha.111

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.
@@ -0,0 +1,386 @@
1
+ import transport, {
2
+ PROTOCOL_V2_CHANNEL_USB,
3
+ ProtocolV2,
4
+ ProtocolV2LinkError,
5
+ } from '@onekeyfe/hd-transport';
6
+ import { HardwareErrorCode, ONEKEY_WEBUSB_FILTER } from '@onekeyfe/hd-shared';
7
+
8
+ import WebUsbTransport from '../src/webusb';
9
+
10
+ const schema = {
11
+ nested: {
12
+ Ping: { fields: { message: { type: 'string', id: 1 } } },
13
+ Success: { fields: { message: { type: 'string', id: 1 } } },
14
+ MessageType: {
15
+ values: {
16
+ MessageType_Ping: 60206,
17
+ MessageType_Success: 60207,
18
+ },
19
+ },
20
+ },
21
+ };
22
+
23
+ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
24
+ test('only enumerates devices with real USB serial numbers', async () => {
25
+ const filter = ONEKEY_WEBUSB_FILTER[0];
26
+ const deviceWithSerial = {
27
+ ...filter,
28
+ manufacturerName: 'OneKey',
29
+ productName: 'OneKey Pro 2',
30
+ serialNumber: 'PRO2-SERIAL',
31
+ } as USBDevice;
32
+ const deviceWithoutSerial = {
33
+ ...filter,
34
+ manufacturerName: 'OneKey',
35
+ productName: 'OneKey Pro 2',
36
+ serialNumber: null,
37
+ } as USBDevice;
38
+ const webusb = new WebUsbTransport();
39
+ webusb.usb = {
40
+ getDevices: jest.fn().mockResolvedValue([deviceWithSerial, deviceWithoutSerial]),
41
+ } as unknown as USB;
42
+
43
+ await expect(webusb.getConnectedDevices()).resolves.toEqual([
44
+ {
45
+ path: 'PRO2-SERIAL',
46
+ device: deviceWithSerial,
47
+ commType: 'webusb',
48
+ },
49
+ ]);
50
+ });
51
+
52
+ test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
53
+ const webusb = new WebUsbTransport() as any;
54
+ webusb.invalidateAllProtocolV2UsbLinks = jest.fn().mockResolvedValue(undefined);
55
+ const schemaSource = JSON.stringify(schema);
56
+
57
+ webusb.configureProtocolV2(schemaSource);
58
+ webusb.configureProtocolV2(schemaSource);
59
+
60
+ expect(webusb.invalidateAllProtocolV2UsbLinks).not.toHaveBeenCalled();
61
+
62
+ webusb.configureProtocolV2(
63
+ JSON.stringify({
64
+ ...schema,
65
+ nested: {
66
+ ...schema.nested,
67
+ Failure: { fields: { message: { type: 'string', id: 1 } } },
68
+ },
69
+ })
70
+ );
71
+ expect(webusb.invalidateAllProtocolV2UsbLinks).toHaveBeenCalledWith(
72
+ 'Protocol V2 schema reconfigured'
73
+ );
74
+ });
75
+
76
+ test('resets the connection between a failed V1 probe and the V2 probe', async () => {
77
+ const webusb = new WebUsbTransport() as any;
78
+ const path = 'pro2-webusb';
79
+ const events: string[] = [];
80
+ webusb.probeProtocolV1 = jest.fn().mockImplementation(() => {
81
+ events.push('probe-v1');
82
+ return Promise.resolve(false);
83
+ });
84
+ webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
85
+ events.push('reset');
86
+ return Promise.resolve();
87
+ });
88
+ webusb.probeProtocolV2 = jest.fn().mockImplementation(() => {
89
+ events.push('probe-v2');
90
+ return Promise.resolve(true);
91
+ });
92
+
93
+ await expect(webusb.detectProtocol(path)).resolves.toBe('V2');
94
+
95
+ expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
96
+ expect(webusb.deviceProtocol.get(path)).toBe('V2');
97
+ });
98
+
99
+ test('reports DeviceNotFound when automatic protocol detection exhausts both probes', async () => {
100
+ const webusb = new WebUsbTransport() as any;
101
+ const path = 'unresponsive-webusb';
102
+ webusb.probeProtocolV1 = jest.fn().mockResolvedValue(false);
103
+ webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
104
+ webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
105
+ webusb.closeConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
106
+
107
+ await expect(webusb.detectProtocol(path)).rejects.toMatchObject({
108
+ errorCode: HardwareErrorCode.DeviceNotFound,
109
+ });
110
+
111
+ expect(webusb.probeProtocolV1).toHaveBeenCalledTimes(1);
112
+ expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(1);
113
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
114
+ expect(webusb.closeConnectionAfterProbe).toHaveBeenCalledTimes(1);
115
+ expect(webusb.deviceProtocol.has(path)).toBe(false);
116
+ });
117
+
118
+ test('allows legacy WebUSB Initialize up to the Node USB probe timeout', async () => {
119
+ const webusb = new WebUsbTransport() as any;
120
+ const path = 'pro-webusb';
121
+ webusb.messages = {};
122
+ webusb.callProtocolV1 = jest.fn().mockResolvedValue({});
123
+
124
+ await expect(webusb.probeProtocolV1(path)).resolves.toBe(true);
125
+
126
+ expect(webusb.callProtocolV1).toHaveBeenCalledWith(
127
+ path,
128
+ 'Initialize',
129
+ {},
130
+ {
131
+ timeoutMs: 5000,
132
+ }
133
+ );
134
+ });
135
+
136
+ test('retries an expected Protocol V2 probe once after resetting the connection', async () => {
137
+ const webusb = new WebUsbTransport() as any;
138
+ const path = 'pro2-webusb';
139
+ webusb.probeProtocolV1 = jest.fn();
140
+ webusb.probeProtocolV2 = jest.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true);
141
+ webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
142
+
143
+ await expect(webusb.detectProtocol(path, 'V2')).resolves.toBe('V2');
144
+
145
+ expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
146
+ expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
147
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
148
+ expect(webusb.deviceProtocol.get(path)).toBe('V2');
149
+ });
150
+
151
+ test('reports a device initialization failure only after the bounded retry is exhausted', async () => {
152
+ const webusb = new WebUsbTransport() as any;
153
+ const path = 'pro2-webusb';
154
+ webusb.probeProtocolV1 = jest.fn();
155
+ webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
156
+ webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
157
+ webusb.closeConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
158
+
159
+ await expect(webusb.detectProtocol(path, 'V2')).rejects.toMatchObject({
160
+ errorCode: HardwareErrorCode.DeviceInitializeFailed,
161
+ message: 'Protocol V2 probe timeout after 2 attempts',
162
+ });
163
+
164
+ expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
165
+ expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
166
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
167
+ expect(webusb.closeConnectionAfterProbe).toHaveBeenCalledTimes(1);
168
+ expect(webusb.deviceProtocol.has(path)).toBe(false);
169
+ });
170
+
171
+ test('closes the reopened device when acquire exhausts the expected Protocol V2 probe', async () => {
172
+ const webusb = new WebUsbTransport() as any;
173
+ const path = 'pro2-webusb';
174
+ const device = {
175
+ opened: false,
176
+ releaseInterface: jest.fn().mockResolvedValue(undefined),
177
+ close: jest.fn().mockImplementation(() => {
178
+ device.opened = false;
179
+ return Promise.resolve();
180
+ }),
181
+ };
182
+ webusb.deviceList = [{ path, device }];
183
+ webusb.Log = { debug: jest.fn() };
184
+ webusb.rotateProtocolV2UsbGeneration = jest.fn().mockResolvedValue(undefined);
185
+ webusb.connect = jest.fn().mockImplementation(() => {
186
+ device.opened = true;
187
+ return Promise.resolve();
188
+ });
189
+ webusb.detectProtocol = jest.fn().mockRejectedValue(new Error('terminal probe failure'));
190
+
191
+ await expect(webusb.acquire({ path, expectedProtocol: 'V2' })).rejects.toThrow(
192
+ 'terminal probe failure'
193
+ );
194
+
195
+ expect(device.releaseInterface).toHaveBeenCalledTimes(1);
196
+ expect(device.close).toHaveBeenCalledTimes(1);
197
+ expect(device.opened).toBe(false);
198
+ });
199
+
200
+ test('invalidates and resets the cached connection before another call can start', async () => {
201
+ const webusb = new WebUsbTransport() as any;
202
+ const path = 'pro2-webusb';
203
+ webusb.messages = transport.parseConfigure(schema);
204
+ webusb.messagesV2 = transport.parseConfigure(schema);
205
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
206
+ webusb.readProtocolV2UsbPacket = jest.fn(() => new Promise<void>(() => {}));
207
+ webusb.resetProtocolV2UsbNativeLink = jest.fn().mockResolvedValue(undefined);
208
+ webusb.resetConnectionAfterProbe = jest.fn();
209
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
210
+
211
+ await expect(
212
+ webusb.callProtocolV2(path, 'Ping', { message: 'timeout' }, { timeoutMs: 10 })
213
+ ).rejects.toThrow('timeout');
214
+
215
+ expect(webusb.resetProtocolV2UsbNativeLink).toHaveBeenCalledWith(
216
+ path,
217
+ expect.stringContaining('timeout')
218
+ );
219
+ expect(webusb.resetConnectionAfterProbe).not.toHaveBeenCalled();
220
+ });
221
+
222
+ test('does not reconnect inside a Protocol V2 frame read after a USB I/O failure', async () => {
223
+ const webusb = new WebUsbTransport() as any;
224
+ const path = 'pro2-webusb';
225
+ webusb.messages = transport.parseConfigure(schema);
226
+ webusb.messagesV2 = transport.parseConfigure(schema);
227
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
228
+ webusb.readProtocolV2UsbPacket = jest
229
+ .fn()
230
+ .mockRejectedValue(new Error('NetworkError: transferIn device disconnected'));
231
+ webusb.resetProtocolV2UsbNativeLink = jest.fn().mockResolvedValue(undefined);
232
+ webusb.resetConnectionAfterProbe = jest.fn();
233
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
234
+
235
+ await expect(webusb.callProtocolV2(path, 'Ping', { message: 'read-error' })).rejects.toThrow(
236
+ 'NetworkError'
237
+ );
238
+
239
+ expect(webusb.readProtocolV2UsbPacket).toHaveBeenCalledTimes(1);
240
+ expect(webusb.resetProtocolV2UsbNativeLink).toHaveBeenCalledWith(
241
+ path,
242
+ expect.stringContaining('NetworkError')
243
+ );
244
+ expect(webusb.resetConnectionAfterProbe).not.toHaveBeenCalled();
245
+ });
246
+
247
+ test('rejects an active Protocol V2 read without reconnecting after release', async () => {
248
+ const webusb = new WebUsbTransport() as any;
249
+ const path = 'pro2-webusb';
250
+ let markReadStarted: () => void = () => undefined;
251
+ const readStarted = new Promise<void>(resolve => {
252
+ markReadStarted = resolve;
253
+ });
254
+ webusb.messages = transport.parseConfigure(schema);
255
+ webusb.messagesV2 = transport.parseConfigure(schema);
256
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
257
+ webusb.readProtocolV2UsbPacket = jest.fn().mockImplementation(() => {
258
+ markReadStarted();
259
+ return new Promise<void>(() => {});
260
+ });
261
+ webusb.closeOpenDevice = jest.fn().mockResolvedValue(undefined);
262
+ webusb.connect = jest.fn();
263
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
264
+
265
+ const call = webusb.callProtocolV2(path, 'Ping', { message: 'release' });
266
+ await readStarted;
267
+ await webusb.release(path);
268
+
269
+ await expect(call).rejects.toThrow('WebUSB transport released');
270
+ expect(webusb.connect).not.toHaveBeenCalled();
271
+ });
272
+
273
+ test.each(['router', 'packet-source', 'ack-sequence', 'response-sequence', 'frame'] as const)(
274
+ 'invalidates cached state for typed Protocol V2 %s errors',
275
+ async code => {
276
+ const webusb = new WebUsbTransport() as any;
277
+ const path = 'pro2-webusb';
278
+ webusb.messages = transport.parseConfigure(schema);
279
+ webusb.messagesV2 = transport.parseConfigure(schema);
280
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
281
+ const recoveredResponse = ProtocolV2.encodeFrame(
282
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
283
+ 'Success',
284
+ { message: 'recovered' },
285
+ { seq: 1 }
286
+ );
287
+ webusb.readProtocolV2UsbPacket = jest
288
+ .fn()
289
+ .mockRejectedValueOnce(
290
+ new ProtocolV2LinkError(code, `Protocol V2 ${code} validation failed`)
291
+ )
292
+ .mockResolvedValue(recoveredResponse);
293
+ webusb.resetProtocolV2UsbNativeLink = jest.fn().mockResolvedValue(undefined);
294
+ webusb.resetConnectionAfterProbe = jest.fn();
295
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
296
+
297
+ await expect(webusb.callProtocolV2(path, 'Ping', { message: 'mismatch' })).rejects.toThrow(
298
+ `${code} validation failed`
299
+ );
300
+
301
+ expect(webusb.resetProtocolV2UsbNativeLink).toHaveBeenCalledWith(
302
+ path,
303
+ expect.stringContaining(`${code} validation failed`)
304
+ );
305
+ expect(webusb.resetConnectionAfterProbe).not.toHaveBeenCalled();
306
+
307
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test reconnect');
308
+ await expect(
309
+ webusb.callProtocolV2(path, 'Ping', { message: 'after-reset' })
310
+ ).resolves.toMatchObject({
311
+ type: 'Success',
312
+ message: { message: 'recovered' },
313
+ });
314
+ }
315
+ );
316
+
317
+ test('does not discard buffered Protocol V2 frames before each call', async () => {
318
+ const webusb = new WebUsbTransport() as any;
319
+ const path = 'pro2-webusb';
320
+ webusb.messages = transport.parseConfigure(schema);
321
+ webusb.messagesV2 = transport.parseConfigure(schema);
322
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
323
+ const firstResponse = ProtocolV2.encodeFrame(
324
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
325
+ 'Success',
326
+ { message: 'first' },
327
+ { router: PROTOCOL_V2_CHANNEL_USB, seq: 1 }
328
+ );
329
+ const secondResponse = ProtocolV2.encodeFrame(
330
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
331
+ 'Success',
332
+ { message: 'second' },
333
+ { router: PROTOCOL_V2_CHANNEL_USB, seq: 2 }
334
+ );
335
+ const coalescedResponses = new Uint8Array(firstResponse.length + secondResponse.length);
336
+ coalescedResponses.set(firstResponse);
337
+ coalescedResponses.set(secondResponse, firstResponse.length);
338
+ webusb.readProtocolV2UsbPacket = jest.fn().mockResolvedValue(coalescedResponses);
339
+ webusb.resetProtocolV2UsbNativeLink = jest.fn().mockResolvedValue(undefined);
340
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
341
+
342
+ await expect(webusb.callProtocolV2(path, 'Ping', { message: 'first' })).resolves.toMatchObject({
343
+ type: 'Success',
344
+ message: { message: 'first' },
345
+ });
346
+ await expect(webusb.callProtocolV2(path, 'Ping', { message: 'second' })).resolves.toMatchObject(
347
+ {
348
+ type: 'Success',
349
+ message: { message: 'second' },
350
+ }
351
+ );
352
+
353
+ expect(webusb.readProtocolV2UsbPacket).toHaveBeenCalledTimes(1);
354
+ });
355
+
356
+ test('keeps queued Protocol V2 read timeouts scoped to each call', async () => {
357
+ const webusb = new WebUsbTransport() as any;
358
+ const path = 'pro2-webusb';
359
+ let responseSequence = 0;
360
+ const readTimeouts: number[] = [];
361
+ webusb.messages = transport.parseConfigure(schema);
362
+ webusb.messagesV2 = transport.parseConfigure(schema);
363
+ webusb.writeProtocolV2UsbPacket = jest.fn().mockResolvedValue(undefined);
364
+ webusb.readProtocolV2UsbPacket = jest.fn().mockImplementation((_path, context) => {
365
+ responseSequence += 1;
366
+ readTimeouts.push(context.timeoutMs);
367
+ return Promise.resolve(
368
+ ProtocolV2.encodeFrame(
369
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
370
+ 'Success',
371
+ { message: 'ok' },
372
+ { seq: responseSequence }
373
+ )
374
+ );
375
+ });
376
+ webusb.resetProtocolV2UsbNativeLink = jest.fn().mockResolvedValue(undefined);
377
+ await webusb.rotateProtocolV2UsbGeneration(path, 'test connection');
378
+
379
+ await Promise.all([
380
+ webusb.callProtocolV2(path, 'Ping', { message: 'long' }, { timeoutMs: 1_000 }),
381
+ webusb.callProtocolV2(path, 'Ping', { message: 'short' }, { timeoutMs: 25 }),
382
+ ]);
383
+
384
+ expect(readTimeouts).toEqual([1_000, 25]);
385
+ });
386
+ });
@@ -0,0 +1,2 @@
1
+ export declare function resolveBlePacketCapacity(mtu: number | null | undefined, maximumPacketCapacity: number, fallbackPacketCapacity: number): number;
2
+ //# sourceMappingURL=ble-packet-capacity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble-packet-capacity.d.ts","sourceRoot":"","sources":["../src/ble-packet-capacity.ts"],"names":[],"mappings":"AAEA,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,qBAAqB,EAAE,MAAM,EAC7B,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAMR"}
@@ -12,24 +12,31 @@ export type BleAcquireInput = {
12
12
  uuid: string;
13
13
  forceCleanRunPromise?: boolean;
14
14
  expectedProtocol?: ProtocolType;
15
+ protocolHint?: ProtocolType;
15
16
  };
16
17
  export default class ElectronBleTransport {
17
18
  private _messages;
18
19
  private _messagesV2;
20
+ private protocolV2SchemaConfiguration;
19
21
  name: string;
20
22
  configured: boolean;
21
23
  runPromise: Deferred<Uint8Array | string> | null;
24
+ private runPromiseDeviceId;
22
25
  Log?: any;
23
26
  emitter?: EventEmitter;
24
27
  private connectedDevices;
25
28
  private deviceProtocol;
26
29
  private deviceProtocolHints;
30
+ private deviceMtus;
31
+ private devicePacketCapacities;
27
32
  private v1Buffers;
28
33
  private v2Assemblers;
29
34
  private v2FrameQueues;
30
35
  private v2FramePromises;
31
36
  private protocolV2Links;
37
+ private warnedMissingRelease;
32
38
  private notificationCleanups;
39
+ private mtuCleanups;
33
40
  private disconnectCleanups;
34
41
  private notificationTokens;
35
42
  private nextNotificationToken;
@@ -51,7 +58,10 @@ export default class ElectronBleTransport {
51
58
  name: string | null;
52
59
  protocolType?: ProtocolType | undefined;
53
60
  }>;
54
- release(id: string): Promise<void>;
61
+ release(id: string, _onclose?: boolean, keepSession?: boolean): Promise<void>;
62
+ disconnect(id: string): Promise<void>;
63
+ private releaseNative;
64
+ private releaseLogical;
55
65
  private createProtocolMismatchError;
56
66
  private createProtocolDetectionError;
57
67
  private clearProbeProtocol;
@@ -60,13 +70,15 @@ export default class ElectronBleTransport {
60
70
  private resetProbeStateAfterProtocolProbe;
61
71
  private probeProtocolV1;
62
72
  private probeProtocolV2;
63
- private writeWithChunking;
64
73
  private writeOnce;
74
+ private refreshBlePacketCapacity;
75
+ private updateBlePacketCapacity;
76
+ private createMtuSubscription;
77
+ private writeProtocolV2Frame;
65
78
  private handleNotification;
66
79
  private handleProtocolV2Notification;
67
80
  private getProtocolV2FrameQueue;
68
81
  private resolveProtocolV2Frame;
69
- private rejectAllProtocolV2Frames;
70
82
  private resetProtocolV2Frames;
71
83
  private rejectProtocolV2Frames;
72
84
  private readProtocolV2Frame;
@@ -1 +1 @@
1
- {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAmBA,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;AAevC,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;IAQ7B,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;YA+BlB,cAAc;YAuEd,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAsBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EAEZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,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;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAqCF,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,OAAO,CAAC,kBAAkB,CAAuB;IAEjD,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,UAAU,CAAkC;IAEpD,OAAO,CAAC,sBAAsB,CAAkC;IAEhE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAGH,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,WAAW,CAAsC;IAEzD,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IAuC1B,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;;;;;;;;;;;IAuG9B,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,OAAO;IAY7D,UAAU,CAAC,EAAE,EAAE,MAAM;YAKb,aAAa;YAiBb,cAAc;IAwB5B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YA+CjC,eAAe;YAgBf,eAAe;YAuBf,SAAS;YAST,wBAAwB;IAKtC,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,oBAAoB;IAqB5B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAqB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YAuBlB,cAAc;YAqFd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IAyC/B,OAAO,CAAC,6BAA6B;IA4CrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/dist/index.d.ts CHANGED
@@ -1,48 +1,99 @@
1
1
  import * as _onekeyfe_hd_transport from '@onekeyfe/hd-transport';
2
- import _onekeyfe_hd_transport__default, { AcquireInput, TransportCallOptions, ProtocolType, OneKeyDeviceInfoBase, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
2
+ import _onekeyfe_hd_transport__default, { ProtocolV2UsbTransportBase, AcquireInput, TransportCallOptions, ProtocolV2Schemas, ProtocolV2CallContext, ProtocolType, OneKeyDeviceInfoBase, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
3
3
  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;
10
13
  protocolType?: ProtocolType;
11
14
  }
12
- declare class WebUsbTransport {
15
+ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
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
+ private protocolV2SchemaSource;
20
+ /** Per-path protocol type detected by active wire-level probe. */
15
21
  private deviceProtocol;
16
22
  private deviceProtocolHints;
17
- private protocolV2Assemblers;
18
- private protocolV2Sessions;
19
- private protocolV2ReadTimeouts;
23
+ /** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
20
24
  private deviceEndpoints;
21
- private mockSerialPaths;
22
- private mockSerialCounter;
23
25
  name: string;
24
26
  stopped: boolean;
25
27
  configured: boolean;
26
28
  Log?: any;
27
29
  usb?: USB;
30
+ /**
31
+ * Cached list of connected devices
32
+ * This is essential for maintaining device references between operations
33
+ */
28
34
  deviceList: Array<DeviceInfo>;
29
35
  configurationId: number;
30
36
  endpointId: number;
31
37
  interfaceId: number;
38
+ constructor();
39
+ /**
40
+ * Initialize WebUSB transport
41
+ */
32
42
  init(logger: any): void;
43
+ /**
44
+ * Configure Protocol V1 protobuf schema (legacy chunked 0x3F framing).
45
+ */
33
46
  configure(signedData: any): void;
47
+ /**
48
+ * Cache the Protocol V2 protobuf schema.
49
+ */
34
50
  configureProtocolV2(signedData: any): void;
51
+ /**
52
+ * Request user to select a device
53
+ * This method must be called in response to a user action
54
+ * to comply with WebUSB security requirements
55
+ */
35
56
  promptDeviceAccess(): Promise<USBDevice | null>;
57
+ /**
58
+ * Enumerate already connected devices
59
+ * This method only returns devices that are already authorized by the browser
60
+ * It does NOT prompt the user to select a device
61
+ */
36
62
  enumerate(): Promise<DeviceInfo[]>;
37
- private getDevicePath;
63
+ /**
64
+ * Get list of connected devices
65
+ */
38
66
  getConnectedDevices(): Promise<DeviceInfo[]>;
67
+ /**
68
+ * Acquire device control
69
+ */
39
70
  acquire(input: AcquireInput): Promise<string | undefined>;
71
+ /**
72
+ * Determine protocol type after connect.
73
+ * Probe Protocol V1 first with Initialize. If it does not answer in time,
74
+ * fall back to a Protocol V2 Ping probe.
75
+ */
40
76
  private createProtocolMismatchError;
77
+ private createProtocolProbeTimeoutError;
41
78
  private createProtocolDetectionError;
42
79
  private detectProtocol;
80
+ /**
81
+ * Find device by path
82
+ */
43
83
  findDevice(path: string): Promise<USBDevice>;
84
+ /**
85
+ * Connect to device with retry mechanism
86
+ */
44
87
  connect(path: string, first: boolean): Promise<void>;
88
+ /**
89
+ * Discover vendor-class (0xFF) interface and its IN/OUT endpoint numbers from USB descriptors.
90
+ * Falls back to legacy hardcoded values if no vendor interface is found.
91
+ */
45
92
  private discoverEndpoints;
93
+ /**
94
+ * Connect to specific device.
95
+ * Discovers interface/endpoint numbers from USB descriptors on first connection.
96
+ */
46
97
  connectToDevice(path: string, first: boolean): Promise<void>;
47
98
  private closeOpenDevice;
48
99
  private clearEndpointHalt;
@@ -55,16 +106,43 @@ declare class WebUsbTransport {
55
106
  private transferOutWithRetry;
56
107
  private transferOutOnce;
57
108
  private transferInWithRetry;
109
+ private transferInOnce;
110
+ private closeConnectionAfterProbe;
58
111
  private resetConnectionAfterProbe;
59
112
  private withProtocolReadTimeout;
60
113
  private probeProtocolV1;
61
114
  private probeProtocolV2;
115
+ /**
116
+ * Call device method — branches to Protocol V1 or Protocol V2 based on active probe.
117
+ */
62
118
  call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
63
119
  private callProtocolV1;
120
+ /**
121
+ * Send/receive a single call over Protocol V2 (0x5A framing).
122
+ *
123
+ * Encoding: protobuf message → 2-byte LE messageTypeId + pb bytes → Protocol V2 frame
124
+ * Decoding: Protocol V2 frame → messageTypeId + pb bytes → protobuf message
125
+ */
64
126
  private callProtocolV2;
65
- private receiveProtocolV2Frame;
127
+ /**
128
+ * Receive data from device
129
+ */
66
130
  receiveData(path: string, timeoutMs?: number): Promise<string>;
131
+ /**
132
+ * Release device
133
+ */
67
134
  release(path: string): Promise<void>;
135
+ protected getProtocolV2UsbSchemas(): ProtocolV2Schemas;
136
+ protected getProtocolV2UsbLogger(): any;
137
+ protected writeProtocolV2UsbPacket(path: string, frame: Uint8Array, _context: ProtocolV2CallContext): Promise<void>;
138
+ protected readProtocolV2UsbPacket(path: string, _context: ProtocolV2CallContext): Promise<Uint8Array>;
139
+ protected resetProtocolV2UsbNativeLink(path: string, _reason: string): Promise<void>;
140
+ protected onProtocolV2UsbLinkInvalidated(path: string, reason: string): void;
141
+ protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error;
142
+ /**
143
+ * Expose the detected protocol type for a given device path.
144
+ * Used by upper layers (e.g. TransportManager) to select the correct schema.
145
+ */
68
146
  getProtocolType(path: string): ProtocolType | undefined;
69
147
  }
70
148
 
@@ -77,24 +155,38 @@ type BleAcquireInput = {
77
155
  uuid: string;
78
156
  forceCleanRunPromise?: boolean;
79
157
  expectedProtocol?: ProtocolType;
158
+ protocolHint?: ProtocolType;
80
159
  };
160
+ /**
161
+ * Desktop Electron BLE transport with automatic Protocol V1/V2 detection.
162
+ *
163
+ * Protocol V1 devices continue using chunked packets. Protocol V2 is detected
164
+ * after a Protocol V1 Initialize timeout by probing Protocol V2 Ping.
165
+ */
81
166
  declare class ElectronBleTransport {
82
167
  private _messages;
83
168
  private _messagesV2;
169
+ private protocolV2SchemaConfiguration;
84
170
  name: string;
85
171
  configured: boolean;
86
172
  runPromise: Deferred<Uint8Array | string> | null;
173
+ private runPromiseDeviceId;
87
174
  Log?: any;
88
175
  emitter?: EventEmitter;
89
176
  private connectedDevices;
90
177
  private deviceProtocol;
91
178
  private deviceProtocolHints;
179
+ private deviceMtus;
180
+ private devicePacketCapacities;
92
181
  private v1Buffers;
93
182
  private v2Assemblers;
94
183
  private v2FrameQueues;
95
184
  private v2FramePromises;
96
185
  private protocolV2Links;
186
+ /** One-shot guard so a missing preload `release` bridge is reported once. */
187
+ private warnedMissingRelease;
97
188
  private notificationCleanups;
189
+ private mtuCleanups;
98
190
  private disconnectCleanups;
99
191
  private notificationTokens;
100
192
  private nextNotificationToken;
@@ -116,7 +208,10 @@ declare class ElectronBleTransport {
116
208
  name: string | null;
117
209
  protocolType?: ProtocolType | undefined;
118
210
  }>;
119
- release(id: string): Promise<void>;
211
+ release(id: string, _onclose?: boolean, keepSession?: boolean): Promise<void>;
212
+ disconnect(id: string): Promise<void>;
213
+ private releaseNative;
214
+ private releaseLogical;
120
215
  private createProtocolMismatchError;
121
216
  private createProtocolDetectionError;
122
217
  private clearProbeProtocol;
@@ -125,13 +220,15 @@ declare class ElectronBleTransport {
125
220
  private resetProbeStateAfterProtocolProbe;
126
221
  private probeProtocolV1;
127
222
  private probeProtocolV2;
128
- private writeWithChunking;
129
223
  private writeOnce;
224
+ private refreshBlePacketCapacity;
225
+ private updateBlePacketCapacity;
226
+ private createMtuSubscription;
227
+ private writeProtocolV2Frame;
130
228
  private handleNotification;
131
229
  private handleProtocolV2Notification;
132
230
  private getProtocolV2FrameQueue;
133
231
  private resolveProtocolV2Frame;
134
- private rejectAllProtocolV2Frames;
135
232
  private resetProtocolV2Frames;
136
233
  private rejectProtocolV2Frames;
137
234
  private readProtocolV2Frame;