@onekeyfe/hd-core 1.2.8-alpha.12 → 1.2.8-alpha.120

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,4 +1,7 @@
1
- import Core, { initConnector } from '../src/core';
1
+ import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
2
+
3
+ import Core, { init, initConnector, initCore, switchTransport } from '../src/core';
4
+ import DataManager from '../src/data-manager/DataManager';
2
5
  import TransportManager from '../src/data-manager/TransportManager';
3
6
  import { DevicePool } from '../src/device/DevicePool';
4
7
 
@@ -31,4 +34,114 @@ describe('Core.dispose', () => {
31
34
  expect(transportStop).toHaveBeenCalledTimes(1);
32
35
  expect(disposeDevicePool).toHaveBeenCalledTimes(1);
33
36
  });
37
+
38
+ test('disposal rejects a queued switch without recreating a transport or connector', async () => {
39
+ const previous = TransportManager.transport;
40
+ const { settings } = DataManager;
41
+ const active = { stop: jest.fn().mockResolvedValue(undefined) };
42
+ const replacement = jest.fn(() => ({ stop: jest.fn().mockResolvedValue(undefined) }));
43
+ TransportManager.transport = active as never;
44
+ const core = initCore();
45
+ initConnector();
46
+ const switching = switchTransport({ env: 'emulator', Transport: replacement });
47
+ const rejected = expect(switching).rejects.toMatchObject({
48
+ errorCode: HardwareErrorCode.NotInitialized,
49
+ });
50
+ try {
51
+ await core.dispose();
52
+ await rejected;
53
+ expect(replacement).not.toHaveBeenCalled();
54
+ expect(TransportManager.transport).toBe(active);
55
+ expect(DataManager.settings).toBe(settings);
56
+ expect(DevicePool.emitter.eventNames()).toHaveLength(0);
57
+ } finally {
58
+ await Promise.allSettled([switching]);
59
+ await core.dispose();
60
+ TransportManager.transport = previous;
61
+ DataManager.settings = settings;
62
+ }
63
+ });
64
+
65
+ test.each(['dispose/reinit', 'init', 'initCore'] as const)(
66
+ '%s invalidates running and queued switches without blocking a new lifecycle',
67
+ async lifecycle => {
68
+ const previous = TransportManager.transport;
69
+ const { settings } = DataManager;
70
+ const entered = createDeferred<void>();
71
+ const closing = createDeferred<void>();
72
+ const loading = createDeferred<void>();
73
+ const active = {
74
+ stop: jest.fn(() => {
75
+ entered.resolve();
76
+ return closing.promise;
77
+ }),
78
+ };
79
+ const intermediate = jest.fn(() => ({ stop: jest.fn().mockResolvedValue(undefined) }));
80
+ const stale = jest.fn(() => ({ stop: jest.fn().mockResolvedValue(undefined) }));
81
+ const fresh = { stop: jest.fn().mockResolvedValue(undefined) };
82
+ const current = { stop: jest.fn().mockResolvedValue(undefined) };
83
+ const Fresh = jest.fn(() => fresh);
84
+ jest.spyOn(DataManager, 'load').mockImplementation(async next => {
85
+ await loading.promise;
86
+ DataManager.settings = next;
87
+ });
88
+ TransportManager.transport = active as never;
89
+ let core = initCore();
90
+ initConnector();
91
+ const first = switchTransport({ env: 'emulator', Transport: intermediate });
92
+ await entered.promise;
93
+ const second = switchTransport({ env: 'webusb', Transport: stale });
94
+ const rejected = Promise.all([
95
+ expect(first).rejects.toMatchObject({ errorCode: HardwareErrorCode.NotInitialized }),
96
+ expect(second).rejects.toMatchObject({ errorCode: HardwareErrorCode.NotInitialized }),
97
+ ]);
98
+ rejected.catch(() => undefined);
99
+ let initializing: ReturnType<typeof init> | undefined;
100
+ try {
101
+ if (lifecycle === 'dispose/reinit') {
102
+ const disposing = core.dispose();
103
+ closing.resolve();
104
+ await disposing;
105
+ }
106
+ if (lifecycle === 'initCore') {
107
+ TransportManager.transport = fresh as never;
108
+ DataManager.settings = { ...settings, env: 'desktop-web-ble' };
109
+ core = initCore();
110
+ initConnector();
111
+ } else {
112
+ initializing = init({ ...settings, env: 'desktop-web-ble' }, Fresh);
113
+ if (lifecycle === 'init') {
114
+ closing.resolve();
115
+ await rejected;
116
+ }
117
+ loading.resolve();
118
+ const initialized = await initializing;
119
+ if (!initialized) throw new Error('Core initialization failed');
120
+ core = initialized;
121
+ }
122
+ await switchTransport({
123
+ env: 'emulator',
124
+ Transport: function Current() {
125
+ return current;
126
+ },
127
+ });
128
+ closing.resolve();
129
+ await rejected;
130
+ expect(intermediate).not.toHaveBeenCalled();
131
+ expect(stale).not.toHaveBeenCalled();
132
+ expect(TransportManager.transport).toBe(current);
133
+ expect(DataManager.getSettings('env')).toBe('emulator');
134
+ expect(fresh.stop).toHaveBeenCalledTimes(1);
135
+ expect(current.stop).not.toHaveBeenCalled();
136
+ } finally {
137
+ closing.resolve();
138
+ loading.resolve();
139
+ await initializing;
140
+ await Promise.allSettled([first, second, rejected]);
141
+ await core.dispose();
142
+ TransportManager.transport = previous;
143
+ DataManager.settings = settings;
144
+ }
145
+ }
146
+ );
34
147
  });
