@onekeyfe/hd-transport-web-device 1.2.0-alpha.22 → 1.2.0-alpha.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
2
2
  import { HardwareErrorCode } from '@onekeyfe/hd-shared';
3
+ import EventEmitter from 'events';
3
4
 
4
5
  import ElectronBleTransport from '../src/electron-ble-transport';
5
6
 
@@ -111,7 +112,10 @@ const createNobleBle = (device = { id: 'flaky-pro2-id', name: 'Unknown BLE Devic
111
112
  ),
112
113
  });
113
114
 
114
- const configureTransport = (nobleBle: ReturnType<typeof createNobleBle>) => {
115
+ const configureTransport = (
116
+ nobleBle: ReturnType<typeof createNobleBle>,
117
+ emitter?: EventEmitter
118
+ ) => {
115
119
  (global as any).window = {
116
120
  desktopApi: {
117
121
  nobleBle,
@@ -119,7 +123,7 @@ const configureTransport = (nobleBle: ReturnType<typeof createNobleBle>) => {
119
123
  };
120
124
 
121
125
  const transport = new ElectronBleTransport();
122
- transport.init(createLogger());
126
+ transport.init(createLogger(), emitter);
123
127
  transport.configure(protocolV1Schema);
124
128
  transport.configureProtocolV2(protocolV2Schema);
125
129
  return transport;
@@ -131,6 +135,54 @@ describe('ElectronBleTransport protocol detection', () => {
131
135
  jest.clearAllMocks();
132
136
  });
133
137
 
138
+ test('keeps raw BLE lifecycle payloads off the public device event channel', async () => {
139
+ const device = { id: 'lifecycle-pro2-id', name: 'OneKey Pro 2' };
140
+ const nobleBle = createNobleBle(device);
141
+ const emitter = new EventEmitter();
142
+ let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
143
+ let disconnectHandler: ((device: { id: string; name: string | null }) => void) | undefined;
144
+ let responseSeq = 0;
145
+
146
+ nobleBle.onNotification.mockImplementation(handler => {
147
+ notificationHandler = handler;
148
+ return jest.fn();
149
+ });
150
+ nobleBle.onDeviceDisconnected.mockImplementation(handler => {
151
+ disconnectHandler = handler;
152
+ return jest.fn();
153
+ });
154
+ nobleBle.write.mockImplementation(() => {
155
+ responseSeq += 1;
156
+ const response = ProtocolV2.encodeFrame(
157
+ schemas,
158
+ 'Success',
159
+ { message: 'ok' },
160
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
161
+ );
162
+ setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
163
+ return Promise.resolve();
164
+ });
165
+
166
+ const publicConnect = jest.fn();
167
+ const publicDisconnect = jest.fn();
168
+ const transportDisconnect = jest.fn();
169
+ emitter.on('device-connect', publicConnect);
170
+ emitter.on('device-disconnect', publicDisconnect);
171
+ emitter.on('transport-device-disconnect', transportDisconnect);
172
+ const bleTransport = configureTransport(nobleBle, emitter);
173
+
174
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
175
+ disconnectHandler?.(device);
176
+
177
+ expect(publicConnect).not.toHaveBeenCalled();
178
+ expect(publicDisconnect).not.toHaveBeenCalled();
179
+ expect(transportDisconnect).toHaveBeenCalledWith({
180
+ id: device.id,
181
+ connectId: device.id,
182
+ name: device.name,
183
+ });
184
+ });
185
+
134
186
  test('detects Protocol V2 after Protocol V1 probe timeout', async () => {
135
187
  const device = { id: 'unknown-pro2-id', name: 'Unknown BLE Device' };
136
188
  const nobleBle = createNobleBle(device);
@@ -341,4 +393,52 @@ describe('ElectronBleTransport protocol detection', () => {
341
393
  await transport.release(device.id);
342
394
  }
343
395
  });
396
+
397
+ test('preserves the active Protocol V2 link when the same schema is configured again', async () => {
398
+ const device = { id: 'stable-schema-pro2-id', name: 'OneKey Pro 2' };
399
+ const nobleBle = createNobleBle(device);
400
+ let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
401
+ nobleBle.onNotification.mockImplementation(handler => {
402
+ notificationHandler = handler;
403
+ return jest.fn();
404
+ });
405
+ let responseSeq = 0;
406
+ nobleBle.write.mockImplementation(() => {
407
+ responseSeq += 1;
408
+ const response = ProtocolV2.encodeFrame(
409
+ schemas,
410
+ 'Success',
411
+ { message: 'ok' },
412
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
413
+ );
414
+ setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
415
+ return Promise.resolve();
416
+ });
417
+ const bleTransport = configureTransport(nobleBle);
418
+
419
+ try {
420
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
421
+ const invalidateAllLinks = jest.spyOn(
422
+ (bleTransport as any).protocolV2Links,
423
+ 'invalidateAllLinks'
424
+ );
425
+ bleTransport.configureProtocolV2(protocolV2Schema);
426
+ await new Promise<void>(resolve => {
427
+ setTimeout(resolve, 0);
428
+ });
429
+ expect(invalidateAllLinks).not.toHaveBeenCalled();
430
+ await expect(
431
+ bleTransport.call(device.id, 'Ping', { message: 'same-schema' })
432
+ ).resolves.toEqual({
433
+ type: 'Success',
434
+ message: { message: 'ok' },
435
+ });
436
+ const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
437
+ Number.parseInt(hex.slice(12, 14), 16)
438
+ );
439
+ expect(sentSeqs).toEqual([1, 2]);
440
+ } finally {
441
+ await bleTransport.release(device.id);
442
+ }
443
+ });
344
444
  });
@@ -43,6 +43,38 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
43
43
  expect(webusb.deviceProtocol.get(path)).toBe('V2');
44
44
  });
