@onekeyfe/hd-core 1.2.0-alpha.18 → 1.2.0-alpha.19

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 (141) hide show
  1. package/__tests__/device-features-state.test.ts +78 -0
  2. package/__tests__/device-identity.test.ts +30 -0
  3. package/__tests__/device-pool-state.test.ts +113 -0
  4. package/__tests__/device-settings.test.ts +108 -0
  5. package/__tests__/device-state-contract.test.ts +27 -0
  6. package/__tests__/device-state-events.test.ts +163 -0
  7. package/__tests__/device-state-mapper.test.ts +222 -0
  8. package/__tests__/device-state-projector.test.ts +94 -0
  9. package/__tests__/device-state-store.test.ts +115 -0
  10. package/__tests__/device-wallet-session-store.test.ts +46 -0
  11. package/__tests__/deviceFeaturesUtils.test.ts +13 -1
  12. package/__tests__/deviceSettings.test.ts +54 -0
  13. package/__tests__/get-device-state.test.ts +367 -0
  14. package/__tests__/logBlockEvent.test.ts +13 -0
  15. package/__tests__/protocol-v2.test.ts +358 -413
  16. package/__tests__/public-device-state-api.test.ts +153 -0
  17. package/__tests__/refresh-device-state.test.ts +101 -0
  18. package/__tests__/request-context-logging.test.ts +16 -0
  19. package/__tests__/search-devices.test.ts +93 -0
  20. package/dist/api/ClearSessionCache.d.ts.map +1 -1
  21. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  22. package/dist/api/GetDeviceState.d.ts +7 -0
  23. package/dist/api/GetDeviceState.d.ts.map +1 -0
  24. package/dist/api/GetFeatures.d.ts +1 -1
  25. package/dist/api/GetFeatures.d.ts.map +1 -1
  26. package/dist/api/GetOnekeyFeatures.d.ts.map +1 -1
  27. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  28. package/dist/api/SearchDevices.d.ts.map +1 -1
  29. package/dist/api/device/DeviceRebootToBoardloader.d.ts.map +1 -1
  30. package/dist/api/device/DeviceRebootToBootloader.d.ts.map +1 -1
  31. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  32. package/dist/api/index.d.ts +1 -4
  33. package/dist/api/index.d.ts.map +1 -1
  34. package/dist/api/internal.d.ts +4 -0
  35. package/dist/api/internal.d.ts.map +1 -0
  36. package/dist/api/protocol-v2/DeviceInfoGet.d.ts +0 -1
  37. package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -1
  38. package/dist/api/protocol-v2/DeviceReboot.d.ts.map +1 -1
  39. package/dist/api/protocol-v2/DeviceSettingsGet.d.ts.map +1 -1
  40. package/dist/api/protocol-v2/DeviceSettingsSet.d.ts.map +1 -1
  41. package/dist/api/protocol-v2/DeviceStatusGet.d.ts.map +1 -1
  42. package/dist/api/utils.d.ts +2 -1
  43. package/dist/api/utils.d.ts.map +1 -1
  44. package/dist/constants/index.d.ts +1 -1
  45. package/dist/constants/index.d.ts.map +1 -1
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/device/Device.d.ts +22 -3
  48. package/dist/device/Device.d.ts.map +1 -1
  49. package/dist/device/DeviceFeaturesState.d.ts +4 -0
  50. package/dist/device/DeviceFeaturesState.d.ts.map +1 -0
  51. package/dist/device/DevicePool.d.ts +1 -0
  52. package/dist/device/DevicePool.d.ts.map +1 -1
  53. package/dist/device/DeviceStateMapper.d.ts +13 -0
  54. package/dist/device/DeviceStateMapper.d.ts.map +1 -0
  55. package/dist/device/DeviceStateProjector.d.ts +8 -0
  56. package/dist/device/DeviceStateProjector.d.ts.map +1 -0
  57. package/dist/device/DeviceStateStore.d.ts +23 -0
  58. package/dist/device/DeviceStateStore.d.ts.map +1 -0
  59. package/dist/device/deviceIdentity.d.ts +5 -0
  60. package/dist/device/deviceIdentity.d.ts.map +1 -0
  61. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  62. package/dist/deviceProfile/index.d.ts +0 -1
  63. package/dist/deviceProfile/index.d.ts.map +1 -1
  64. package/dist/events/device.d.ts +7 -2
  65. package/dist/events/device.d.ts.map +1 -1
  66. package/dist/index.d.ts +688 -639
  67. package/dist/index.js +1274 -942
  68. package/dist/inject.d.ts.map +1 -1
  69. package/dist/protocols/protocol-v2/features.d.ts +1 -0
  70. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  71. package/dist/protocols/protocol-v2/index.d.ts +1 -1
  72. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  73. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  74. package/dist/types/api/deviceSettings.d.ts +8 -0
  75. package/dist/types/api/deviceSettings.d.ts.map +1 -1
  76. package/dist/types/api/getDeviceState.d.ts +12 -0
  77. package/dist/types/api/getDeviceState.d.ts.map +1 -0
  78. package/dist/types/api/index.d.ts +4 -7
  79. package/dist/types/api/index.d.ts.map +1 -1
  80. package/dist/types/api/protocolV2.d.ts +1 -6
  81. package/dist/types/api/protocolV2.d.ts.map +1 -1
  82. package/dist/types/device.d.ts +121 -1
  83. package/dist/types/device.d.ts.map +1 -1
  84. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  85. package/dist/utils/deviceSettings.d.ts.map +1 -1
  86. package/dist/utils/patch.d.ts +1 -1
  87. package/dist/utils/tracing.d.ts.map +1 -1
  88. package/package.json +4 -4
  89. package/src/api/ClearSessionCache.ts +4 -0
  90. package/src/api/FirmwareUpdateV4.ts +15 -7
  91. package/src/api/GetDeviceState.ts +55 -0
  92. package/src/api/GetFeatures.ts +8 -3
  93. package/src/api/GetOnekeyFeatures.ts +3 -72
  94. package/src/api/GetPassphraseState.ts +3 -1
  95. package/src/api/SearchDevices.ts +25 -4
  96. package/src/api/device/DeviceRebootToBoardloader.ts +1 -0
  97. package/src/api/device/DeviceRebootToBootloader.ts +1 -0
  98. package/src/api/device/DeviceSettings.ts +28 -0
  99. package/src/api/index.ts +1 -4
  100. package/src/api/internal.ts +9 -0
  101. package/src/api/protocol-v2/DeviceInfoGet.ts +3 -5
  102. package/src/api/protocol-v2/DeviceReboot.ts +3 -1
  103. package/src/api/protocol-v2/DeviceSettingsGet.ts +3 -1
  104. package/src/api/protocol-v2/DeviceSettingsSet.ts +2 -0
  105. package/src/api/protocol-v2/DeviceStatusGet.ts +1 -0
  106. package/src/api/utils.ts +9 -3
  107. package/src/constants/index.ts +1 -4
  108. package/src/core/index.ts +7 -1
  109. package/src/data/messages/messages-protocol-v2.json +22 -13
  110. package/src/device/Device.ts +256 -78
  111. package/src/device/DeviceFeaturesState.ts +34 -0
  112. package/src/device/DevicePool.ts +35 -9
  113. package/src/device/DeviceStateMapper.ts +364 -0
  114. package/src/device/DeviceStateProjector.ts +92 -0
  115. package/src/device/DeviceStateStore.ts +242 -0
  116. package/src/device/deviceIdentity.ts +18 -0
  117. package/src/deviceProfile/buildDeviceFeatures.ts +166 -109
  118. package/src/deviceProfile/index.ts +0 -1
  119. package/src/events/device.ts +13 -1
  120. package/src/events/logBlockEvent.ts +1 -1
  121. package/src/inject.ts +4 -5
  122. package/src/protocols/protocol-v2/features.ts +18 -9
  123. package/src/protocols/protocol-v2/index.ts +1 -0
  124. package/src/protocols/protocol-v2/walletSession.ts +5 -7
  125. package/src/types/api/deviceSettings.ts +8 -0
  126. package/src/types/api/getDeviceState.ts +19 -0
  127. package/src/types/api/index.ts +3 -20
  128. package/src/types/api/protocolV2.ts +0 -24
  129. package/src/types/device.ts +148 -1
  130. package/src/utils/deviceFeaturesUtils.ts +5 -1
  131. package/src/utils/deviceSettings.ts +41 -0
  132. package/src/utils/tracing.ts +3 -1
  133. package/dist/api/GetDeviceInfo.d.ts +0 -9
  134. package/dist/api/GetDeviceInfo.d.ts.map +0 -1
  135. package/dist/deviceProfile/buildDeviceProfile.d.ts +0 -22
  136. package/dist/deviceProfile/buildDeviceProfile.d.ts.map +0 -1
  137. package/dist/types/api/getDeviceInfo.d.ts +0 -88
  138. package/dist/types/api/getDeviceInfo.d.ts.map +0 -1
  139. package/src/api/GetDeviceInfo.ts +0 -152
  140. package/src/deviceProfile/buildDeviceProfile.ts +0 -370
  141. package/src/types/api/getDeviceInfo.ts +0 -106
