@onekeyfe/hd-transport-web-device 1.2.0-alpha.33 → 1.2.0-alpha.35

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.
@@ -1,5 +1,5 @@
1
1
  import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
2
- import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
3
3
  import EventEmitter from 'events';
4
4
 
5
5
  import ElectronBleTransport from '../src/electron-ble-transport';
@@ -11,6 +11,9 @@ const protocolV1Schema = {
11
11
  Initialize: {
12
12
  fields: {},
13
13
  },
14
+ GetFeatures: {
15
+ fields: {},
16
+ },
14
17
  Success: {
15
18
  fields: {
16
19
  message: {
@@ -23,6 +26,7 @@ const protocolV1Schema = {
23
26
  values: {
24
27
  MessageType_Initialize: 1,
25
28
  MessageType_Success: 2,
29
+ MessageType_GetFeatures: 55,
26
30
  },
27
31
  },
28
32
  },
@@ -238,7 +242,7 @@ describe('ElectronBleTransport protocol detection', () => {
238
242
  }
239
243
  });
240
244
 
241
- test('detects Protocol V1 when device responds to Initialize', async () => {
245
+ test('reconnects Protocol V1 with a non-destructive GetFeatures probe', async () => {
242
246
  const device = { id: 'classic-id', name: 'OneKey Classic' };
243
247
  const nobleBle = createNobleBle(device);
244
248
  let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
@@ -252,7 +256,7 @@ describe('ElectronBleTransport protocol detection', () => {
252
256
  return jest.fn();
253
257
  });
254
258
  nobleBle.write.mockImplementation(() => {
255
- // Respond to first write (V1 Initialize probe) with V1 Success
259
+ // The first write is the V1 GetFeatures probe; answer with a V1 Success response.
256
260
  setTimeout(() => notificationHandler?.(device.id, v1ResponseHex), 0);
257
261
  return Promise.resolve();
258
262
  });
@@ -266,12 +270,100 @@ describe('ElectronBleTransport protocol detection', () => {
266
270
  })
267
271
  );
268
272
  expect(transport.getProtocolType(device.id)).toBe('V1');
273
+ await expect(transport.acquire({ uuid: device.id, expectedProtocol: 'V1' })).resolves.toEqual(
274
+ expect.objectContaining({
275
+ uuid: device.id,
276
+ })
277
+ );
278
+ expect(nobleBle.write).toHaveBeenCalledTimes(2);
279
+ expect(nobleBle.write.mock.calls.every(([, hex]) => /^3f23230037/.test(hex))).toBe(true);
269
280
  expect(protocolV2Writer).not.toHaveBeenCalled();
270
281
  } finally {
271
282
  await transport.release(device.id);
272
283
  }
273
284
  });
274
285
 
286
+ test('invalidates and disconnects a Protocol V1 link after a response timeout', async () => {
287
+ const device = { id: 'classic-timeout-id', name: 'OneKey Classic' };
288
+ const nobleBle = createNobleBle(device);
289
+ let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
290
+ const v1ResponseHex = '3f23230002000000040a026f6b';
291
+ let writeCount = 0;
292
+
293
+ nobleBle.onNotification.mockImplementation(handler => {
294
+ notificationHandler = handler;
295
+ return jest.fn();
296
+ });
297
+ nobleBle.write.mockImplementation(() => {
298
+ writeCount += 1;
299
+ if (writeCount === 1) {
300
+ setTimeout(() => notificationHandler?.(device.id, v1ResponseHex), 0);
301
+ }
302
+ return Promise.resolve();
303
+ });
304
+ const bleTransport = configureTransport(nobleBle);
305
+
306
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V1' });
307
+ await expect(
308
+ bleTransport.call(device.id, 'Initialize', {}, { timeoutMs: 5 })
309
+ ).rejects.toMatchObject({ errorCode: HardwareErrorCode.BleTimeoutError });
310
+
311
+ expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
312
+ expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
313
+ expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
314
+ });
315
+
316
+ test('keeps another device V2 reader when force-cleaning a V1 call', async () => {
317
+ const device = { id: 'classic-force-clean-id', name: 'OneKey Classic' };
318
+ const nobleBle = createNobleBle(device);
319
+ let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
320
+ const v1ResponseHex = '3f23230002000000040a026f6b';
321
+ nobleBle.onNotification.mockImplementation(handler => {
322
+ notificationHandler = handler;
323
+ return jest.fn();
324
+ });
325
+ nobleBle.write.mockImplementation(() => {
326
+ setTimeout(() => notificationHandler?.(device.id, v1ResponseHex), 0);
327
+ return Promise.resolve();
328
+ });
329
+ const bleTransport = configureTransport(nobleBle) as any;
330
+ const activeV1Call = createDeferred<string>();
331
+ const otherDeviceReader = createDeferred<Uint8Array>();
332
+ activeV1Call.promise.catch(() => undefined);
333
+ otherDeviceReader.promise.catch(() => undefined);
334
+ bleTransport.runPromise = activeV1Call;
335
+ bleTransport.v2FramePromises.set('device-b', otherDeviceReader);
336
+
337
+ await bleTransport.acquire({
338
+ uuid: device.id,
339
+ expectedProtocol: 'V1',
340
+ forceCleanRunPromise: true,
341
+ });
342
+
343
+ expect(bleTransport.v2FramePromises.get('device-b')).toBe(otherDeviceReader);
344
+ await bleTransport.release(device.id);
345
+ });
346
+
347
+ test('rejects a pending V2 reader when its device frame state resets', async () => {
348
+ const nobleBle = createNobleBle();
349
+ const bleTransport = configureTransport(nobleBle) as any;
350
+ const reader = createDeferred<Uint8Array>();
351
+ bleTransport.v2FramePromises.set('device-a', reader);
352
+ const result = Promise.race([
353
+ reader.promise.then(
354
+ () => 'resolved',
355
+ () => 'rejected'
356
+ ),
357
+ new Promise(resolve => {
358
+ setTimeout(() => resolve('pending'), 20);
359
+ }),
360
+ ]);
361
+
362
+ bleTransport.resetProtocolV2Frames('device-a');
363
+
364
+ await expect(result).resolves.toBe('rejected');
365
+ });
366
+
275
367
  test('throws when both protocol probes fail', async () => {
276
368
  const device = { id: 'dead-device-id', name: 'Unknown Device' };
277
369
  const nobleBle = createNobleBle(device);
@@ -3,7 +3,7 @@ import transport, {
3
3
  ProtocolV2,
4
4
  ProtocolV2LinkError,
5
5
  } from '@onekeyfe/hd-transport';
6
- import { ONEKEY_WEBUSB_FILTER } from '@onekeyfe/hd-shared';
6
+ import { HardwareErrorCode, ONEKEY_WEBUSB_FILTER } from '@onekeyfe/hd-shared';
7
7
 
8
8
  import WebUsbTransport from '../src/webusb';
9
9
 
@@ -96,6 +96,43 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
96
96
  expect(webusb.deviceProtocol.get(path)).toBe('V2');
97
97
  });
98
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
+
99
136
  test('retries an expected Protocol V2 probe once after resetting the connection', async () => {
100
137
  const webusb = new WebUsbTransport() as any;
101
138
  const path = 'pro2-webusb';
@@ -117,6 +154,7 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
117
154
  webusb.probeProtocolV1 = jest.fn();
118
155
  webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
119
156
  webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
157
+ webusb.closeConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
120
158
 
121
159
  await expect(webusb.detectProtocol(path, 'V2')).rejects.toThrow(
122
160
  'Protocol V2 probe timeout after 2 attempts'
@@ -124,10 +162,40 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
124
162
 
125
163
  expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
126
164
  expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
127
- expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(2);
165
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
166
+ expect(webusb.closeConnectionAfterProbe).toHaveBeenCalledTimes(1);
128
167
  expect(webusb.deviceProtocol.has(path)).toBe(false);
129
168
  });
130
169
 
170
+ test('closes the reopened device when acquire exhausts the expected Protocol V2 probe', async () => {
171
+ const webusb = new WebUsbTransport() as any;
172
+ const path = 'pro2-webusb';
173
+ const device = {
174
+ opened: false,
175
+ releaseInterface: jest.fn().mockResolvedValue(undefined),
176
+ close: jest.fn().mockImplementation(() => {
177
+ device.opened = false;
178
+ return Promise.resolve();
179
+ }),
180
+ };
181
+ webusb.deviceList = [{ path, device }];
182
+ webusb.Log = { debug: jest.fn() };
183
+ webusb.rotateProtocolV2UsbGeneration = jest.fn().mockResolvedValue(undefined);
184
+ webusb.connect = jest.fn().mockImplementation(() => {
185
+ device.opened = true;
186
+ return Promise.resolve();
187
+ });
188
+ webusb.detectProtocol = jest.fn().mockRejectedValue(new Error('terminal probe failure'));
189
+
190
+ await expect(webusb.acquire({ path, expectedProtocol: 'V2' })).rejects.toThrow(
191
+ 'terminal probe failure'
192
+ );
193
+
194
+ expect(device.releaseInterface).toHaveBeenCalledTimes(1);
195
+ expect(device.close).toHaveBeenCalledTimes(1);
196
+ expect(device.opened).toBe(false);
197
+ });
198
+
131
199
  test('invalidates and resets the cached connection before another call can start', async () => {
132
200
  const webusb = new WebUsbTransport() as any;
133
201
  const path = 'pro2-webusb';
@@ -12,6 +12,7 @@ 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;
@@ -20,6 +21,7 @@ export default class ElectronBleTransport {
20
21
  name: string;
21
22
  configured: boolean;
22
23
  runPromise: Deferred<Uint8Array | string> | null;
24
+ private runPromiseDeviceId;
23
25
  Log?: any;
24
26
  emitter?: EventEmitter;
25
27
  private connectedDevices;
@@ -68,7 +70,6 @@ export default class ElectronBleTransport {
68
70
  private handleProtocolV2Notification;
69
71
  private getProtocolV2FrameQueue;
70
72
  private resolveProtocolV2Frame;
71
- private rejectAllProtocolV2Frames;
72
73
  private resetProtocolV2Frames;
73
74
  private rejectProtocolV2Frames;
74
75
  private readProtocolV2Frame;
@@ -1 +1 @@
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;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;YAUV,aAAa;IAe3B,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,SAAS;IASvB,OAAO,CAAC,oBAAoB;IAmB5B,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;IAyC/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;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,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,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;IA+B1B,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;;;;;;;;;;;IAsF9B,OAAO,CAAC,EAAE,EAAE,MAAM;YAUV,aAAa;IAe3B,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YA8CjC,eAAe;YAkBf,eAAe;YAuBf,SAAS;IASvB,OAAO,CAAC,oBAAoB;IAmB5B,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;YA2BlB,cAAc;YAqFd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IAyC/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/dist/index.d.ts CHANGED
@@ -107,6 +107,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
107
107
  private transferOutOnce;
108
108
  private transferInWithRetry;
109
109
  private transferInOnce;
110
+ private closeConnectionAfterProbe;
110
111
  private resetConnectionAfterProbe;
111
112
  private withProtocolReadTimeout;
112
113
  private probeProtocolV1;
@@ -154,12 +155,13 @@ type BleAcquireInput = {
154
155
  uuid: string;
155
156
  forceCleanRunPromise?: boolean;
156
157
  expectedProtocol?: ProtocolType;
158
+ protocolHint?: ProtocolType;
157
159
  };
158
160
  /**
159
161
  * Desktop Electron BLE transport with automatic Protocol V1/V2 detection.
160
162
  *
161
163
  * Protocol V1 devices continue using chunked packets. Protocol V2 is detected
162
- * after a Protocol V1 Initialize timeout by probing Protocol V2 Ping.
164
+ * after a Protocol V1 GetFeatures timeout by probing Protocol V2 Ping.
163
165
  */
164
166
  declare class ElectronBleTransport {
165
167
  private _messages;
@@ -168,6 +170,7 @@ declare class ElectronBleTransport {
168
170
  name: string;
169
171
  configured: boolean;
170
172
  runPromise: Deferred<Uint8Array | string> | null;
173
+ private runPromiseDeviceId;
171
174
  Log?: any;
172
175
  emitter?: EventEmitter;
173
176
  private connectedDevices;
@@ -216,7 +219,6 @@ declare class ElectronBleTransport {
216
219
  private handleProtocolV2Notification;
217
220
  private getProtocolV2FrameQueue;
218
221
  private resolveProtocolV2Frame;
219
- private rejectAllProtocolV2Frames;
220
222
  private resetProtocolV2Frames;
221
223
  private rejectProtocolV2Frames;
222
224
  private readProtocolV2Frame;
package/dist/index.js CHANGED
@@ -41,14 +41,6 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
41
41
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
42
42
  };
43
43
 
44
- const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
45
- function shouldSuppressHighVolumeCallLog(name) {
46
- return HIGH_VOLUME_CALLS.has(name);
47
- }
48
- function createTransportCallLog(name, protocol) {
49
- return { name, protocol };
50
- }
51
-
52
44
  const { parseConfigure: parseConfigure$1, check: check$1, ProtocolV1: ProtocolV1$1 } = transport__default["default"];
53
45
  const CONFIGURATION_ID = 1;
54
46
  const INTERFACE_ID = 0;
@@ -59,7 +51,8 @@ const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
59
51
  const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
60
52
  const PACKET_IO_MAX_RETRIES = 3;
61
53
  const PACKET_IO_RETRY_DELAY = 300;
62
- const PROTOCOL_PROBE_TIMEOUT = 1000;
54
+ const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
55
+ const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
63
56
  const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
64
57
  function inferProtocolHintFromDeviceName$1(name) {
65
58
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
@@ -155,7 +148,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
155
148
  });
156
149
  }
157
150
  acquire(input) {
158
- var _a, _b, _c;
151
+ var _a, _b, _c, _d;
159
152
  return __awaiter(this, void 0, void 0, function* () {
160
153
  if (!input.path)
161
154
  return;
@@ -166,7 +159,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
166
159
  const deviceName = (_b = this.deviceList.find(device => device.path === input.path)) === null || _b === void 0 ? void 0 : _b.device.productName;
167
160
  const protocolHint = input.expectedProtocol
168
161
  ? undefined
169
- : (_c = this.deviceProtocolHints.get(input.path)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName$1(deviceName);
162
+ : (_d = (_c = input.protocolHint) !== null && _c !== void 0 ? _c : this.deviceProtocolHints.get(input.path)) !== null && _d !== void 0 ? _d : inferProtocolHintFromDeviceName$1(deviceName);
170
163
  if (protocolHint) {
171
164
  this.deviceProtocolHints.set(input.path, protocolHint);
172
165
  }
@@ -175,6 +168,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
175
168
  }
176
169
  catch (e) {
177
170
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
171
+ yield this.closeOpenDevice(input.path);
178
172
  throw e;
179
173
  }
180
174
  });
@@ -186,7 +180,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
186
180
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
187
181
  }
188
182
  createProtocolDetectionError() {
189
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
183
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
190
184
  }
191
185
  detectProtocol(path, expectedProtocol, protocolHint) {
192
186
  var _a;
@@ -196,7 +190,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
196
190
  this.deviceProtocol.set(path, 'V1');
197
191
  return 'V1';
198
192
  }
199
- yield this.resetConnectionAfterProbe(path);
193
+ yield this.closeConnectionAfterProbe(path);
200
194
  throw this.createProtocolMismatchError(expectedProtocol);
201
195
  }
202
196
  if (expectedProtocol === 'V2') {
@@ -205,22 +199,30 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
205
199
  this.deviceProtocol.set(path, 'V2');
206
200
  return 'V2';
207
201
  }
208
- yield this.resetConnectionAfterProbe(path);
209
202
  if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
203
+ yield this.resetConnectionAfterProbe(path);
210
204
  (_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}`);
211
205
  }
206
+ else {
207
+ yield this.closeConnectionAfterProbe(path);
208
+ }
212
209
  }
213
210
  this.deviceProtocol.delete(path);
214
211
  throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
215
212
  }
216
213
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
217
- for (const protocol of probeOrder) {
214
+ for (const [index, protocol] of probeOrder.entries()) {
218
215
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
219
216
  if (detected) {
220
217
  this.deviceProtocol.set(path, protocol);
221
218
  return protocol;
222
219
  }
223
- yield this.resetConnectionAfterProbe(path);
220
+ if (index < probeOrder.length - 1) {
221
+ yield this.resetConnectionAfterProbe(path);
222
+ }
223
+ else {
224
+ yield this.closeConnectionAfterProbe(path);
225
+ }
224
226
  }
225
227
  this.deviceProtocol.delete(path);
226
228
  throw this.createProtocolDetectionError();
@@ -509,27 +511,15 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
509
511
  return this.getTransferInData(result);
510
512
  });
511
513
  }
512
- resetConnectionAfterProbe(path) {
513
- var _a;
514
+ closeConnectionAfterProbe(path) {
514
515
  return __awaiter(this, void 0, void 0, function* () {
515
516
  yield this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
516
- try {
517
- const device = yield this.findDevice(path);
518
- if (device.opened) {
519
- const endpoints = this.deviceEndpoints.get(path);
520
- const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
521
- try {
522
- yield device.releaseInterface(ifaceNum);
523
- }
524
- catch (error) {
525
- this.Log.debug('[WebUsbTransport] releaseInterface after protocol probe error:', error);
526
- }
527
- yield device.close();
528
- }
529
- }
530
- catch (error) {
531
- this.Log.debug('[WebUsbTransport] close after protocol probe error:', error);
532
- }
517
+ yield this.closeOpenDevice(path);
518
+ });
519
+ }
520
+ resetConnectionAfterProbe(path) {
521
+ return __awaiter(this, void 0, void 0, function* () {
522
+ yield this.closeConnectionAfterProbe(path);
533
523
  yield this.getConnectedDevices();
534
524
  yield this.connect(path, false);
535
525
  });
@@ -577,7 +567,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
577
567
  return false;
578
568
  }
579
569
  try {
580
- yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
570
+ yield this.callProtocolV1(path, 'Initialize', {}, {
571
+ timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
572
+ });
581
573
  return true;
582
574
  }
583
575
  catch (_error) {
@@ -592,7 +584,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
592
584
  }
593
585
  return transport.probeProtocolV2({
594
586
  call: (name, data, options) => this.callProtocolV2(path, name, data, options),
595
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
587
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
596
588
  logger: this.Log,
597
589
  logPrefix: 'ProtocolV2 WebUSB',
598
590
  });
@@ -611,8 +603,8 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
611
603
  if (!protocol) {
612
604
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
613
605
  }
614
- if (!shouldSuppressHighVolumeCallLog(name)) {
615
- this.Log.debug('transport call', createTransportCallLog(name, protocol));
606
+ if (!transport.shouldSuppressHighVolumeCallLog(name)) {
607
+ this.Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
616
608
  }
617
609
  if (protocol === 'V2') {
618
610
  return this.callProtocolV2(path, name, data, options);
@@ -747,6 +739,7 @@ class ElectronBleTransport {
747
739
  this.name = 'ElectronBleTransport';
748
740
  this.configured = false;
749
741
  this.runPromise = null;
742
+ this.runPromiseDeviceId = null;
750
743
  this.connectedDevices = new Set();
751
744
  this.deviceProtocol = new Map();
752
745
  this.deviceProtocolHints = new Map();
@@ -819,6 +812,11 @@ class ElectronBleTransport {
819
812
  this.v2Assemblers.delete(deviceId);
820
813
  this.resetProtocolV2Frames(deviceId);
821
814
  this.notificationTokens.delete(deviceId);
815
+ if (this.runPromise && this.runPromiseDeviceId === deviceId) {
816
+ this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceDisconnected));
817
+ this.runPromise = null;
818
+ this.runPromiseDeviceId = null;
819
+ }
822
820
  const notifyCleanup = this.notificationCleanups.get(deviceId);
823
821
  if (notifyCleanup) {
824
822
  notifyCleanup();
@@ -887,7 +885,7 @@ class ElectronBleTransport {
887
885
  });
888
886
  }
889
887
  acquire(input) {
890
- var _a, _b, _c, _d, _e;
888
+ var _a, _b, _c, _d, _e, _f;
891
889
  return __awaiter(this, void 0, void 0, function* () {
892
890
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
893
891
  if (!uuid) {
@@ -899,8 +897,8 @@ class ElectronBleTransport {
899
897
  if (forceCleanRunPromise && this.runPromise) {
900
898
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
901
899
  this.runPromise.reject(error);
902
- this.rejectAllProtocolV2Frames(error);
903
900
  this.runPromise = null;
901
+ this.runPromiseDeviceId = null;
904
902
  }
905
903
  try {
906
904
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
@@ -912,7 +910,7 @@ class ElectronBleTransport {
912
910
  }
913
911
  const protocolHint = expectedProtocol
914
912
  ? undefined
915
- : (_b = this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(device.name);
913
+ : (_c = (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName(device.name);
916
914
  if (protocolHint) {
917
915
  this.deviceProtocolHints.set(uuid, protocolHint);
918
916
  }
@@ -928,6 +926,7 @@ class ElectronBleTransport {
928
926
  yield window.desktopApi.nobleBle.subscribe(uuid);
929
927
  const cleanup = this.createNotificationSubscription(uuid);
930
928
  this.notificationCleanups.set(uuid, cleanup);
929
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
931
930
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
932
931
  var _a;
933
932
  if (disconnectedDevice.id === uuid) {
@@ -940,19 +939,18 @@ class ElectronBleTransport {
940
939
  }
941
940
  });
942
941
  this.disconnectCleanups.set(uuid, disconnectCleanup);
943
- const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
944
942
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
945
943
  }
946
944
  catch (error) {
947
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] acquire failed:', error);
945
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] acquire failed:', error);
948
946
  try {
949
- if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
947
+ if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
950
948
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
951
949
  yield window.desktopApi.nobleBle.disconnect(uuid);
952
950
  }
953
951
  }
954
952
  catch (cleanupError) {
955
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
953
+ (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
956
954
  }
957
955
  this.cleanupDeviceState(uuid);
958
956
  throw error;
@@ -994,7 +992,7 @@ class ElectronBleTransport {
994
992
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
995
993
  }
996
994
  createProtocolDetectionError() {
997
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
995
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
998
996
  }
999
997
  clearProbeProtocol(uuid, protocol) {
1000
998
  if (this.deviceProtocol.get(uuid) === protocol) {
@@ -1052,16 +1050,17 @@ class ElectronBleTransport {
1052
1050
  });
1053
1051
  }
1054
1052
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1055
- var _a, _b, _c, _d, _e, _f, _g;
1053
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1056
1054
  return __awaiter(this, void 0, void 0, function* () {
1057
1055
  yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1058
1056
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1059
1057
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1060
1058
  this.resetProtocolV2Frames(uuid);
1061
- if (this.runPromise) {
1059
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
1062
1060
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1063
1061
  this.runPromise.reject(error);
1064
1062
  this.runPromise = null;
1063
+ this.runPromiseDeviceId = null;
1065
1064
  }
1066
1065
  const notifyCleanup = this.notificationCleanups.get(uuid);
1067
1066
  if (notifyCleanup) {
@@ -1076,10 +1075,19 @@ class ElectronBleTransport {
1076
1075
  (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1077
1076
  }
1078
1077
  try {
1079
- yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
1078
+ yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.disconnect(uuid));
1080
1079
  }
1081
1080
  catch (error) {
1082
- (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1081
+ (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] disconnect after Protocol ${protocol} probe failed:`, error);
1082
+ }
1083
+ this.connectedDevices.delete(uuid);
1084
+ try {
1085
+ yield ((_j = (_h = window.desktopApi) === null || _h === void 0 ? void 0 : _h.nobleBle) === null || _j === void 0 ? void 0 : _j.connect(uuid));
1086
+ this.connectedDevices.add(uuid);
1087
+ yield ((_l = (_k = window.desktopApi) === null || _k === void 0 ? void 0 : _k.nobleBle) === null || _l === void 0 ? void 0 : _l.subscribe(uuid));
1088
+ }
1089
+ catch (error) {
1090
+ (_m = this.Log) === null || _m === void 0 ? void 0 : _m.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
1083
1091
  throw error;
1084
1092
  }
1085
1093
  const cleanup = this.createNotificationSubscription(uuid);
@@ -1094,12 +1102,12 @@ class ElectronBleTransport {
1094
1102
  }
1095
1103
  try {
1096
1104
  this.deviceProtocol.set(uuid, 'V1');
1097
- yield this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1105
+ yield this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
1098
1106
  return true;
1099
1107
  }
1100
1108
  catch (error) {
1101
1109
  this.clearProbeProtocol(uuid, 'V1');
1102
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 Initialize probe failed:', error);
1110
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
1103
1111
  return false;
1104
1112
  }
1105
1113
  });
@@ -1160,7 +1168,7 @@ class ElectronBleTransport {
1160
1168
  if (this.deviceProtocol.get(deviceId) === 'V2') {
1161
1169
  this.rejectProtocolV2Frames(deviceId, error);
1162
1170
  }
1163
- else if (this.runPromise) {
1171
+ else if (this.runPromise && this.runPromiseDeviceId === deviceId) {
1164
1172
  this.runPromise.reject(error);
1165
1173
  }
1166
1174
  return;
@@ -1212,16 +1220,8 @@ class ElectronBleTransport {
1212
1220
  }
1213
1221
  this.getProtocolV2FrameQueue(uuid).push(frame);
1214
1222
  }
1215
- rejectAllProtocolV2Frames(error) {
1216
- this.v2FrameQueues.clear();
1217
- for (const framePromise of this.v2FramePromises.values()) {
1218
- framePromise.reject(error);
1219
- }
1220
- this.v2FramePromises.clear();
1221
- }
1222
1223
  resetProtocolV2Frames(uuid) {
1223
- this.v2FrameQueues.delete(uuid);
1224
- this.v2FramePromises.delete(uuid);
1224
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
1225
1225
  }
1226
1226
  rejectProtocolV2Frames(uuid, error) {
1227
1227
  this.v2FrameQueues.delete(uuid);
@@ -1254,12 +1254,15 @@ class ElectronBleTransport {
1254
1254
  const result = this.processProtocolV1Notification(deviceId, hexData);
1255
1255
  if (result.error) {
1256
1256
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V1 packet processing error:', result.error);
1257
- if (this.runPromise) {
1257
+ if (this.runPromise && this.runPromiseDeviceId === deviceId) {
1258
1258
  this.runPromise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError));
1259
1259
  }
1260
1260
  return;
1261
1261
  }
1262
- if (result.isComplete && result.completePacket && this.runPromise) {
1262
+ if (result.isComplete &&
1263
+ result.completePacket &&
1264
+ this.runPromise &&
1265
+ this.runPromiseDeviceId === deviceId) {
1263
1266
  this.runPromise.resolve(result.completePacket);
1264
1267
  }
1265
1268
  }
@@ -1276,8 +1279,8 @@ class ElectronBleTransport {
1276
1279
  if (!protocol) {
1277
1280
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1278
1281
  }
1279
- if (!shouldSuppressHighVolumeCallLog(name)) {
1280
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
1282
+ if (!transport.shouldSuppressHighVolumeCallLog(name)) {
1283
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', transport.createTransportCallLog(name, protocol, data));
1281
1284
  }
1282
1285
  if (protocol === 'V2') {
1283
1286
  return this.callProtocolV2(uuid, name, data, options);
@@ -1298,6 +1301,7 @@ class ElectronBleTransport {
1298
1301
  const runPromise = hdShared.createDeferred();
1299
1302
  runPromise.promise.catch(() => undefined);
1300
1303
  this.runPromise = runPromise;
1304
+ this.runPromiseDeviceId = uuid;
1301
1305
  const messages = this._messages;
1302
1306
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
1303
1307
  let timeout;
@@ -1336,6 +1340,17 @@ class ElectronBleTransport {
1336
1340
  }
1337
1341
  catch (e) {
1338
1342
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V1 call error:', e);
1343
+ const isProbeTimeout = name === 'GetFeatures' && (options === null || options === void 0 ? void 0 : options.timeoutMs) === PROTOCOL_PROBE_TIMEOUT_MS;
1344
+ if ((e === null || e === void 0 ? void 0 : e.errorCode) === hdShared.HardwareErrorCode.BleTimeoutError) {
1345
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1346
+ const notifyCleanup = this.notificationCleanups.get(uuid);
1347
+ notifyCleanup === null || notifyCleanup === void 0 ? void 0 : notifyCleanup();
1348
+ this.notificationCleanups.delete(uuid);
1349
+ this.notificationTokens.delete(uuid);
1350
+ if (!isProbeTimeout) {
1351
+ yield this.releaseNative(uuid);
1352
+ }
1353
+ }
1339
1354
  throw e;
1340
1355
  }
1341
1356
  finally {
@@ -1343,6 +1358,7 @@ class ElectronBleTransport {
1343
1358
  clearTimeout(timeout);
1344
1359
  if (this.runPromise === runPromise) {
1345
1360
  this.runPromise = null;
1361
+ this.runPromiseDeviceId = null;
1346
1362
  }
1347
1363
  }
1348
1364
  });
@@ -1,7 +1,2 @@
1
- import type { ProtocolType } from '@onekeyfe/hd-transport';
2
- export declare function shouldSuppressHighVolumeCallLog(name: string): boolean;
3
- export declare function createTransportCallLog(name: string, protocol: ProtocolType): {
4
- name: string;
5
- protocol: ProtocolType;
6
- };
1
+ export { createTransportCallLog, shouldSuppressHighVolumeCallLog } from '@onekeyfe/hd-transport';
7
2
  //# sourceMappingURL=transportLog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,WAE3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY;;;EAE1E"}
1
+ {"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/webusb.d.ts CHANGED
@@ -50,6 +50,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
50
50
  private transferOutOnce;
51
51
  private transferInWithRetry;
52
52
  private transferInOnce;
53
+ private closeConnectionAfterProbe;
53
54
  private resetConnectionAfterProbe;
54
55
  private withProtocolReadTimeout;
55
56
  private probeProtocolV1;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAAS,EAAE,EAQhB,0BAA0B,EAE3B,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,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,eAAgB,SAAQ,0BAA0B,CAAC,MAAM,CAAC;IAC7E,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;IAEpE,OAAO,CAAC,sBAAsB,CAAqB;IAGnD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,eAAe,CAA2C;IAElE,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;;IAa3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAsB7B,kBAAkB;IAmBlB,SAAS;IAQT,mBAAmB;IAiCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA2BjC,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;YAgCpC,eAAe;YAkBf,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,cAAc;YAWd,yBAAyB;YAuBzB,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;IAYtB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAQ1B,SAAS,CAAC,uBAAuB,IAAI,iBAAiB;IAUtD,SAAS,CAAC,sBAAsB;cAIhB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC;cAIA,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,UAAU,CAAC;cASN,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1F,SAAS,CAAC,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKrE,SAAS,CAAC,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAWjF,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.0-alpha.33",
3
+ "version": "1.2.0-alpha.35",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -21,13 +21,13 @@
21
21
  "lint:fix": "eslint . --fix"
22
22
  },
23
23
  "dependencies": {
24
- "@onekeyfe/hd-shared": "1.2.0-alpha.33",
25
- "@onekeyfe/hd-transport": "1.2.0-alpha.33"
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.35",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.35"
26
26
  },
27
27
  "devDependencies": {
28
- "@onekeyfe/hd-transport-electron": "1.2.0-alpha.33",
28
+ "@onekeyfe/hd-transport-electron": "1.2.0-alpha.35",
29
29
  "@types/w3c-web-usb": "^1.0.6",
30
30
  "@types/web-bluetooth": "^0.0.17"
31
31
  },
32
- "gitHead": "965e837c93fc67225ccfac28d22c3403ff96ad25"
32
+ "gitHead": "638bd33030686c4a3321e4feb4924c3f9f5c81a8"
33
33
  }
@@ -42,6 +42,7 @@ export type BleAcquireInput = {
42
42
  uuid: string;
43
43
  forceCleanRunPromise?: boolean;
44
44
  expectedProtocol?: ProtocolType;
45
+ protocolHint?: ProtocolType;
45
46
  };
46
47
 
47
48
  interface PacketProcessResult {
@@ -76,7 +77,7 @@ const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
76
77
  * Desktop Electron BLE transport with automatic Protocol V1/V2 detection.
77
78
  *
78
79
  * Protocol V1 devices continue using chunked packets. Protocol V2 is detected
79
- * after a Protocol V1 Initialize timeout by probing Protocol V2 Ping.
80
+ * after a Protocol V1 GetFeatures timeout by probing Protocol V2 Ping.
80
81
  */
81
82
  export default class ElectronBleTransport {
82
83
  private _messages: ReturnType<typeof transport.parseConfigure> | undefined;
@@ -91,6 +92,8 @@ export default class ElectronBleTransport {
91
92
 
92
93
  runPromise: Deferred<Uint8Array | string> | null = null;
93
94
 
95
+ private runPromiseDeviceId: string | null = null;
96
+
94
97
  Log?: any;
95
98
 
96
99
  emitter?: EventEmitter;
@@ -181,6 +184,11 @@ export default class ElectronBleTransport {
181
184
  this.v2Assemblers.delete(deviceId);
182
185
  this.resetProtocolV2Frames(deviceId);
183
186
  this.notificationTokens.delete(deviceId);
187
+ if (this.runPromise && this.runPromiseDeviceId === deviceId) {
188
+ this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleDeviceDisconnected));
189
+ this.runPromise = null;
190
+ this.runPromiseDeviceId = null;
191
+ }
184
192
 
185
193
  const notifyCleanup = this.notificationCleanups.get(deviceId);
186
194
  if (notifyCleanup) {
@@ -269,8 +277,8 @@ export default class ElectronBleTransport {
269
277
  if (forceCleanRunPromise && this.runPromise) {
270
278
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
271
279
  this.runPromise.reject(error);
272
- this.rejectAllProtocolV2Frames(error);
273
280
  this.runPromise = null;
281
+ this.runPromiseDeviceId = null;
274
282
  }
275
283
 
276
284
  try {
@@ -284,7 +292,9 @@ export default class ElectronBleTransport {
284
292
  }
285
293
  const protocolHint = expectedProtocol
286
294
  ? undefined
287
- : this.deviceProtocolHints.get(uuid) ?? inferProtocolHintFromDeviceName(device.name);
295
+ : input.protocolHint ??
296
+ this.deviceProtocolHints.get(uuid) ??
297
+ inferProtocolHintFromDeviceName(device.name);
288
298
  if (protocolHint) {
289
299
  this.deviceProtocolHints.set(uuid, protocolHint);
290
300
  }
@@ -304,6 +314,8 @@ export default class ElectronBleTransport {
304
314
  const cleanup = this.createNotificationSubscription(uuid);
305
315
  this.notificationCleanups.set(uuid, cleanup);
306
316
 
317
+ const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
318
+
307
319
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
308
320
  (disconnectedDevice: any) => {
309
321
  if (disconnectedDevice.id === uuid) {
@@ -318,8 +330,6 @@ export default class ElectronBleTransport {
318
330
  );
319
331
  this.disconnectCleanups.set(uuid, disconnectCleanup);
320
332
 
321
- const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
322
-
323
333
  return {
324
334
  ...toBleDescriptor({ id: device.id, name: device.name }, protocolType),
325
335
  uuid,
@@ -374,7 +384,7 @@ export default class ElectronBleTransport {
374
384
  private createProtocolDetectionError() {
375
385
  return ERRORS.TypedError(
376
386
  HardwareErrorCode.BleTimeoutError,
377
- 'Unable to detect BLE protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping'
387
+ 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping'
378
388
  );
379
389
  }
380
390
 
@@ -455,10 +465,11 @@ export default class ElectronBleTransport {
455
465
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
456
466
  this.v2Assemblers.get(uuid)?.reset();
457
467
  this.resetProtocolV2Frames(uuid);
458
- if (this.runPromise) {
468
+ if (this.runPromise && this.runPromiseDeviceId === uuid) {
459
469
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
460
470
  this.runPromise.reject(error);
461
471
  this.runPromise = null;
472
+ this.runPromiseDeviceId = null;
462
473
  }
463
474
 
464
475
  const notifyCleanup = this.notificationCleanups.get(uuid);
@@ -474,9 +485,17 @@ export default class ElectronBleTransport {
474
485
  this.Log?.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
475
486
  }
476
487
  try {
488
+ await window.desktopApi?.nobleBle?.disconnect(uuid);
489
+ } catch (error) {
490
+ this.Log?.debug(`[Electron BLE] disconnect after Protocol ${protocol} probe failed:`, error);
491
+ }
492
+ this.connectedDevices.delete(uuid);
493
+ try {
494
+ await window.desktopApi?.nobleBle?.connect(uuid);
495
+ this.connectedDevices.add(uuid);
477
496
  await window.desktopApi?.nobleBle?.subscribe(uuid);
478
497
  } catch (error) {
479
- this.Log?.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
498
+ this.Log?.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
480
499
  throw error;
481
500
  }
482
501
 
@@ -491,11 +510,13 @@ export default class ElectronBleTransport {
491
510
 
492
511
  try {
493
512
  this.deviceProtocol.set(uuid, 'V1');
494
- await this.callProtocolV1(uuid, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
513
+ // GetFeatures identifies Protocol V1 without resetting an existing wallet
514
+ // session before Core has a chance to restore a hidden wallet.
515
+ await this.callProtocolV1(uuid, 'GetFeatures', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT_MS });
495
516
  return true;
496
517
  } catch (error) {
497
518
  this.clearProbeProtocol(uuid, 'V1');
498
- this.Log?.debug('[Electron BLE] Protocol V1 Initialize probe failed:', error);
519
+ this.Log?.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
499
520
  return false;
500
521
  }
501
522
  }
@@ -557,7 +578,7 @@ export default class ElectronBleTransport {
557
578
  const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
558
579
  if (this.deviceProtocol.get(deviceId) === 'V2') {
559
580
  this.rejectProtocolV2Frames(deviceId, error);
560
- } else if (this.runPromise) {
581
+ } else if (this.runPromise && this.runPromiseDeviceId === deviceId) {
561
582
  this.runPromise.reject(error);
562
583
  }
563
584
  return;
@@ -612,17 +633,8 @@ export default class ElectronBleTransport {
612
633
  this.getProtocolV2FrameQueue(uuid).push(frame);
613
634
  }
614
635
 
615
- private rejectAllProtocolV2Frames(error: Error) {
616
- this.v2FrameQueues.clear();
617
- for (const framePromise of this.v2FramePromises.values()) {
618
- framePromise.reject(error);
619
- }
620
- this.v2FramePromises.clear();
621
- }
622
-
623
636
  private resetProtocolV2Frames(uuid: string) {
624
- this.v2FrameQueues.delete(uuid);
625
- this.v2FramePromises.delete(uuid);
637
+ this.rejectProtocolV2Frames(uuid, new Error(`Protocol V2 frame state reset for ${uuid}`));
626
638
  }
627
639
 
628
640
  private rejectProtocolV2Frames(uuid: string, error: Error) {
@@ -656,13 +668,18 @@ export default class ElectronBleTransport {
656
668
 
657
669
  if (result.error) {
658
670
  this.Log?.error('[Electron BLE] Protocol V1 packet processing error:', result.error);
659
- if (this.runPromise) {
671
+ if (this.runPromise && this.runPromiseDeviceId === deviceId) {
660
672
  this.runPromise.reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError));
661
673
  }
662
674
  return;
663
675
  }
664
676
 
665
- if (result.isComplete && result.completePacket && this.runPromise) {
677
+ if (
678
+ result.isComplete &&
679
+ result.completePacket &&
680
+ this.runPromise &&
681
+ this.runPromiseDeviceId === deviceId
682
+ ) {
666
683
  this.runPromise.resolve(result.completePacket);
667
684
  }
668
685
  }
@@ -689,7 +706,7 @@ export default class ElectronBleTransport {
689
706
  );
690
707
  }
691
708
  if (!shouldSuppressHighVolumeCallLog(name)) {
692
- this.Log?.debug('transport call', createTransportCallLog(name, protocol));
709
+ this.Log?.debug('transport call', createTransportCallLog(name, protocol, data));
693
710
  }
694
711
 
695
712
  if (protocol === 'V2') {
@@ -716,6 +733,7 @@ export default class ElectronBleTransport {
716
733
  const runPromise = createDeferred<Uint8Array | string>();
717
734
  runPromise.promise.catch(() => undefined);
718
735
  this.runPromise = runPromise;
736
+ this.runPromiseDeviceId = uuid;
719
737
  const messages = this._messages;
720
738
  const buffers = ProtocolV1.encodeTransportPackets(messages, name, data);
721
739
  let timeout: ReturnType<typeof setTimeout> | undefined;
@@ -760,11 +778,24 @@ export default class ElectronBleTransport {
760
778
  return check.call(jsonData);
761
779
  } catch (e) {
762
780
  this.Log?.error('[Electron BLE] Protocol V1 call error:', e);
781
+ const isProbeTimeout =
782
+ name === 'GetFeatures' && options?.timeoutMs === PROTOCOL_PROBE_TIMEOUT_MS;
783
+ if ((e as { errorCode?: unknown })?.errorCode === HardwareErrorCode.BleTimeoutError) {
784
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
785
+ const notifyCleanup = this.notificationCleanups.get(uuid);
786
+ notifyCleanup?.();
787
+ this.notificationCleanups.delete(uuid);
788
+ this.notificationTokens.delete(uuid);
789
+ if (!isProbeTimeout) {
790
+ await this.releaseNative(uuid);
791
+ }
792
+ }
763
793
  throw e;
764
794
  } finally {
765
795
  if (timeout) clearTimeout(timeout);
766
796
  if (this.runPromise === runPromise) {
767
797
  this.runPromise = null;
798
+ this.runPromiseDeviceId = null;
768
799
  }
769
800
  }
770
801
  }
@@ -1,11 +1 @@
1
- import type { ProtocolType } from '@onekeyfe/hd-transport';
2
-
3
- const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
4
-
5
- export function shouldSuppressHighVolumeCallLog(name: string) {
6
- return HIGH_VOLUME_CALLS.has(name);
7
- }
8
-
9
- export function createTransportCallLog(name: string, protocol: ProtocolType) {
10
- return { name, protocol };
11
- }
1
+ export { createTransportCallLog, shouldSuppressHighVolumeCallLog } from '@onekeyfe/hd-transport';
package/src/webusb.ts CHANGED
@@ -41,7 +41,11 @@ const REPORT_ID = PROTOCOL_V1_REPORT_ID;
41
41
  const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
42
42
  const PACKET_IO_MAX_RETRIES = 3;
43
43
  const PACKET_IO_RETRY_DELAY = 300;
44
- const PROTOCOL_PROBE_TIMEOUT = 1000;
44
+ // Legacy devices can take longer to answer Initialize after a WebUSB reset.
45
+ // Keep this aligned with Node USB so a slow Protocol V1 response is not
46
+ // misreported as a protocol mismatch during acquire.
47
+ const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
48
+ const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
45
49
  const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
46
50
  function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
47
51
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
@@ -233,7 +237,9 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
233
237
  .productName;
234
238
  const protocolHint = input.expectedProtocol
235
239
  ? undefined
236
- : this.deviceProtocolHints.get(input.path) ?? inferProtocolHintFromDeviceName(deviceName);
240
+ : input.protocolHint ??
241
+ this.deviceProtocolHints.get(input.path) ??
242
+ inferProtocolHintFromDeviceName(deviceName);
237
243
  if (protocolHint) {
238
244
  this.deviceProtocolHints.set(input.path, protocolHint);
239
245
  }
@@ -241,6 +247,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
241
247
  return await Promise.resolve(input.path);
242
248
  } catch (e) {
243
249
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
250
+ await this.closeOpenDevice(input.path);
244
251
  throw e;
245
252
  }
246
253
  }
@@ -265,10 +272,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
265
272
  }
266
273
 
267
274
  private createProtocolDetectionError() {
268
- return ERRORS.TypedError(
269
- HardwareErrorCode.RuntimeError,
270
- 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping'
271
- );
275
+ return ERRORS.TypedError(HardwareErrorCode.DeviceNotFound);
272
276
  }
273
277
 
274
278
  private async detectProtocol(
@@ -281,7 +285,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
281
285
  this.deviceProtocol.set(path, 'V1');
282
286
  return 'V1';
283
287
  }
284
- await this.resetConnectionAfterProbe(path);
288
+ await this.closeConnectionAfterProbe(path);
285
289
  throw this.createProtocolMismatchError(expectedProtocol);
286
290
  }
287
291
 
@@ -291,13 +295,15 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
291
295
  this.deviceProtocol.set(path, 'V2');
292
296
  return 'V2';
293
297
  }
294
- await this.resetConnectionAfterProbe(path);
295
298
  if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
299
+ await this.resetConnectionAfterProbe(path);
296
300
  this.Log?.debug(
297
301
  `[WebUsbTransport] Protocol V2 probe timed out, retrying ${
298
302
  attempt + 1
299
303
  }/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`
300
304
  );
305
+ } else {
306
+ await this.closeConnectionAfterProbe(path);
301
307
  }
302
308
  }
303
309
  this.deviceProtocol.delete(path);
@@ -312,17 +318,21 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
312
318
  const probeOrder: ProtocolType[] =
313
319
  protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
314
320
 
315
- for (const protocol of probeOrder) {
321
+ for (const [index, protocol] of probeOrder.entries()) {
316
322
  const detected =
317
323
  protocol === 'V1' ? await this.probeProtocolV1(path) : await this.probeProtocolV2(path);
318
324
  if (detected) {
319
325
  this.deviceProtocol.set(path, protocol);
320
326
  return protocol;
321
327
  }
322
- // A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
323
- // reopening the device guarantees the next protocol probe cannot consume a
324
- // late response from the previous protocol generation.
325
- await this.resetConnectionAfterProbe(path);
328
+ if (index < probeOrder.length - 1) {
329
+ // A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
330
+ // reopening the device guarantees the next protocol probe cannot consume a
331
+ // late response from the previous protocol generation.
332
+ await this.resetConnectionAfterProbe(path);
333
+ } else {
334
+ await this.closeConnectionAfterProbe(path);
335
+ }
326
336
  }
327
337
 
328
338
  this.deviceProtocol.delete(path);
@@ -645,25 +655,13 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
645
655
  return this.getTransferInData(result);
646
656
  }
647
657
 
648
- private async resetConnectionAfterProbe(path: string) {
658
+ private async closeConnectionAfterProbe(path: string) {
649
659
  await this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
660
+ await this.closeOpenDevice(path);
661
+ }
650
662
 
651
- try {
652
- const device = await this.findDevice(path);
653
- if (device.opened) {
654
- const endpoints = this.deviceEndpoints.get(path);
655
- const ifaceNum = endpoints?.interfaceNumber ?? this.interfaceId;
656
- try {
657
- await device.releaseInterface(ifaceNum);
658
- } catch (error) {
659
- this.Log.debug('[WebUsbTransport] releaseInterface after protocol probe error:', error);
660
- }
661
- await device.close();
662
- }
663
- } catch (error) {
664
- this.Log.debug('[WebUsbTransport] close after protocol probe error:', error);
665
- }
666
-
663
+ private async resetConnectionAfterProbe(path: string) {
664
+ await this.closeConnectionAfterProbe(path);
667
665
  await this.getConnectedDevices();
668
666
  await this.connect(path, false);
669
667
  }
@@ -716,7 +714,14 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
716
714
  }
717
715
 
718
716
  try {
719
- await this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
717
+ await this.callProtocolV1(
718
+ path,
719
+ 'Initialize',
720
+ {},
721
+ {
722
+ timeoutMs: PROTOCOL_V1_PROBE_TIMEOUT,
723
+ }
724
+ );
720
725
  return true;
721
726
  } catch (_error) {
722
727
  return false;
@@ -730,7 +735,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
730
735
 
731
736
  return probeProtocolV2Helper({
732
737
  call: (name, data, options) => this.callProtocolV2(path, name, data, options),
733
- timeoutMs: PROTOCOL_PROBE_TIMEOUT,
738
+ timeoutMs: PROTOCOL_V2_PROBE_TIMEOUT,
734
739
  logger: this.Log,
735
740
  logPrefix: 'ProtocolV2 WebUSB',
736
741
  });
@@ -763,7 +768,7 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
763
768
  }
764
769
 
765
770
  if (!shouldSuppressHighVolumeCallLog(name)) {
766
- this.Log.debug('transport call', createTransportCallLog(name, protocol));
771
+ this.Log.debug('transport call', createTransportCallLog(name, protocol, data));
767
772
  }
768
773
 
769
774
  if (protocol === 'V2') {