45
45
 
46
+ test('retries an expected Protocol V2 probe once after resetting the connection', async () => {
47
+ const webusb = new WebUsbTransport() as any;
48
+ const path = 'pro2-webusb';
49
+ webusb.probeProtocolV1 = jest.fn();
50
+ webusb.probeProtocolV2 = jest.fn().mockResolvedValueOnce(false).mockResolvedValueOnce(true);
51
+ webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
52
+
53
+ await expect(webusb.detectProtocol(path, 'V2')).resolves.toBe('V2');
54
+
55
+ expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
56
+ expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
57
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(1);
58
+ expect(webusb.deviceProtocol.get(path)).toBe('V2');
59
+ });
60
+
61
+ test('reports a Protocol V2 probe timeout only after the bounded retry is exhausted', async () => {
62
+ const webusb = new WebUsbTransport() as any;
63
+ const path = 'pro2-webusb';
64
+ webusb.probeProtocolV1 = jest.fn();
65
+ webusb.probeProtocolV2 = jest.fn().mockResolvedValue(false);
66
+ webusb.resetConnectionAfterProbe = jest.fn().mockResolvedValue(undefined);
67
+
68
+ await expect(webusb.detectProtocol(path, 'V2')).rejects.toThrow(
69
+ 'Protocol V2 probe timeout after 2 attempts'
70
+ );
71
+
72
+ expect(webusb.probeProtocolV2).toHaveBeenCalledTimes(2);
73
+ expect(webusb.probeProtocolV1).not.toHaveBeenCalled();
74
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledTimes(2);
75
+ expect(webusb.deviceProtocol.has(path)).toBe(false);
76
+ });
77
+
46
78
  test('invalidates and resets the cached connection before another call can start', async () => {
47
79
  const webusb = new WebUsbTransport() as any;
48
80
  const path = 'pro2-webusb';
@@ -16,6 +16,7 @@ export type BleAcquireInput = {
16
16
  export default class ElectronBleTransport {
17
17
  private _messages;
18
18
  private _messagesV2;
19
+ private protocolV2SchemaConfiguration;
19
20
  name: string;
20
21
  configured: boolean;
21
22
  runPromise: Deferred<Uint8Array | string> | null;
@@ -1 +1 @@
1
- {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAoCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAO7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAuEd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAqBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAoCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAgB7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAoF9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAuEd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/dist/index.d.ts CHANGED
@@ -88,6 +88,7 @@ declare class WebUsbTransport {
88
88
  * fall back to a Protocol V2 Ping probe.
89
89
  */
90
90
  private createProtocolMismatchError;
91
+ private createProtocolProbeTimeoutError;
91
92
  private createProtocolDetectionError;
92
93
  private detectProtocol;
93
94
  /**
@@ -170,6 +171,7 @@ type BleAcquireInput = {
170
171
  declare class ElectronBleTransport {
171
172
  private _messages;
172
173
  private _messagesV2;
174
+ private protocolV2SchemaConfiguration;
173
175
  name: string;
174
176
  configured: boolean;
175
177
  runPromise: Deferred<Uint8Array | string> | null;
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
60
60
  const PACKET_IO_MAX_RETRIES = 3;
61
61
  const PACKET_IO_RETRY_DELAY = 300;
62
62
  const PROTOCOL_PROBE_TIMEOUT = 1000;
63
+ const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
63
64
  function inferProtocolHintFromDeviceName$1(name) {
64
65
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
65
66
  }
@@ -183,10 +184,14 @@ class WebUsbTransport {
183
184
  createProtocolMismatchError(expected) {
184
185
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
185
186
  }
187
+ createProtocolProbeTimeoutError(expected, attempts) {
188
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
189
+ }
186
190
  createProtocolDetectionError() {
187
191
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
188
192
  }
189
193
  detectProtocol(path, expectedProtocol, protocolHint) {
194
+ var _a;
190
195
  return __awaiter(this, void 0, void 0, function* () {
191
196
  if (expectedProtocol === 'V1') {
192
197
  if (yield this.probeProtocolV1(path)) {
@@ -197,11 +202,18 @@ class WebUsbTransport {
197
202
  throw this.createProtocolMismatchError(expectedProtocol);
198
203
  }
199
204
  if (expectedProtocol === 'V2') {
200
- if (yield this.probeProtocolV2(path)) {
201
- this.deviceProtocol.set(path, 'V2');
202
- return 'V2';
205
+ for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
206
+ if (yield this.probeProtocolV2(path)) {
207
+ this.deviceProtocol.set(path, 'V2');
208
+ return 'V2';
209
+ }
210
+ yield this.resetConnectionAfterProbe(path);
211
+ if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
212
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 probe timed out, retrying ${attempt + 1}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`);
213
+ }
203
214
  }
204
- throw this.createProtocolMismatchError(expectedProtocol);
215
+ this.deviceProtocol.delete(path);
216
+ throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
205
217
  }
206
218
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
207
219
  for (const protocol of probeOrder) {
@@ -210,9 +222,7 @@ class WebUsbTransport {
210
222
  this.deviceProtocol.set(path, protocol);
211
223
  return protocol;
212
224
  }
213
- if (protocol === 'V1') {
214
- yield this.resetConnectionAfterProbe(path);
215
- }
225
+ yield this.resetConnectionAfterProbe(path);
216
226
  }
217
227
  this.deviceProtocol.delete(path);
218
228
  throw this.createProtocolDetectionError();
@@ -574,7 +584,7 @@ class WebUsbTransport {
574
584
  return false;
575
585
  }
576
586
  return transport.probeProtocolV2({
577
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
587
+ call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
578
588
  timeoutMs: PROTOCOL_PROBE_TIMEOUT,
579
589
  logger: this.Log,
580
590
  logPrefix: 'ProtocolV2 WebUSB',
@@ -624,7 +634,7 @@ class WebUsbTransport {
624
634
  return check$1.call(jsonData);
625
635
  });
626
636
  }
627
- callProtocolV2(path, name, data, options) {
637
+ callProtocolV2(path, name, data, options, resetOnError = true) {
628
638
  return __awaiter(this, void 0, void 0, function* () {
629
639
  const protocolV1Messages = this.messages;
630
640
  if (!this.messagesV2) {
@@ -659,7 +669,7 @@ class WebUsbTransport {
659
669
  return yield session.call(name, data, options);
660
670
  }
661
671
  catch (error) {
662
- if (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
672
+ if (resetOnError && (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
663
673
  try {
664
674
  yield this.resetConnectionAfterProbe(path);
665
675
  }
@@ -863,10 +873,18 @@ class ElectronBleTransport {
863
873
  this.configured = true;
864
874
  }
865
875
  configureProtocolV2(signedData) {
876
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
877
+ if (this.protocolV2SchemaConfiguration === configuration) {
878
+ return;
879
+ }
880
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
866
881
  this._messagesV2 = parseConfigure(signedData);
867
- this.protocolV2Links
868
- .invalidateAllLinks('Protocol V2 schema reconfigured')
869
- .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
882
+ this.protocolV2SchemaConfiguration = configuration;
883
+ if (isReconfiguration) {
884
+ this.protocolV2Links
885
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
886
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
887
+ }
870
888
  }
871
889
  listen() {
872
890
  return __awaiter(this, void 0, void 0, function* () {
@@ -898,7 +916,7 @@ class ElectronBleTransport {
898
916
  });
899
917
  }
900
918
  acquire(input) {
901
- var _a, _b, _c, _d, _e, _f;
919
+ var _a, _b, _c, _d, _e;
902
920
  return __awaiter(this, void 0, void 0, function* () {
903
921
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
904
922
  if (!uuid) {
@@ -943,7 +961,7 @@ class ElectronBleTransport {
943
961
  var _a;
944
962
  if (disconnectedDevice.id === uuid) {
945
963
  this.cleanupDeviceState(uuid);
946
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
964
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
947
965
  name: disconnectedDevice.name,
948
966
  id: disconnectedDevice.id,
949
967
  connectId: disconnectedDevice.id,
@@ -952,23 +970,18 @@ class ElectronBleTransport {
952
970
  });
953
971
  this.disconnectCleanups.set(uuid, disconnectCleanup);
954
972
  const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
955
- (_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
956
- name: device.name,
957
- id: device.id,
958
- connectId: device.id,
959
- });
960
973
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
961
974
  }
962
975
  catch (error) {
963
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] acquire failed:', error);
976
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] acquire failed:', error);
964
977
  try {
965
- if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
978
+ if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
966
979
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
967
980
  yield window.desktopApi.nobleBle.disconnect(uuid);
968
981
  }
969
982
  }
970
983
  catch (cleanupError) {
971
- (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
984
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
972
985
  }
973
986
  this.cleanupDeviceState(uuid);
974
987
  throw error;
package/dist/webusb.d.ts CHANGED
@@ -35,6 +35,7 @@ export default class WebUsbTransport {
35
35
  getConnectedDevices(): Promise<DeviceInfo[]>;
36
36
  acquire(input: AcquireInput): Promise<string | undefined>;
37
37
  private createProtocolMismatchError;
38
+ private createProtocolProbeTimeoutError;
38
39
  private createProtocolDetectionError;
39
40
  private detectProtocol;
40
41
  findDevice(path: string): Promise<USBDevice>;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAaN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IAiDtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAwBzB,uBAAuB;YA0CvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YA0Dd,sBAAsB;IAqC9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAkB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAaN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAsBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,+BAA+B;IAOvC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IA6DtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAwBzB,uBAAuB;YA0CvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YA2Dd,sBAAsB;IAqC9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAkB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.0-alpha.22",
3
+ "version": "1.2.0-alpha.24",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -20,13 +20,13 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.2.0-alpha.22",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.22"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.24",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.24"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.2.0-alpha.22",
27
+ "@onekeyfe/hd-transport-electron": "1.2.0-alpha.24",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "ce94a5ebbe141b0c15db7c68d08085ca1ddb0166"
31
+ "gitHead": "43663337d94350430cc507c74d6eec5a076bd4e9"
32
32
  }
@@ -3,6 +3,7 @@ import transport, {
3
3
  PROTOCOL_V2_CHANNEL_BLE_UART,
4
4
  ProtocolV2FrameAssembler,
5
5
  ProtocolV2LinkManager,
6
+ TRANSPORT_EVENT,
6
7
  bytesToHex,
7
8
  hexToBytes,
8
9
  probeProtocolV2 as probeProtocolV2Helper,
@@ -76,6 +77,8 @@ export default class ElectronBleTransport {
76
77
 
77
78
  private _messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
78
79
 
80
+ private protocolV2SchemaConfiguration: string | undefined;
81
+
79
82
  name = 'ElectronBleTransport';
80
83
 
81
84
  configured = false;
@@ -206,10 +209,19 @@ export default class ElectronBleTransport {
206
209
  }
207
210
 
208
211
  configureProtocolV2(signedData: any) {
212
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
213
+ if (this.protocolV2SchemaConfiguration === configuration) {
214
+ return;
215
+ }
216
+
217
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
209
218
  this._messagesV2 = parseConfigure(signedData);
210
- this.protocolV2Links
211
- .invalidateAllLinks('Protocol V2 schema reconfigured')
212
- .catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
219
+ this.protocolV2SchemaConfiguration = configuration;
220
+ if (isReconfiguration) {
221
+ this.protocolV2Links
222
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
223
+ .catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
224
+ }
213
225
  }
214
226
 
215
227
  async listen() {
@@ -290,7 +302,7 @@ export default class ElectronBleTransport {
290
302
  (disconnectedDevice: any) => {
291
303
  if (disconnectedDevice.id === uuid) {
292
304
  this.cleanupDeviceState(uuid);
293
- this.emitter?.emit('device-disconnect', {
305
+ this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
294
306
  name: disconnectedDevice.name,
295
307
  id: disconnectedDevice.id,
296
308
  connectId: disconnectedDevice.id,
@@ -302,12 +314,6 @@ export default class ElectronBleTransport {
302
314
 
303
315
  const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
304
316
 
305
- this.emitter?.emit('device-connect', {
306
- name: device.name,
307
- id: device.id,
308
- connectId: device.id,
309
- });
310
-
311
317
  return {
312
318
  ...toBleDescriptor({ id: device.id, name: device.name }, protocolType),
313
319
  uuid,
package/src/webusb.ts CHANGED
@@ -43,6 +43,7 @@ const HEADER_LENGTH = PROTOCOL_V1_MESSAGE_HEADER_SIZE;
43
43
  const PACKET_IO_MAX_RETRIES = 3;
44
44
  const PACKET_IO_RETRY_DELAY = 300;
45
45
  const PROTOCOL_PROBE_TIMEOUT = 1000;
46
+ const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
46
47
  function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
47
48
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
48
49
  }
@@ -269,6 +270,13 @@ export default class WebUsbTransport {
269
270
  );
270
271
  }
271
272
 
273
+ private createProtocolProbeTimeoutError(expected: ProtocolType, attempts: number) {
274
+ return ERRORS.TypedError(
275
+ HardwareErrorCode.RuntimeError,
276
+ `Protocol ${expected} probe timeout after ${attempts} attempts`
277
+ );
278
+ }
279
+
272
280
  private createProtocolDetectionError() {
273
281
  return ERRORS.TypedError(
274
282
  HardwareErrorCode.RuntimeError,
@@ -291,11 +299,25 @@ export default class WebUsbTransport {
291
299
  }
292
300
 
293
301
  if (expectedProtocol === 'V2') {
294
- if (await this.probeProtocolV2(path)) {
295
- this.deviceProtocol.set(path, 'V2');
296
- return 'V2';
302
+ for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
303
+ if (await this.probeProtocolV2(path)) {
304
+ this.deviceProtocol.set(path, 'V2');
305
+ return 'V2';
306
+ }
307
+ await this.resetConnectionAfterProbe(path);
308
+ if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
309
+ this.Log?.debug(
310
+ `[WebUsbTransport] Protocol V2 probe timed out, retrying ${
311
+ attempt + 1
312
+ }/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`
313
+ );
314
+ }
297
315
  }
298
- throw this.createProtocolMismatchError(expectedProtocol);
316
+ this.deviceProtocol.delete(path);
317
+ throw this.createProtocolProbeTimeoutError(
318
+ expectedProtocol,
319
+ EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS
320
+ );
299
321
  }
300
322
 
301
323
  // Protocol must be actively probed after connection. Name, PID, and descriptors only
@@ -310,12 +332,10 @@ export default class WebUsbTransport {
310
332
  this.deviceProtocol.set(path, protocol);
311
333
  return protocol;
312
334
  }
313
- if (protocol === 'V1') {
314
- // A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
315
- // reopening the device guarantees the next protocol probe cannot consume a
316
- // late V1 response. V2 timeout recovery is owned by callProtocolV2.
317
- await this.resetConnectionAfterProbe(path);
318
- }
335
+ // A timed-out WebUSB transferIn cannot be cancelled in place. Closing and
336
+ // reopening the device guarantees the next protocol probe cannot consume a
337
+ // late response from the previous protocol generation.
338
+ await this.resetConnectionAfterProbe(path);
319
339
  }
320
340
 
321
341
  this.deviceProtocol.delete(path);
@@ -716,7 +736,7 @@ export default class WebUsbTransport {
716
736
  }
717
737
 
718
738
  return probeProtocolV2Helper({
719
- call: (name, data, options) => this.callProtocolV2(path, name, data, options),
739
+ call: (name, data, options) => this.callProtocolV2(path, name, data, options, false),
720
740
  timeoutMs: PROTOCOL_PROBE_TIMEOUT,
721
741
  logger: this.Log,
722
742
  logPrefix: 'ProtocolV2 WebUSB',
@@ -798,7 +818,8 @@ export default class WebUsbTransport {
798
818
  path: string,
799
819
  name: string,
800
820
  data: Record<string, unknown>,
801
- options?: TransportCallOptions
821
+ options?: TransportCallOptions,
822
+ resetOnError = true
802
823
  ) {
803
824
  const protocolV1Messages = this.messages;
804
825
  if (!this.messagesV2) {
@@ -841,7 +862,7 @@ export default class WebUsbTransport {
841
862
  try {
842
863
  return await session.call(name, data, options);
843
864
  } catch (error) {
844
- if (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
865
+ if (resetOnError && (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error))) {
845
866
  try {
846
867
  await this.resetConnectionAfterProbe(path);
847
868
  } catch (resetError) {