@@ -0,0 +1,222 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import {
4
+ mapApplySettingsToState,
5
+ mapDeviceSettingsToState,
6
+ mapProtocolV1FeaturesToState,
7
+ mapProtocolV2DeviceInfoToState,
8
+ mapProtocolV2DeviceStatusToState,
9
+ } from '../src/device/DeviceStateMapper';
10
+
11
+ import type { PROTO } from '../src/constants';
12
+ import type { DeviceSettings, DeviceStatus, ProtocolV2DeviceInfo } from '@onekeyfe/hd-transport';
13
+
14
+ describe('DeviceStateMapper', () => {
15
+ test('maps Protocol V1 Features into structured state sections', () => {
16
+ const patch = mapProtocolV1FeaturesToState({
17
+ onekey_device_type: 'PRO',
18
+ device_id: 'device-1',
19
+ label: 'Pro Wallet',
20
+ initialized: true,
21
+ unlocked: true,
22
+ language: 'en-US',
23
+ major_version: 4,
24
+ minor_version: 10,
25
+ patch_version: 1,
26
+ se_ver: '1.1.0.2',
27
+ } as PROTO.Features);
28
+
29
+ expect(patch).toMatchObject({
30
+ protocol: 'V1',
31
+ identity: {
32
+ deviceType: EDeviceType.Pro,
33
+ deviceId: 'device-1',
34
+ label: 'Pro Wallet',
35
+ },
36
+ status: { mode: 'normal', initialized: true, unlocked: true },
37
+ settings: { language: 'en-US' },
38
+ versions: { firmware: '4.10.1', se: '1.1.0.2' },
39
+ });
40
+ });
41
+
42
+ test.each([
43
+ ['CLASSIC1S', EDeviceType.Classic1s],
44
+ ['TOUCH', EDeviceType.Touch],
45
+ ['PRO', EDeviceType.Pro],
46
+ ] as const)(
47
+ 'keeps the real label separate from display fallbacks for Protocol V1 %s',
48
+ (onekeyDeviceType, expectedDeviceType) => {
49
+ const patch = mapProtocolV1FeaturesToState({
50
+ onekey_device_type: onekeyDeviceType,
51
+ onekey_ble_name: `${onekeyDeviceType} BLE`,
52
+ initialized: true,
53
+ } as PROTO.Features);
54
+
55
+ expect(patch.identity).toMatchObject({
56
+ deviceType: expectedDeviceType,
57
+ label: null,
58
+ bleName: `${onekeyDeviceType} BLE`,
59
+ });
60
+ }
61
+ );
62
+
63
+ test.each([
64
+ ['CLASSIC1S', EDeviceType.Classic1s],
65
+ ['TOUCH', EDeviceType.Touch],
66
+ ['PRO', EDeviceType.Pro],
67
+ ] as const)(
68
+ 'preserves Protocol V1 backup mode semantics for %s',
69
+ (onekeyDeviceType, expectedDeviceType) => {
70
+ const patch = mapProtocolV1FeaturesToState({
71
+ onekey_device_type: onekeyDeviceType,
72
+ initialized: true,
73
+ no_backup: true,
74
+ } as PROTO.Features);
75
+
76
+ expect(patch.identity?.deviceType).toBe(expectedDeviceType);
77
+ expect(patch.status?.mode).toBe('backupMode');
78
+ }
79
+ );
80
+
81
+ test('normalizes legacy Bitcoin-only and Attach PIN fields from Protocol V1', () => {
82
+ const patch = mapProtocolV1FeaturesToState({
83
+ onekey_device_type: 'PRO',
84
+ capabilities: [1],
85
+ attach_to_pin_user: true,
86
+ unlocked_attach_pin: true,
87
+ initialized: true,
88
+ } as PROTO.Features);
89
+
90
+ expect(patch.identity?.firmwareType).toBe(EFirmwareType.BitcoinOnly);
91
+ expect(patch.status).toMatchObject({
92
+ attachToPinEnabled: true,
93
+ unlockedAttachPin: true,
94
+ });
95
+ });
96
+
97
+ test('keeps Protocol V1 universal firmware when the decoded Bitcoin-like capability is present', () => {
98
+ const patch = mapProtocolV1FeaturesToState({
99
+ onekey_device_type: 'CLASSIC1S',
100
+ capabilities: ['Capability_Bitcoin_like'],
101
+ initialized: true,
102
+ } as PROTO.Features);
103
+
104
+ expect(patch.identity?.firmwareType).toBe(EFirmwareType.Universal);
105
+ });
106
+
107
+ test('maps Protocol V2 DeviceInfo without inventing runtime status', () => {
108
+ const patch = mapProtocolV2DeviceInfoToState({
109
+ protocol_version: 2,
110
+ hw: { serial_no: 'SERIAL-1' },
111
+ fw: {
112
+ application: { version: '5.0.0' },
113
+ bootloader: { version: '2.0.0' },
114
+ },
115
+ coprocessor: {
116
+ bt_adv_name: 'Pro2 1234',
117
+ application: { version: '1.2.3' },
118
+ },
119
+ } as ProtocolV2DeviceInfo);
120
+
121
+ expect(patch.identity).toMatchObject({
122
+ deviceType: EDeviceType.Pro2,
123
+ serialNo: 'SERIAL-1',
124
+ bleName: 'Pro2 1234',
125
+ });
126
+ expect(patch.versions).toMatchObject({
127
+ firmware: '5.0.0',
128
+ bootloader: '2.0.0',
129
+ ble: '1.2.3',
130
+ });
131
+ expect(patch).toMatchObject({ protocolVersion: 2 });
132
+ expect(patch.status?.unlocked).toBeUndefined();
133
+ });
134
+
135
+ test('keeps normal mode when Protocol V2 application and SE versions are returned together', () => {
136
+ const patch = mapProtocolV2DeviceInfoToState({
137
+ protocol_version: 2,
138
+ hw: { serial_no: 'SERIAL-NORMAL' },
139
+ fw: {
140
+ application: { version: '5.0.0' },
141
+ bootloader: { version: '2.0.0' },
142
+ },
143
+ se1: {
144
+ application: { version: '1.0.0', build_id: 'se-app', hash: [0x01, 0x02] },
145
+ bootloader: { version: '0.1.0', build_id: 'se-boot', hash: [0x03, 0x04] },
146
+ },
147
+ } as ProtocolV2DeviceInfo);
148
+
149
+ expect(patch.status?.mode).toBe('normal');
150
+ expect(patch.verification).toMatchObject({
151
+ se01BuildId: 'se-app',
152
+ se01Hash: '0102',
153
+ se01BootBuildId: 'se-boot',
154
+ se01BootHash: '0304',
155
+ });
156
+ });
157
+
158
+ test.each([
159
+ [
160
+ 'bootloader',
161
+ {
162
+ hw: { serial_no: 'SERIAL-BOOT' },
163
+ fw: { bootloader: { version: '2.0.0' } },
164
+ },
165
+ ],
166
+ [
167
+ 'romloader',
168
+ {
169
+ hw: { serial_no: 'SERIAL-ROM' },
170
+ fw: {
171
+ romloader: { version: '1.0.0' },
172
+ bootloader: { version: '2.0.0' },
173
+ },
174
+ },
175
+ ],
176
+ ] as const)('clears stale device identity in Protocol V2 %s mode', (mode, info) => {
177
+ const patch = mapProtocolV2DeviceInfoToState(info as ProtocolV2DeviceInfo);
178
+
179
+ expect(patch.status?.mode).toBe(mode);
180
+ expect(patch.identity).toMatchObject({ deviceId: null });
181
+ });
182
+
183
+ test('maps Protocol V2 status separately and ignores locked passphrase value', () => {
184
+ const locked = mapProtocolV2DeviceStatusToState({
185
+ device_id: 'device-2',
186
+ init_states: true,
187
+ unlocked: false,
188
+ passphrase_enabled: true,
189
+ backup_required: true,
190
+ } as DeviceStatus);
191
+
192
+ expect(locked.identity?.deviceId).toBe('device-2');
193
+ expect(locked.status).toMatchObject({
194
+ mode: 'normal',
195
+ initialized: true,
196
+ unlocked: false,
197
+ backupRequired: true,
198
+ });
199
+ expect(locked.status).not.toHaveProperty('passphraseProtection');
200
+ });
201
+
202
+ test('maps common settings into identity, status and settings sections', () => {
203
+ expect(
204
+ mapApplySettingsToState({ label: 'Renamed', use_passphrase: true, haptic_feedback: false })
205
+ ).toEqual({
206
+ identity: { label: 'Renamed' },
207
+ status: { passphraseProtection: true },
208
+ settings: { hapticFeedback: false },
209
+ });
210
+
211
+ expect(
212
+ mapDeviceSettingsToState({
213
+ label: 'Pro2',
214
+ brightness: 80,
215
+ autolock_delay_ms: 30_000,
216
+ } as DeviceSettings)
217
+ ).toEqual({
218
+ identity: { label: 'Pro2' },
219
+ settings: { brightness: 80, autoLockDelayMs: 30_000 },
220
+ });
221
+ });
222
+ });
@@ -0,0 +1,94 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import { createEmptyDeviceState } from '../src/device/DeviceStateStore';
4
+ import { projectFeatures } from '../src/device/DeviceStateProjector';
5
+
6
+ describe('DeviceStateProjector', () => {
7
+ test('projects legacy Features from the canonical snapshot', () => {
8
+ const state = createEmptyDeviceState({
9
+ deviceType: EDeviceType.Pro2,
10
+ firmwareType: EFirmwareType.Universal,
11
+ deviceId: 'device-1',
12
+ serialNo: 'SERIAL-1',
13
+ label: 'My Pro2',
14
+ bleName: 'Pro2 1234',
15
+ });
16
+ state.protocol = 'V2';
17
+ state.protocolVersion = 7;
18
+ state.status = {
19
+ ...state.status,
20
+ mode: 'normal',
21
+ initialized: true,
22
+ unlocked: true,
23
+ passphraseProtection: false,
24
+ };
25
+ state.settings = {
26
+ ...state.settings,
27
+ language: 'en-US',
28
+ brightness: 80,
29
+ autoLockDelayMs: 30_000,
30
+ };
31
+ state.versions = {
32
+ ...state.versions,
33
+ firmware: '5.0.0',
34
+ bootloader: '2.0.0',
35
+ ble: '1.2.3',
36
+ };
37
+
38
+ const features = projectFeatures(state);
39
+ expect(features).toMatchObject({
40
+ protocol: 'V2',
41
+ protocolVersion: 7,
42
+ label: 'My Pro2',
43
+ unlocked: true,
44
+ brightness: 80,
45
+ firmwareVersion: '5.0.0',
46
+ });
47
+ });
48
+
49
+ test.each([
50
+ [EDeviceType.Classic1s],
51
+ [EDeviceType.ClassicPure],
52
+ [EDeviceType.Touch],
53
+ [EDeviceType.Pro],
54
+ ] as const)('preserves Protocol V1 compatibility defaults for %s', deviceType => {
55
+ const state = createEmptyDeviceState({ deviceType });
56
+ state.protocol = 'V1';
57
+ state.versions.se = '1.1.0.2';
58
+ state.raw = {
59
+ protocolV1Features: {
60
+ onekey_device_type: deviceType,
61
+ initialized: true,
62
+ } as never,
63
+ };
64
+
65
+ const features = projectFeatures(state);
66
+
67
+ expect(features).toMatchObject({
68
+ protocolVersion: 1,
69
+ label: null,
70
+ bootloaderMode: null,
71
+ bootloader_mode: null,
72
+ seVersion: '1.1.0.2',
73
+ });
74
+ });
75
+
76
+ test('returns an isolated compatibility snapshot', () => {
77
+ const state = createEmptyDeviceState({ deviceType: EDeviceType.Pro });
78
+ state.protocol = 'V1';
79
+ state.capabilities = [1];
80
+ state.verification = { firmwareBuildId: 'build-1' };
81
+ state.raw = {
82
+ protocolV1Features: { label: 'Original' } as never,
83
+ };
84
+
85
+ const features = projectFeatures(state);
86
+ features.capabilities.push(2);
87
+ if (features.verify) features.verify.firmwareBuildId = 'mutated';
88
+ if (features.raw?.protocolV1Features) features.raw.protocolV1Features.label = 'Mutated';
89
+
90
+ expect(state.capabilities).toEqual([1]);
91
+ expect(state.verification?.firmwareBuildId).toBe('build-1');
92
+ expect(state.raw.protocolV1Features?.label).toBe('Original');
93
+ });
94
+ });
@@ -0,0 +1,115 @@
1
+ import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
+
3
+ import { DeviceStateStore, createEmptyDeviceState } from '../src/device/DeviceStateStore';
4
+
5
+ describe('DeviceStateStore', () => {
6
+ test('merges patches, ignores undefined and recomputes displayName', () => {
7
+ const store = new DeviceStateStore(
8
+ createEmptyDeviceState({
9
+ deviceType: EDeviceType.Pro2,
10
+ firmwareType: EFirmwareType.Universal,
11
+ })
12
+ );
13
+
14
+ store.update({ identity: { bleName: 'Pro2 1234', label: null } }, 'initialize');
15
+ const result = store.update(
16
+ { identity: { label: 'My Wallet', bleName: undefined } },
17
+ 'apply-settings'
18
+ );
19
+
20
+ expect(result.state.identity).toMatchObject({
21
+ label: 'My Wallet',
22
+ bleName: 'Pro2 1234',
23
+ displayName: 'My Wallet',
24
+ });
25
+ expect(result.revision).toBe(2);
26
+ expect(result.changedKeys).toContain('identity.label');
27
+ expect(result.changedKeys).toContain('identity.displayName');
28
+ });
29
+
30
+ test('does not advance revision when the patch has no real changes', () => {
31
+ const store = new DeviceStateStore(createEmptyDeviceState());
32
+ const first = store.update({ status: { unlocked: null } }, 'device-status');
33
+ const second = store.update({ status: { unlocked: null } }, 'device-status');
34
+
35
+ expect(first.revision).toBe(0);
36
+ expect(second.revision).toBe(0);
37
+ expect(second.changedKeys).toEqual([]);
38
+ });
39
+
40
+ test('stores the wire protocol version independently from the protocol family', () => {
41
+ const store = new DeviceStateStore(createEmptyDeviceState());
42
+
43
+ const result = store.update({ protocol: 'V2', protocolVersion: 7 }, 'device-info');
44
+
45
+ expect(result.state.protocol).toBe('V2');
46
+ expect(result.state.protocolVersion).toBe(7);
47
+ expect(result.changedKeys).toContain('protocolVersion');
48
+ });
49
+
50
+ test('clears only ephemeral session state', () => {
51
+ const store = new DeviceStateStore(createEmptyDeviceState());
52
+ store.update(
53
+ {
54
+ identity: { label: 'Kept' },
55
+ session: { sessionId: 'session-1', passphraseState: 'state-1' },
56
+ },
57
+ 'initialize'
58
+ );
59
+
60
+ const result = store.clearSession('transport-reconnect');
61
+
62
+ expect(result?.state.identity.label).toBe('Kept');
63
+ expect(result?.state.session).toBeUndefined();
64
+ });
65
+
66
+ test('merges raw protocol sources instead of replacing the previous source', () => {
67
+ const store = new DeviceStateStore(createEmptyDeviceState());
68
+ const deviceInfo = { protocol_version: 2, hw: { serial_no: 'SERIAL-1' } };
69
+ const deviceStatus = { device_id: 'DEVICE-1', unlocked: true };
70
+
71
+ store.update({ raw: { protocolV2DeviceInfo: deviceInfo } }, 'device-info');
72
+ const result = store.update({ raw: { protocolV2DeviceStatus: deviceStatus } }, 'device-status');
73
+
74
+ expect(result.state.raw).toEqual({
75
+ protocolV2DeviceInfo: deviceInfo,
76
+ protocolV2DeviceStatus: deviceStatus,
77
+ });
78
+ });
79
+
80
+ test('removes only the explicitly cleared raw protocol source', () => {
81
+ const store = new DeviceStateStore(createEmptyDeviceState());
82
+ const deviceInfo = { protocol_version: 2, hw: { serial_no: 'SERIAL-1' } };
83
+ const deviceStatus = { device_id: 'DEVICE-1', unlocked: true };
84
+
85
+ store.update(
86
+ {
87
+ raw: {
88
+ protocolV2DeviceInfo: deviceInfo,
89
+ protocolV2DeviceStatus: deviceStatus,
90
+ },
91
+ },
92
+ 'initialize'
93
+ );
94
+ const result = store.update({ raw: { protocolV2DeviceStatus: null } }, 'device-info');
95
+
96
+ expect(result.state.raw).toEqual({
97
+ protocolV2DeviceInfo: deviceInfo,
98
+ });
99
+ });
100
+
101
+ test.each([
102
+ [EDeviceType.Classic1s, 'OneKey Classic 1S'],
103
+ [EDeviceType.ClassicPure, 'OneKey Classic 1S Pure'],
104
+ [EDeviceType.Touch, 'OneKey Touch'],
105
+ [EDeviceType.Pro, 'OneKey Pro'],
106
+ [EDeviceType.Pro2, 'OneKey Pro 2'],
107
+ ] as const)(
108
+ 'uses the product display name for %s when label and BLE name are absent',
109
+ (type, name) => {
110
+ const state = createEmptyDeviceState({ deviceType: type });
111
+
112
+ expect(state.identity.displayName).toBe(name);
113
+ }
114
+ );
115
+ });
@@ -3,6 +3,8 @@ import {
3
3
  DeviceWalletSessionStore,
4
4
  deviceWalletSessionStore,
5
5
  } from '../src/device/DeviceWalletSessionStore';
