@onekeyfe/hd-transport-web-device 1.2.8-alpha.13 → 1.2.8-alpha.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/webusb.d.ts CHANGED
@@ -8,7 +8,16 @@ export interface DeviceInfo extends OneKeyDeviceInfoBase {
8
8
  device: USBDevice;
9
9
  protocolType?: ProtocolType;
10
10
  }
11
+ interface WebUsbConnectOptions {
12
+ expectedProtocol?: ProtocolType;
13
+ forceProtocolDetection?: boolean;
14
+ }
11
15
  export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
16
+ private readonly identityProbe;
17
+ private readonly transportInstanceId;
18
+ private handleOperationSequence;
19
+ private deviceLogSequence;
20
+ private deviceLogIds;
12
21
  messages: ReturnType<typeof transport.parseConfigure> | undefined;
13
22
  messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
14
23
  private protocolV2SchemaSource;
@@ -17,6 +26,11 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
17
26
  private deviceProtocolHints;
18
27
  private staleProtocolPaths;
19
28
  private acquiredPaths;
29
+ private identityProbeDevices;
30
+ private normalDevices;
31
+ private pendingHandleOperations;
32
+ private stopPromise?;
33
+ private identityProbeCleanupPaths;
20
34
  private probedDeviceObjects;
21
35
  private deviceEndpoints;
22
36
  name: string;
@@ -29,8 +43,19 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
29
43
  configurationId: number;
30
44
  endpointId: number;
31
45
  interfaceId: number;
32
- constructor();
46
+ constructor(identityProbe?: boolean);
33
47
  init(logger: any, emitter?: EventEmitter): void;
48
+ stop(): Promise<void>;
49
+ private stopResources;
50
+ private assertRunning;
51
+ private runHandleOperation;
52
+ private getInterfaceClaimed;
53
+ private getDeviceLogId;
54
+ private getHandleLogState;
55
+ private getDiagnosticError;
56
+ private logLifecycle;
57
+ private logSkippedHandleOperation;
58
+ private runLoggedHandleOperation;
34
59
  emitDeviceDisconnect(path: string, device?: USBDevice): void;
35
60
  markProtocolStale(path: string): void;
36
61
  configure(signedData: any): void;
@@ -44,10 +69,19 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
44
69
  private createProtocolDetectionError;
45
70
  private detectProtocol;
46
71
  findDevice(path: string): Promise<USBDevice>;
47
- connect(path: string, first: boolean): Promise<undefined>;
72
+ connect(path: string, first: boolean, options?: WebUsbConnectOptions): Promise<undefined>;
73
+ private shouldUseLegacyV1Connection;
48
74
  private discoverEndpoints;
49
- connectToDevice(path: string, first: boolean): Promise<undefined>;
75
+ connectToDevice(path: string, first: boolean, options?: WebUsbConnectOptions): Promise<undefined>;
76
+ private claimDeviceInterface;
77
+ private assertHandleAvailable;
78
+ private claimNormalHandle;
79
+ private releaseNormalHandles;
80
+ private isHandleOwnershipError;
81
+ private claimIdentityProbeHandle;
82
+ private releaseIdentityProbeHandle;
50
83
  private closeOpenDevice;
84
+ private closeDeviceHandle;
51
85
  private clearEndpointHalt;
52
86
  private assertAcquired;
53
87
  post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
@@ -84,4 +118,5 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
84
118
  protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error;
85
119
  getProtocolType(path: string): ProtocolType | undefined;
86
120
  }
121
+ export {};
87
122
  //# sourceMappingURL=webusb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";;AACA,OAAO,SAAS,EAAE,EAQhB,0BAA0B,EAG3B,MAAM,wBAAwB,CAAC;AAahC,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AA0BhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AA2BD,UAAU,oBAAoB;IAC5B,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAmCD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,0BAA0B,CAAC,MAAM,CAAC;IA+EjE,OAAO,CAAC,QAAQ,CAAC,aAAa;IA9E1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmC;IAEvE,OAAO,CAAC,uBAAuB,CAAK;IAEpC,OAAO,CAAC,iBAAiB,CAAK;IAE9B,OAAO,CAAC,YAAY,CAAoC;IAExD,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;IAG9D,OAAO,CAAC,wBAAwB,CAAwC;IAExE,OAAO,CAAC,mBAAmB,CAAwC;IAMnE,OAAO,CAAC,kBAAkB,CAA0B;IAGpD,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,oBAAoB,CAAgC;IAE5D,OAAO,CAAC,aAAa,CAAgC;IAErD,OAAO,CAAC,uBAAuB,CAA+B;IAE9D,OAAO,CAAC,WAAW,CAAC,CAAgB;IAEpC,OAAO,CAAC,yBAAyB,CAAqB;IAStD,OAAO,CAAC,mBAAmB,CAAqC;IAGhE,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;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAMvB,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;gBAEE,aAAa,UAAQ;IAWlD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAexC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YA6BP,aAAa;IA+E3B,OAAO,CAAC,aAAa;YAMP,kBAAkB;IAahC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,yBAAyB;YAgBnB,wBAAwB;IAsDtC,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS;IAkBrD,iBAAiB,CAAC,IAAI,EAAE,MAAM;IAQ9B,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAsB7B,kBAAkB;IA+BlB,SAAS;IAmCT,mBAAmB;IAgEnB,OAAO,CAAC,KAAK,EAAE,YAAY;IAwIjC,OAAO,CAAC,2BAA2B;IAMnC,OAAO,CAAC,+BAA+B;IAOvC,OAAO,CAAC,4BAA4B;YAItB,cAAc;IAmEtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,oBAAyB;IAwD9E,OAAO,CAAC,2BAA2B;IASnC,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,oBAAyB;YA4HxE,oBAAoB;IAclC,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,0BAA0B;YAapB,eAAe;YAwBf,iBAAiB;YAwCjB,iBAAiB;IA+B/B,OAAO,CAAC,cAAc;IAOhB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IASvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,uBAAuB;YASjB,uBAAuB;YAWvB,kBAAkB;IAyChC,OAAO,CAAC,+BAA+B;IAIvC,OAAO,CAAC,wBAAwB;YAgBlB,yBAAyB;IA+DvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA6BpB,eAAe;YAsBf,mBAAmB;YA+CnB,cAAc;YAad,yBAAyB;YAKzB,yBAAyB;YAMzB,uBAAuB;YA0CvB,eAAe;YAqBf,eAAe;IAiBvB,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;YAkCd,cAAc;IAkBtB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IA8B1B,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.8-alpha.13",