@@ -45,6 +45,23 @@ describe('Core 错误输出边界', () => {
45
45
  });
46
46
  });
47
47
 
48
+ test.each([
49
+ [HardwareErrorCode.TransportProtocolProbeFailed, { expectedProtocol: 'V1' }],
50
+ [
51
+ HardwareErrorCode.TransportProtocolMismatch,
52
+ { expectedProtocol: 'V2', detectedProtocol: 'V1' },
53
+ ],
54
+ ] as const)(
55
+ 'preserves protocol error %s and structured context across the public boundary',
56
+ (code, params) => {
57
+ const error = ERRORS.TypedError(code, undefined, params);
58
+ expect(JSON.parse(JSON.stringify(createErrorMessage(error)))).toEqual({
59
+ success: false,
60
+ payload: { code, error: error.message, params },
61
+ });
62
+ }
63
+ );
64
+
48
65
  test.each([
49
66
  ['webusb', false],
50
67
  ['desktop-webusb', false],
@@ -129,6 +146,8 @@ describe('Core 错误输出边界', () => {
129
146
  HardwareErrorCode.BleUnsupported,
130
147
  HardwareErrorCode.BridgeNeedsPermission,
131
148
  HardwareErrorCode.WebUsbDeviceAccessError,
149
+ HardwareErrorCode.TransportProtocolProbeFailed,
150
+ HardwareErrorCode.TransportProtocolMismatch,
132
151
  ])(
133
152
  'preserves terminal transport error %s in the public response without polling again',
134
153
  async errorCode => {
@@ -138,6 +157,8 @@ describe('Core 错误输出边界', () => {
138
157
  [
139
158
  HardwareErrorCode.BridgeNeedsPermission,
140
159
  HardwareErrorCode.WebUsbDeviceAccessError,
160
+ HardwareErrorCode.TransportProtocolProbeFailed,
161
+ HardwareErrorCode.TransportProtocolMismatch,
141
162
  ].includes(errorCode)
142
163
  ? ('desktop-webusb' as never)
143
164
  : ('react-native' as never)
@@ -75,8 +75,11 @@ describe('DeviceConnector protocol validation', () => {
75
75
  });
76
76
  const connector = new DeviceConnector();
77
77
 
78
- await expect(connector.acquire('classic-id', undefined, undefined, 'V2')).rejects.toThrow(
79
- 'Device protocol mismatch: expected V2, detected V1'
78
+ await expect(connector.acquire('classic-id', undefined, undefined, 'V2')).rejects.toMatchObject(
79
+ {
80
+ errorCode: HardwareErrorCode.TransportProtocolMismatch,
81
+ params: { expectedProtocol: 'V2', detectedProtocol: 'V1' },
82
+ }
80
83
  );
81
84
  });
82
85
 
@@ -610,7 +610,9 @@ describe('public device lifecycle events', () => {
610
610
  );
611
611
 
612
612
  test.each([
613
- [HardwareErrorCode.RuntimeError, true],
613
+ [HardwareErrorCode.TransportProtocolProbeFailed, true],
614
+ [HardwareErrorCode.TransportProtocolMismatch, false],
615
+ [HardwareErrorCode.RuntimeError, false],
614
616
  [HardwareErrorCode.BleDeviceBondError, false],
615
617
  ] as const)(
616
618
  'retries a Protocol V2 probe mismatch with error code %s: %s',
@@ -618,8 +620,8 @@ describe('public device lifecycle events', () => {
618
620
  const method = { payload: { connectProtocol: 'V2' } } as never;
619
621
  const error = {
620
622
  errorCode,
621
- message:
622
- 'Device protocol mismatch: expected V2, but device did not respond to expected protocol',
623
+ message: 'diagnostic wording is not used for classification',
624
+ params: { expectedProtocol: 'V2' },
623
625
  };
624
626
 
625
627
  expect(isRetryableBleProtocolV2ProbeError(method, error)).toBe(expected);
@@ -629,6 +631,8 @@ describe('public device lifecycle events', () => {
629
631
  test.each([
630
632
  [HardwareErrorCode.BleConnectedError, true],
631
633
  [HardwareErrorCode.BleTimeoutError, true],
634
+ [HardwareErrorCode.TransportProtocolProbeFailed, true],
635
+ [HardwareErrorCode.TransportProtocolMismatch, false],
632
636
  [HardwareErrorCode.PollingTimeout, false],
633
637
  [HardwareErrorCode.BleDeviceBondError, false],
634
638
  ] as const)('retries a BLE connection error with error code %s: %s', (errorCode, expected) => {
@@ -641,6 +645,21 @@ describe('public device lifecycle events', () => {
641
645
  expect(isRetryableBleConnectionError(method, error)).toBe(expected);
642
646
  });
643
647
 
648
+ test('does not retry a failed strict V1 probe as a V2 probe or after user cancellation', () => {
649
+ const error = ERRORS.TypedError(HardwareErrorCode.TransportProtocolProbeFailed, undefined, {
650
+ expectedProtocol: 'V1',
651
+ });
652
+ const method = { payload: { connectProtocol: 'V2' } } as never;
653
+ expect(isRetryableBleProtocolV2ProbeError(method, error)).toBe(false);
654
+ expect(isRetryableBleConnectionError(method, error)).toBe(false);
655
+ expect(
656
+ isRetryableBleConnectionError(
657
+ { payload: {}, device: { wasInterruptedByUser: () => true } } as never,
658
+ ERRORS.TypedError(HardwareErrorCode.TransportProtocolProbeFailed)
659
+ )
660
+ ).toBe(false);
661
+ });
662
+
644
663
  test('does not retry the desktop acquire deadline fallback', () => {
645
664
  const method = { payload: { connectProtocol: 'V2' } } as never;
646
665
  const error = {
@@ -0,0 +1,18 @@
1
+ import { LoggerNames, enableLog, getLogger } from '../src/utils/logger';
2
+
3
+ describe('logger', () => {
4
+ afterEach(() => {
5
+ enableLog(false);
6
+ jest.restoreAllMocks();
7
+ });
8
+
9
+ test('enables method logs that share a prefix with common-connect-sdk', () => {
10
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
11
+ enableLog(true);
12
+
13
+ expect(getLogger(LoggerNames.Method)).toBe(getLogger(LoggerNames.HdCommonConnectSdk));
14
+ getLogger(LoggerNames.Method).debug('shared prefix message');
15
+
16
+ expect(consoleSpy).toHaveBeenCalledWith(LoggerNames.Method, 'shared prefix message');
17
+ });
18
+ });