6
+ import { Device } from '../src/device/Device';
7
+ import { DevicePool } from '../src/device/DevicePool';
6
8
  import { createCoreApi } from '../src/inject';
7
9
 
8
10
  jest.mock('../src/data/config', () => ({
@@ -73,6 +75,50 @@ describe('DeviceWalletSessionStore', () => {
73
75
  describe('ClearSessionCache', () => {
74
76
  beforeEach(() => {
75
77
  deviceWalletSessionStore.clear();
78
+ DevicePool.devicesCache = {};
79
+ });
80
+
81
+ afterEach(() => {
82
+ DevicePool.devicesCache = {};
83
+ });
84
+
85
+ test('clears matching sessions from both runtime stores', async () => {
86
+ const matchingDevice = Device.fromDescriptor({ id: 'one', path: 'one' } as never);
87
+ matchingDevice.updateState(
88
+ {
89
+ protocol: 'V1',
90
+ identity: { deviceId: 'device-1' },
91
+ session: { sessionId: 'session-a', passphraseState: 'hidden-a' },
92
+ },
93
+ 'initialize'
94
+ );
95
+ const otherDevice = Device.fromDescriptor({ id: 'two', path: 'two' } as never);
96
+ otherDevice.updateState(
97
+ {
98
+ protocol: 'V1',
99
+ identity: { deviceId: 'device-2' },
100
+ session: { sessionId: 'session-b', passphraseState: 'hidden-a' },
101
+ },
102
+ 'initialize'
103
+ );
104
+ DevicePool.devicesCache = { one: matchingDevice, two: otherDevice };
105
+ deviceWalletSessionStore.set('device-1', 'hidden-a', 'session-a');
106
+
107
+ const method = new ClearSessionCache({
108
+ payload: {
109
+ method: 'clearSessionCache',
110
+ deviceId: 'device-1',
111
+ passphraseState: 'hidden-a',
112
+ },
113
+ });
114
+ method.init();
115
+ await method.run();
116
+
117
+ expect((matchingDevice.state as any)?.session).toBeUndefined();
118
+ expect((otherDevice.state as any)?.session).toEqual({
119
+ sessionId: 'session-b',
120
+ passphraseState: 'hidden-a',
121
+ });
76
122
  });
77
123
 
78
124
  test('clears one wallet without using a device', async () => {
@@ -1,7 +1,10 @@
1
1
  import { EDeviceType } from '@onekeyfe/hd-shared';
2
2
 
3
3
  import DataManager from '../src/data-manager/DataManager';
4
- import { getSupportProtocolV1MessageSchema } from '../src/utils/deviceFeaturesUtils';
4
+ import {
5
+ getSupportProtocolV1MessageSchema,
6
+ supportInputPinOnSoftware,
7
+ } from '../src/utils/deviceFeaturesUtils';
5
8
 
6
9
  import type { Features } from '../src/types';
7
10
 
@@ -24,4 +27,13 @@ describe('getSupportProtocolV1MessageSchema', () => {
24
27
  expect(result.protocolV1MessageSchema).toBe('v1CurrentSchema');
25
28
  expect(result.messages).toBe(DataManager.messages.v1CurrentSchema);
26
29
  });
30
+
31
+ test('Pro2 never supports entering PIN in software', () => {
32
+ const features = {
33
+ deviceType: EDeviceType.Pro2,
34
+ firmwareVersion: '9.9.9',
35
+ } as Features;
36
+
37
+ expect(supportInputPinOnSoftware(features)).toEqual({ support: false });
38
+ });
27
39
  });
@@ -0,0 +1,54 @@
1
+ import { EDeviceType } from '@onekeyfe/hd-shared';
2
+
3
+ import {
4
+ getAutoLockOptions,
5
+ getAutoShutDownOptions,
6
+ getLanguageConfig,
7
+ } from '../src/utils/deviceSettings';
8
+
9
+ describe('Pro 2 device settings options', () => {
10
+ test('returns the firmware-supported BCP-47 languages', () => {
11
+ expect(getLanguageConfig(EDeviceType.Pro2)).toEqual([
12
+ { code: 'en-Latn-US', label: 'English' },
13
+ { code: 'zh-Hans-CN', label: '简体中文' },
14
+ { code: 'zh-Hant-HK', label: '繁體中文(香港)' },
15
+ { code: 'zh-Hant-TW', label: '繁體中文(台灣)' },
16
+ { code: 'ja-Jpan-JP', label: '日本語' },
17
+ { code: 'ko-Kore-KR', label: '한국어' },
18
+ { code: 'fr-Latn-FR', label: 'Français' },
19
+ { code: 'de-Latn-DE', label: 'Deutsch' },
20
+ { code: 'ru-Cyrl-RU', label: 'Русский' },
21
+ { code: 'es-Latn-ES', label: 'Español' },
22
+ { code: 'it-Latn-IT', label: 'Italiano' },
23
+ { code: 'pt-Latn-BR', label: 'Portuguese (Brazil)' },
24
+ { code: 'vi-Latn-VN', label: 'Tiếng Việt' },
25
+ { code: 'tr-Latn-TR', label: 'Türkçe' },
26
+ { code: 'id-Latn-ID', label: 'Bahasa Indonesia' },
27
+ { code: 'fil-Latn-PH', label: 'Filipino' },
28
+ { code: 'uk-Cyrl-UA', label: 'Українська' },
29
+ ]);
30
+ });
31
+
32
+ test('returns the firmware-supported auto-lock delays', () => {
33
+ expect(getAutoLockOptions(EDeviceType.Pro2)).toEqual([
34
+ { seconds: 30, minute: 0, hour: 0, day: 0 },
35
+ { seconds: 0, minute: 1, hour: 0, day: 0 },
36
+ { seconds: 0, minute: 2, hour: 0, day: 0 },
37
+ { seconds: 0, minute: 5, hour: 0, day: 0 },
38
+ { seconds: 0, minute: 10, hour: 0, day: 0 },
39
+ { seconds: 0, minute: 30, hour: 0, day: 0 },
40
+ { seconds: 0, minute: 0, hour: 0, day: 0 },
41
+ ]);
42
+ });
43
+
44
+ test('returns the firmware-supported auto-shutdown delays', () => {
45
+ expect(getAutoShutDownOptions(EDeviceType.Pro2)).toEqual([
46
+ { seconds: 0, minute: 1, hour: 0, day: 0 },
47
+ { seconds: 0, minute: 2, hour: 0, day: 0 },
48
+ { seconds: 0, minute: 5, hour: 0, day: 0 },
49
+ { seconds: 0, minute: 10, hour: 0, day: 0 },
50
+ { seconds: 0, minute: 30, hour: 0, day: 0 },
51
+ { seconds: 0, minute: 0, hour: 0, day: 0 },
52
+ ]);
53
+ });
54
+ });