@onekeyfe/hd-core 1.2.0-alpha.56 → 1.2.0-alpha.58

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.
Files changed (36) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +84 -2
  2. package/__tests__/DeviceCommands.test.ts +17 -6
  3. package/__tests__/core-initialization.test.ts +23 -0
  4. package/__tests__/device-connector-protocol.test.ts +81 -0
  5. package/__tests__/device-settings.test.ts +39 -18
  6. package/__tests__/protocol-v2-resources.test.ts +24 -0
  7. package/__tests__/protocol-v2.test.ts +46 -1
  8. package/__tests__/protocolV2FileWrite.test.ts +113 -0
  9. package/dist/api/FirmwareUpdateV4.d.ts +0 -1
  10. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  11. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  12. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  13. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -2
  14. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  15. package/dist/api/helpers/protocolV2FileWrite.d.ts +7 -0
  16. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/data-manager/DataManager.d.ts +1 -0
  19. package/dist/data-manager/DataManager.d.ts.map +1 -1
  20. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  21. package/dist/device/DeviceCommands.d.ts.map +1 -1
  22. package/dist/device/DeviceConnector.d.ts +2 -1
  23. package/dist/device/DeviceConnector.d.ts.map +1 -1
  24. package/dist/index.d.ts +3 -1
  25. package/dist/index.js +448 -336
  26. package/package.json +4 -4
  27. package/src/api/FirmwareUpdateV4.ts +39 -69
  28. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +3 -1
  29. package/src/api/device/DeviceSettings.ts +5 -4
  30. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +12 -1
  31. package/src/api/helpers/protocolV2FileWrite.ts +192 -73
  32. package/src/core/index.ts +9 -6
  33. package/src/data-manager/DataManager.ts +25 -4
  34. package/src/data-manager/TransportManager.ts +6 -0
  35. package/src/device/DeviceCommands.ts +20 -2
  36. package/src/device/DeviceConnector.ts +33 -13