3
+ "version": "1.2.8-alpha.130",
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.8-alpha.13",
25
- "@onekeyfe/hd-transport": "1.2.8-alpha.13"
24
+ "@onekeyfe/hd-shared": "1.2.8-alpha.130",
25
+ "@onekeyfe/hd-transport": "1.2.8-alpha.130"
26
26
  },
27
27
  "devDependencies": {
28
- "@onekeyfe/hd-transport-electron": "1.2.8-alpha.13",
28
+ "@onekeyfe/hd-transport-electron": "1.2.8-alpha.130",
29
29
  "@types/w3c-web-usb": "^1.0.6",
30
30
  "@types/web-bluetooth": "^0.0.17"
31
31
  },
32
- "gitHead": "ed22eddce37ce0562b329c42a6985b50949ca412"
32
+ "gitHead": "bacd066a15648891f9683586fcd06e063b98dfec"
33
33
  }
@@ -26,6 +26,7 @@ import { resolveBlePacketCapacity } from './ble-packet-capacity';
26
26
  import type { Deferred } from '@onekeyfe/hd-shared';
27
27
  import type { DesktopAPI, NobleBleIpcErrorResponse } from '@onekeyfe/hd-transport-electron';
28
28
  import type {
29
+ AcquireInput,
29
30
  OneKeyDeviceInfo,
30
31
  ProtocolType,
31
32
  ProtocolV2CallContext,
@@ -55,7 +56,7 @@ interface PacketProcessResult {
55
56
  }
56
57
 
57
58
  const toBleDescriptor = (
58
- device: { id: string; name: string | null },
59
+ device: { id: string; name: string | null; bleColorCode?: string },
59
60
  protocolType?: ProtocolType
60
61
  ): OneKeyDeviceInfo =>
61
62
  ({
@@ -64,6 +65,7 @@ const toBleDescriptor = (
64
65
  path: device.id,
65
66
  debug: false,
66
67
  commType: 'electron-ble',
68
+ ...(device.bleColorCode ? { bleColorCode: device.bleColorCode } : {}),
67
69
  ...(protocolType ? { protocolType } : {}),
68
70
  } as OneKeyDeviceInfo);
69
71
 
@@ -116,6 +118,10 @@ export default class ElectronBleTransport {
116
118
 
117
119
  private connectedDevices: Set<string> = new Set();
118
120
 
121
+ private retainedDevices: Set<string> = new Set();
122
+
123
+ private protocolV2CallCounts: Map<string, number> = new Map();
124
+
119
125
  /**
120
126
  * Devices whose acquire() is still in flight.
121
127
  *
@@ -233,6 +239,7 @@ export default class ElectronBleTransport {
233
239
  .invalidateLink(deviceId, 'Electron BLE device state cleaned')
234
240
  .catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
235
241
  this.connectedDevices.delete(deviceId);
242
+ this.retainedDevices.delete(deviceId);
236
243
  this.deviceProtocol.delete(deviceId);
237
244
  this.deviceMtus.delete(deviceId);
238
245
  this.devicePacketCapacities.delete(deviceId);
@@ -357,13 +364,22 @@ export default class ElectronBleTransport {
357
364
  }
358
365
 
359
366
  async acquire(input: BleAcquireInput) {
360
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
367
+ const { uuid, forceCleanRunPromise, expectedProtocol, forceProtocolDetection }: AcquireInput =
368
+ input;
361
369
 
362
370
  if (!uuid) {
363
371
  throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
364
372
  }
365
373
 
366
- if (this.connectedDevices.has(uuid)) {
374
+ const retainedToken =
375
+ this.retainedDevices.has(uuid) &&
376
+ !forceProtocolDetection &&
377
+ (!expectedProtocol || expectedProtocol === 'V2') &&
378
+ this.deviceProtocol.get(uuid) === 'V2'
379
+ ? this.notificationTokens.get(uuid)
380
+ : undefined;
381
+ this.retainedDevices.delete(uuid);
382
+ if (this.connectedDevices.has(uuid) && retainedToken === undefined) {
367
383
  await this.release(uuid);
368
384
  }
369
385
 
@@ -384,6 +400,18 @@ export default class ElectronBleTransport {
384
400
  if (!device) {
385
401
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
386
402
  }
403
+ // A disconnect during the IPC lookup invalidates the retained notification generation.
404
+ if (
405
+ retainedToken !== undefined &&
406
+ this.notificationTokens.get(uuid) === retainedToken &&
407
+ this.connectedDevices.has(uuid) &&
408
+ this.deviceProtocol.get(uuid) === 'V2'
409
+ ) {
410
+ return {
411
+ ...toBleDescriptor({ id: device.id, name: device.name }, 'V2'),
412
+ uuid,
413
+ };
414
+ }
387
415
  const protocolHint = expectedProtocol
388
416
  ? undefined
389
417
  : input.protocolHint ?? this.deviceProtocolHints.get(uuid);
@@ -451,10 +479,18 @@ export default class ElectronBleTransport {
451
479
  }
452
480
  }
453
481
 
454
- async release(id: string, _onclose?: boolean, keepSession?: boolean) {
482
+ async release(id: string, onclose?: boolean, keepSession?: boolean) {
455
483
  try {
456
- await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
457
- await this.releaseLogical(id, keepSession);
484
+ const retainLink =
485
+ keepSession === true &&
486
+ !onclose &&
487
+ this.connectedDevices.has(id) &&
488
+ this.deviceProtocol.get(id) === 'V2' &&
489
+ !this.protocolV2CallCounts.has(id);
490
+ if (!retainLink) {
491
+ await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
492
+ }
493
+ await this.releaseLogical(id, keepSession, retainLink);
458
494
  } catch (error) {
459
495
  this.Log?.error('[Electron BLE] release failed:', error);
460
496
  this.cleanupDeviceState(id);
@@ -499,12 +535,15 @@ export default class ElectronBleTransport {
499
535
  }
500
536
  }
501
537
 
502
- // Logical release: link and subscription stay up for the next call. Renderer
503
- // listeners are still torn down, or fresh ones would double-process packets.
504
- private async releaseLogical(id: string, keepSession?: boolean) {
538
+ // Retained V2 links reuse their renderer listeners; legacy releases remove them.
539
+ private async releaseLogical(id: string, keepSession?: boolean, retainLink = false) {
505
540
  try {
506
541
  if (!this.connectedDevices.has(id)) return;
507
- this.cleanupDeviceState(id);
542
+ if (retainLink) {
543
+ this.retainedDevices.add(id);
544
+ } else {
545
+ this.cleanupDeviceState(id);
546
+ }
508
547
 
509
548
  const release = window.desktopApi?.nobleBle?.release;
510
549
  if (!release) {
@@ -526,17 +565,13 @@ export default class ElectronBleTransport {
526
565
  }
527
566
 
528
567
  private createProtocolMismatchError(expected: ProtocolType) {
529
- return ERRORS.TypedError(
530
- HardwareErrorCode.RuntimeError,
531
- `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`
532
- );
568
+ return ERRORS.TypedError(HardwareErrorCode.TransportProtocolProbeFailed, undefined, {
569
+ expectedProtocol: expected,
570
+ });
533
571
  }
534
572
 
535
573
  private createProtocolDetectionError() {
536
- return ERRORS.TypedError(
537
- HardwareErrorCode.BleTimeoutError,
538
- 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping'
539
- );
574
+ return ERRORS.TypedError(HardwareErrorCode.TransportProtocolProbeFailed);
540
575
  }
541
576
 
542
577
  private clearProbeProtocol(uuid: string, protocol: ProtocolType) {
@@ -1019,6 +1054,7 @@ export default class ElectronBleTransport {
1019
1054
 
1020
1055
  const callOptions = options;
1021
1056
 
1057
+ this.protocolV2CallCounts.set(uuid, (this.protocolV2CallCounts.get(uuid) ?? 0) + 1);
1022
1058
  try {
1023
1059
  return await this.protocolV2Links.call(
1024
1060
  uuid,
@@ -1030,6 +1066,10 @@ export default class ElectronBleTransport {
1030
1066
  } catch (e) {
1031
1067
  this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
1032
1068
  throw e;
1069
+ } finally {
1070
+ const remaining = (this.protocolV2CallCounts.get(uuid) ?? 1) - 1;
1071
+ if (remaining > 0) this.protocolV2CallCounts.set(uuid, remaining);
1072
+ else this.protocolV2CallCounts.delete(uuid);
1033
1073
  }
1034
1074
  }
1035
1075