@@ -26,6 +26,7 @@ import type {
26
26
  Features,
27
27
  IDeviceBLEFirmwareStatus,
28
28
  IDeviceFirmwareStatus,
29
+ IProtocolV2Resources,
29
30
  ITransportStatus,
30
31
  IVersionArray,
31
32
  RemoteConfigResponse,
@@ -112,6 +113,8 @@ export default class DataManager {
112
113
 
113
114
  static lastCheckTimestamp = 0;
114
115
 
116
+ static protocolV2ResourcesConfigError: Error | undefined;
117
+
115
118
  static getFirmwareStatus = (
116
119
  features: Features,
117
120
  firmwareType: EFirmwareType
@@ -400,6 +403,7 @@ export default class DataManager {
400
403
 
401
404
  static async load(settings: ConnectSettings): Promise<boolean> {
402
405
  this.settings = settings;
406
+ this.protocolV2ResourcesConfigError = undefined;
403
407
  if (!settings.fetchConfig) {
404
408
  return false;
405
409
  }
@@ -446,9 +450,20 @@ export default class DataManager {
446
450
 
447
451
  // 3. Apply config if available
448
452
  if (data) {
449
- const pro2Resources = parseProtocolV2Resources(
450
- (data.pro2 as { resources?: unknown } | undefined)?.resources
451
- );
453
+ let pro2Resources: IProtocolV2Resources | undefined;
454
+ try {
455
+ pro2Resources = parseProtocolV2Resources(
456
+ (data.pro2 as { resources?: unknown } | undefined)?.resources
457
+ );
458
+ } catch (error) {
459
+ // Firmware resource metadata is not required for base communication. If the
460
+ // remote config is temporarily incomplete, disable this resource update only.
461
+ this.protocolV2ResourcesConfigError =
462
+ error instanceof Error ? error : new Error(String(error));
463
+ Log.warn('[DataConfig] Ignoring invalid Pro2 resources config:', error);
464
+ }
465
+ const enrichedPro2Config = this.enrichFirmwareReleaseInfo(data.pro2);
466
+ const { resources: _unvalidatedResources, ...pro2Config } = enrichedPro2Config;
452
467
  Log.log(`[DataConfig] Config loaded successfully via [${fetchMethod}]`);
453
468
  this.deviceMap = {
454
469
  [EDeviceType.Classic]: this.enrichFirmwareReleaseInfo(data.classic),
@@ -458,7 +473,7 @@ export default class DataManager {
458
473
  [EDeviceType.Touch]: this.enrichFirmwareReleaseInfo(data.touch),
459
474
  [EDeviceType.Pro]: this.enrichFirmwareReleaseInfo(data.pro),
460
475
  [EDeviceType.Pro2]: {
461
- ...this.enrichFirmwareReleaseInfo(data.pro2),
476
+ ...pro2Config,
462
477
  ...(pro2Resources ? { resources: pro2Resources } : undefined),
463
478
  },
464
479
  };
@@ -505,6 +520,12 @@ export default class DataManager {
505
520
  'Unable to refresh the latest remote config'
506
521
  );
507
522
  }
523
+ if (this.protocolV2ResourcesConfigError) {
524
+ throw ERRORS.TypedError(
525
+ HardwareErrorCode.NetworkError,
526
+ `Invalid Pro2 resources config: ${this.protocolV2ResourcesConfigError.message}`
527
+ );
528
+ }
508
529
  this.lastCheckTimestamp = getTimeStamp();
509
530
  }
510
531
 
@@ -121,6 +121,12 @@ export default class TransportManager {
121
121
  }
122
122
 
123
123
  static setTransport(TransportConstructor: any, plugin?: LowlevelTransportSharedPlugin) {
124
+ if (typeof TransportConstructor !== 'function') {
125
+ throw ERRORS.TypedError(
126
+ HardwareErrorCode.TransportNotConfigured,
127
+ 'Transport constructor is not available'
128
+ );
129
+ }
124
130
  const env = DataManager.getSettings('env');
125
131
  if (env === 'react-native') {
126
132
  /** Actually initializes the ReactNativeTransport */
@@ -55,8 +55,10 @@ const DEVICE_SESSION_CALLS = new Set([
55
55
  'DeviceSessionAskPassphrase',
56
56
  ]);
57
57
 
58
- // Protocol V2 subcodes are domain-scoped, so cross-domain cancellation fallback
59
- // must use explicit firmware cancellation messages instead of a global number map.
58
+ const PROTOCOL_V2_ACTION_CANCELLED_SUBCODE = 1;
59
+
60
+ // Older Protocol V2 firmware may omit the cancellation subcode, so retain a
61
+ // narrow message fallback for compatibility.
60
62
  const isProtocolV2ActionCancelledMessage = (message: string) =>
61
63
  /^(?:cancel(?:led|ed)(?: on device)?|confirm dismissed|user cancel(?:led|ed)(?:\s+.*)?)$/i.test(
62
64
  message
@@ -499,6 +501,15 @@ export class DeviceCommands {
499
501
  const normalizedMessage = message?.trim() ?? '';
500
502
  const isProtocolV2ActionCancelledFailure =
501
503
  this.device.isProtocolV2() && isProtocolV2ActionCancelledMessage(normalizedMessage);
504
+ const isProtocolV2ActionCancelledSubcode =
505
+ this.device.isProtocolV2() && subcode === PROTOCOL_V2_ACTION_CANCELLED_SUBCODE;
506
+ // DeviceErrorCode currently reuses subcode 1 for Busy. Resolve that
507
+ // domain-specific exception before applying the common cancellation map.
508
+ const isProtocolV2DeviceBusyFailure =
509
+ this.device.isProtocolV2() &&
510
+ callType.startsWith('Device') &&
511
+ !DEVICE_SESSION_CALLS.has(callType) &&
512
+ subcode === DeviceErrorCode.DeviceError_Busy;
502
513
  const isLegacyProtocolV2LockedFailure =
503
514
  this.device.isProtocolV2() && /^device (?:is )?locked$/i.test(normalizedMessage);
504
515
  if (
@@ -556,8 +567,15 @@ export class DeviceCommands {
556
567
  subcode,
557
568
  firmwareMessage: message,
558
569
  });
570
+ } else if (isProtocolV2DeviceBusyFailure) {
571
+ error = ERRORS.TypedError(HardwareErrorCode.DeviceBusy, message, {
572
+ failureCode: code,
573
+ subcode,
574
+ firmwareMessage: message,
575
+ });
559
576
  } else if (
560
577
  subcode === DeviceErrorCode.DeviceError_ActionCancelled ||
578
+ isProtocolV2ActionCancelledSubcode ||
561
579
  isProtocolV2ActionCancelledFailure
562
580
  ) {
563
581
  error = ERRORS.TypedError(HardwareErrorCode.ActionCancelled, message, {
@@ -14,7 +14,7 @@ import type { OneKeyDeviceInfo as DeviceDescriptor, Transport } from '@onekeyfe/
14
14
  const Log = getLogger(LoggerNames.DeviceConnector);
15
15
 
16
16
  export default class DeviceConnector {
17
- transport: Transport;
17
+ transport?: Transport;
18
18
 
19
19
  listenTimestamp = 0;
20
20
 
@@ -30,9 +30,21 @@ export default class DeviceConnector {
30
30
  DevicePool.setConnector(this);
31
31
  }
32
32
 
33
+ private getActiveTransport(): Transport {
34
+ const transport = this.transport ?? TransportManager.getTransport();
35
+ if (!transport) {
36
+ throw ERRORS.TypedError(
37
+ HardwareErrorCode.TransportNotConfigured,
38
+ 'Device connector was created before transport initialization'
39
+ );
40
+ }
41
+ this.transport = transport;
42
+ return transport;
43
+ }
44
+
33
45
  async enumerate() {
34
46
  try {
35
- const descriptors = await this.transport.enumerate();
47
+ const descriptors = await this.getActiveTransport().enumerate();
36
48
  this.upcoming = descriptors;
37
49
  this._reportDevicesChange();
38
50
  return { descriptors } as DeviceDescriptorDiff;
@@ -50,11 +62,10 @@ export default class DeviceConnector {
50
62
  let descriptors: DeviceDescriptor[];
51
63
 
52
64
  try {
65
+ const transport = this.getActiveTransport();
53
66
  Log.debug('Start listening', current);
54
67
  this.listenTimestamp = new Date().getTime();
55
- descriptors = waitForEvent
56
- ? await this.transport.listen(current)
57
- : await this.transport.enumerate();
68
+ descriptors = waitForEvent ? await transport.listen(current) : await transport.enumerate();
58
69
  if (!this.listening) return; // do not continue if stop() was called
59
70
 
60
71
  this.upcoming = descriptors;
@@ -88,16 +99,17 @@ export default class DeviceConnector {
88
99
  Log.debug('acquire', path, session, expectedProtocol, protocolHint);
89
100
  const env = DataManager.getSettings('env');
90
101
  try {
102
+ const transport = this.getActiveTransport();
91
103
  let res;
92
104
  if (DataManager.isBleConnect(env)) {
93
- res = await this.transport.acquire({
105
+ res = await transport.acquire({
94
106
  uuid: path,
95
107
  forceCleanRunPromise,
96
108
  expectedProtocol,
97
109
  protocolHint,
98
110
  });
99
111
  } else {
100
- res = await this.transport.acquire({
112
+ res = await transport.acquire({
101
113
  path,
102
114
  previous: session ?? null,
103
115
  expectedProtocol,
@@ -105,7 +117,13 @@ export default class DeviceConnector {
105
117
  });
106
118
  }
107
119
  if (expectedProtocol) {
108
- const detectedProtocol = this.transport.getProtocolType(path);
120
+ // The acquire response is the stable snapshot from this active probe. A delayed
121
+ // disconnect from an older generation may clear caches, so do not rely on cache alone.
122
+ const acquiredProtocol =
123
+ typeof res === 'object' && res !== null
124
+ ? (res as DeviceDescriptor).protocolType
125
+ : undefined;
126
+ const detectedProtocol = acquiredProtocol ?? transport.getProtocolType(path);
109
127
  if (detectedProtocol !== expectedProtocol) {
110
128
  throw ERRORS.TypedError(
111
129
  HardwareErrorCode.RuntimeError,
@@ -122,7 +140,7 @@ export default class DeviceConnector {
122
140
 
123
141
  async release(session: string, onclose: boolean) {
124
142
  try {
125
- const res = await this.transport.release(session, onclose);
143
+ const res = await this.getActiveTransport().release(session, onclose);
126
144
  return res;
127
145
  } catch (error) {
128
146
  safeThrowError(error);
@@ -131,8 +149,9 @@ export default class DeviceConnector {
131
149
 
132
150
  async disconnect(session: string | undefined | null) {
133
151
  try {
134
- if (this.transport.disconnect && !!session) {
135
- await this.transport.disconnect(session);
152
+ const transport = this.getActiveTransport();
153
+ if (transport.disconnect && !!session) {
154
+ await transport.disconnect(session);
136
155
  }
137
156
  } catch (error) {
138
157
  safeThrowError(error);
@@ -140,10 +159,11 @@ export default class DeviceConnector {
140
159
  }
141
160
 
142
161
  promptDeviceAccess(): Promise<USBDevice | BluetoothDevice | null> {
143
- if (!this.transport.promptDeviceAccess) {
162
+ const transport = this.getActiveTransport();
163
+ if (!transport.promptDeviceAccess) {
144
164
  return Promise.resolve(null);
145
165
  }
146
- return this.transport.promptDeviceAccess();
166
+ return transport.promptDeviceAccess();
147
167
  }
148
168
 
149
169
  _reportDevicesChange() {