@onekeyfe/hd-core 1.2.0-alpha.54 → 1.2.0-alpha.55

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 (256) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +231 -0
  2. package/__tests__/DeviceCommands.test.ts +45 -0
  3. package/__tests__/DeviceEventRegistration.test.ts +6 -0
  4. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +198 -3
  5. package/__tests__/connectSettings.test.ts +5 -47
  6. package/__tests__/device-lifecycle-events.test.ts +105 -3
  7. package/__tests__/deviceUploadNft.test.ts +293 -0
  8. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  9. package/__tests__/get-device-state.test.ts +132 -12
  10. package/__tests__/homescreen.test.ts +41 -1
  11. package/__tests__/kaspaSignTransaction.test.ts +3 -26
  12. package/__tests__/logBlockEvent.test.ts +1 -1
  13. package/__tests__/method-protocol-support.test.ts +19 -0
  14. package/__tests__/open-wallet-session.test.ts +568 -37
  15. package/__tests__/pro2Nft.test.ts +108 -0
  16. package/__tests__/protocol-binding.test.ts +89 -0
  17. package/__tests__/protocol-v2-resources.test.ts +286 -0
  18. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  19. package/__tests__/protocol-v2-unlock-policy.test.ts +201 -35
  20. package/__tests__/protocol-v2.test.ts +1350 -1012
  21. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  22. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  23. package/__tests__/search-devices.test.ts +12 -3
  24. package/__tests__/uiProgressThrottle.test.ts +74 -0
  25. package/__tests__/uiPromiseRegistry.test.ts +86 -0
  26. package/dist/api/BaseMethod.d.ts +3 -1
  27. package/dist/api/BaseMethod.d.ts.map +1 -1
  28. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  29. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  30. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  31. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  32. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  33. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  34. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  35. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  36. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  37. package/dist/api/FirmwareUpdateV4.d.ts +11 -25
  38. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  39. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  40. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  41. package/dist/api/SearchDevices.d.ts.map +1 -1
  42. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  43. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  44. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  45. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  46. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  47. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +0 -9
  48. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  49. package/dist/api/firmware/getBinary.d.ts +0 -7
  50. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  51. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  52. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  53. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  54. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  55. package/dist/api/helpers/protocolV2FileWrite.d.ts +2 -0
  56. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  57. package/dist/api/index.d.ts +2 -0
  58. package/dist/api/index.d.ts.map +1 -1
  59. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  60. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  61. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  62. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  63. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  64. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  65. package/dist/core/deviceEventRegistration.d.ts +2 -0
  66. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  67. package/dist/core/index.d.ts.map +1 -1
  68. package/dist/core/uiPromiseRegistry.d.ts +4 -0
  69. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  70. package/dist/data-manager/DataManager.d.ts +4 -2
  71. package/dist/data-manager/DataManager.d.ts.map +1 -1
  72. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  73. package/dist/device/Device.d.ts +29 -6
  74. package/dist/device/Device.d.ts.map +1 -1
  75. package/dist/device/DeviceCommands.d.ts +4 -4
  76. package/dist/device/DeviceCommands.d.ts.map +1 -1
  77. package/dist/device/DevicePool.d.ts.map +1 -1
  78. package/dist/events/device.d.ts +4 -0
  79. package/dist/events/device.d.ts.map +1 -1
  80. package/dist/events/logBlockEvent.d.ts.map +1 -1
  81. package/dist/events/ui-promise.d.ts +4 -2
  82. package/dist/events/ui-promise.d.ts.map +1 -1
  83. package/dist/events/ui-request.d.ts +31 -2
  84. package/dist/events/ui-request.d.ts.map +1 -1
  85. package/dist/events/ui-response.d.ts +10 -2
  86. package/dist/events/ui-response.d.ts.map +1 -1
  87. package/dist/index.d.ts +214 -231
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +2616 -3527
  90. package/dist/inject.d.ts +6 -1
  91. package/dist/inject.d.ts.map +1 -1
  92. package/dist/lowLevelInject.d.ts.map +1 -1
  93. package/dist/protocols/protocol-v2/features.d.ts +0 -4
  94. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  95. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  96. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  97. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  98. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  99. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  100. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  101. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  102. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  103. package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
  104. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  105. package/dist/topLevelInject.d.ts.map +1 -1
  106. package/dist/types/api/checkAllFirmwareRelease.d.ts +2 -4
  107. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  108. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  109. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  110. package/dist/types/api/deviceUnlock.d.ts +5 -1
  111. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  112. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  113. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  114. package/dist/types/api/export.d.ts +2 -4
  115. package/dist/types/api/export.d.ts.map +1 -1
  116. package/dist/types/api/firmwareUpdate.d.ts +2 -70
  117. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  118. package/dist/types/api/index.d.ts +7 -10
  119. package/dist/types/api/index.d.ts.map +1 -1
  120. package/dist/types/api/protocolV2.d.ts +3 -0
  121. package/dist/types/api/protocolV2.d.ts.map +1 -1
  122. package/dist/types/device.d.ts.map +1 -1
  123. package/dist/types/params.d.ts +1 -0
  124. package/dist/types/params.d.ts.map +1 -1
  125. package/dist/types/settings.d.ts +22 -13
  126. package/dist/types/settings.d.ts.map +1 -1
  127. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  128. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  129. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  130. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  131. package/dist/utils/homescreen.d.ts +4 -0
  132. package/dist/utils/homescreen.d.ts.map +1 -1
  133. package/dist/utils/index.d.ts +1 -1
  134. package/dist/utils/index.d.ts.map +1 -1
  135. package/dist/utils/patch.d.ts +1 -1
  136. package/dist/utils/patch.d.ts.map +1 -1
  137. package/dist/utils/pro2Nft.d.ts +31 -0
  138. package/dist/utils/pro2Nft.d.ts.map +1 -0
  139. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  140. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  141. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  142. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  143. package/package.json +4 -4
  144. package/src/api/BaseMethod.ts +8 -10
  145. package/src/api/CheckAllFirmwareRelease.ts +54 -57
  146. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  147. package/src/api/FirmwareUpdate.ts +7 -15
  148. package/src/api/FirmwareUpdateV2.ts +126 -316
  149. package/src/api/FirmwareUpdateV3.ts +63 -265
  150. package/src/api/FirmwareUpdateV4.ts +569 -965
  151. package/src/api/GetPassphraseState.ts +7 -1
  152. package/src/api/OpenWalletSession.ts +70 -17
  153. package/src/api/SearchDevices.ts +3 -1
  154. package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
  155. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +34 -11
  156. package/src/api/device/DeviceUnlock.ts +8 -3
  157. package/src/api/device/DeviceUpdateBootloader.ts +6 -122
  158. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +16 -83
  159. package/src/api/firmware/updateBootloader.ts +4 -19
  160. package/src/api/firmware/uploadFirmware.ts +39 -144
  161. package/src/api/helpers/protocolV2FileWrite.ts +11 -4
  162. package/src/api/index.ts +2 -0
  163. package/src/api/kaspa/KaspaSignTransaction.ts +1 -12
  164. package/src/api/protocol-v2/DeviceUploadNft.ts +189 -0
  165. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  166. package/src/api/protocol-v2/helpers.ts +1 -0
  167. package/src/core/deviceEventRegistration.ts +4 -0
  168. package/src/core/index.ts +167 -83
  169. package/src/core/uiPromiseRegistry.ts +41 -0
  170. package/src/data/messages/messages-protocol-v2.json +25 -0
  171. package/src/data-manager/DataManager.ts +71 -39
  172. package/src/data-manager/connectSettings.ts +0 -11
  173. package/src/device/Device.ts +259 -43
  174. package/src/device/DeviceCommands.ts +7 -1
  175. package/src/device/DevicePool.ts +7 -2
  176. package/src/events/device.ts +4 -0
  177. package/src/events/logBlockEvent.ts +1 -0
  178. package/src/events/ui-promise.ts +4 -2
  179. package/src/events/ui-request.ts +36 -2
  180. package/src/events/ui-response.ts +12 -2
  181. package/src/index.ts +0 -5
  182. package/src/inject.ts +60 -24
  183. package/src/lowLevelInject.ts +7 -8
  184. package/src/protocols/protocol-v2/features.ts +10 -7
  185. package/src/protocols/protocol-v2/resources.ts +359 -0
  186. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  187. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  188. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +118 -0
  189. package/src/protocols/protocol-v2/walletSession.ts +214 -36
  190. package/src/topLevelInject.ts +7 -8
  191. package/src/types/api/checkAllFirmwareRelease.ts +2 -4
  192. package/src/types/api/detectDeviceConnectProtocol.ts +7 -0
  193. package/src/types/api/deviceUnlock.ts +12 -1
  194. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  195. package/src/types/api/export.ts +1 -18
  196. package/src/types/api/firmwareUpdate.ts +3 -76
  197. package/src/types/api/index.ts +13 -14
  198. package/src/types/api/protocolV2.ts +13 -0
  199. package/src/types/device.ts +3 -0
  200. package/src/types/params.ts +7 -1
  201. package/src/types/settings.ts +42 -13
  202. package/src/utils/deviceFeaturesCompat.ts +0 -6
  203. package/src/utils/deviceFeaturesUtils.ts +8 -0
  204. package/src/utils/homescreen.ts +32 -6
  205. package/src/utils/index.ts +6 -1
  206. package/src/utils/pro2Nft.ts +142 -0
  207. package/src/utils/pro2Wallpaper.ts +35 -8
  208. package/src/utils/uiProgressThrottle.ts +63 -0
  209. package/__tests__/device-initialize-timeout.test.ts +0 -56
  210. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  211. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  212. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  213. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  214. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  215. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  216. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -294
  217. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  218. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  219. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  220. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  221. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  222. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  223. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  224. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  225. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  226. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  227. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  228. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  229. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  230. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  231. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  232. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  233. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  234. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  235. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  236. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  237. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  238. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  239. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  240. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  241. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  242. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  243. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  244. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  245. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  246. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  247. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  248. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  249. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  250. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -415
  251. package/src/api/firmware/progressThrottle.ts +0 -44
  252. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  253. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  254. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  255. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  256. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  deviceGetOnboardingStatus,
3
3
  deviceReboot,
4
+ deviceUploadNft,
4
5
  deviceUploadWallpaper,
5
6
  testProtocolV2Ping,
6
7
  uploadPortfolio,
@@ -18,6 +19,7 @@ import type { checkBootloaderRelease } from './checkBootloaderRelease';
18
19
  import type { checkAllFirmwareRelease } from './checkAllFirmwareRelease';
19
20
  import type { checkFirmwareTypeAvailable } from './checkFirmwareTypeAvailable';
20
21
  import type { searchDevices } from './searchDevices';
22
+ import type { detectDeviceConnectProtocol } from './detectDeviceConnectProtocol';
21
23
  import type { getFeatures } from './getFeatures';
22
24
  import type { getDeviceState } from './getDeviceState';
23
25
  import type { getOnekeyFeatures } from './getOnekeyFeatures';
@@ -30,15 +32,7 @@ import type {
30
32
  firmwareUpdateV2,
31
33
  firmwareUpdateV3,
32
34
  firmwareUpdateV4,
33
- getFirmwareUpdateHostBindingGeneration,
34
- registerFirmwareUpdateHostBinding,
35
- unregisterFirmwareUpdateHostBinding,
36
35
  } from './firmwareUpdate';
37
- import type { getFirmwareUpdateCapabilities } from './firmwareUpdateCapabilities';
38
- import type {
39
- prepareFirmwareUpdatePlan,
40
- validateFirmwareUpdatePreparedPlan,
41
- } from './firmwareUpdatePreparedPlan';
42
36
  import type { promptWebDeviceAccess } from './promptWebDeviceAccess';
43
37
  import type { deviceReset } from './deviceReset';
44
38
  import type { deviceRecovery } from './deviceRecovery';
@@ -152,6 +146,7 @@ import type { benfenSignMessage } from './benfenSignMessage';
152
146
  import type { neoGetAddress } from './neoGetAddress';
153
147
  import type { neoSignTransaction } from './neoSignTransaction';
154
148
  import type { ConnectSettings } from '../settings';
149
+ import type { HardwareConnectProtocol } from '@onekeyfe/hd-shared';
155
150
 
156
151
  export * from './export';
157
152
  export type { DeviceStateScope, GetDeviceStateParams } from './getDeviceState';
@@ -176,6 +171,14 @@ export type CoreApi = {
176
171
  removeAllListeners: typeof removeAllListeners;
177
172
  dispose: () => void | Promise<void>;
178
173
  call: (params: any) => Promise<any>;
174
+ /**
175
+ * Bind a protocol that has already been verified for one transport endpoint.
176
+ * Every later SDK call for the same connectId uses it as a strict expectation.
177
+ */
178
+ setDeviceConnectProtocol: (
179
+ connectId: string,
180
+ connectProtocol: HardwareConnectProtocol | undefined
181
+ ) => void;
179
182
  uiResponse: typeof uiResponse;
180
183
  cancel: (connectId?: string) => void;
181
184
  updateSettings: typeof updateSettings;
@@ -203,6 +206,7 @@ export type CoreApi = {
203
206
  * Device function
204
207
  */
205
208
  searchDevices: typeof searchDevices;
209
+ detectDeviceConnectProtocol: typeof detectDeviceConnectProtocol;
206
210
  promptWebDeviceAccess: typeof promptWebDeviceAccess;
207
211
  getFeatures: typeof getFeatures;
208
212
  getDeviceState: typeof getDeviceState;
@@ -235,12 +239,6 @@ export type CoreApi = {
235
239
  firmwareUpdateV2: typeof firmwareUpdateV2;
236
240
  firmwareUpdateV3: typeof firmwareUpdateV3;
237
241
  firmwareUpdateV4: typeof firmwareUpdateV4;
238
- registerFirmwareUpdateHostBinding: typeof registerFirmwareUpdateHostBinding;
239
- unregisterFirmwareUpdateHostBinding: typeof unregisterFirmwareUpdateHostBinding;
240
- getFirmwareUpdateHostBindingGeneration: typeof getFirmwareUpdateHostBindingGeneration;
241
- getFirmwareUpdateCapabilities: typeof getFirmwareUpdateCapabilities;
242
- prepareFirmwareUpdatePlan: typeof prepareFirmwareUpdatePlan;
243
- validateFirmwareUpdatePreparedPlan: typeof validateFirmwareUpdatePreparedPlan;
244
242
  cipherKeyValue: typeof cipherKeyValue;
245
243
 
246
244
  /**
@@ -248,6 +246,7 @@ export type CoreApi = {
248
246
  */
249
247
  deviceReboot: typeof deviceReboot;
250
248
  deviceGetOnboardingStatus: typeof deviceGetOnboardingStatus;
249
+ deviceUploadNft: typeof deviceUploadNft;
251
250
  deviceUploadWallpaper: typeof deviceUploadWallpaper;
252
251
  uploadPortfolio: typeof uploadPortfolio;
253
252
 
@@ -5,6 +5,10 @@ import type {
5
5
  DeviceUploadWallpaperParams,
6
6
  DeviceUploadWallpaperResponse,
7
7
  } from '../../api/protocol-v2/DeviceUploadWallpaper';
8
+ import type {
9
+ DeviceUploadNftParams,
10
+ DeviceUploadNftResponse,
11
+ } from '../../api/protocol-v2/DeviceUploadNft';
8
12
 
9
13
  // Re-export implementation parameter types as the single source of truth.
10
14
  export type { DeviceRebootParams, RebootTypeInput } from '../../api/protocol-v2/helpers';
@@ -12,6 +16,10 @@ export type {
12
16
  DeviceUploadWallpaperParams,
13
17
  DeviceUploadWallpaperResponse,
14
18
  } from '../../api/protocol-v2/DeviceUploadWallpaper';
19
+ export type {
20
+ DeviceUploadNftParams,
21
+ DeviceUploadNftResponse,
22
+ } from '../../api/protocol-v2/DeviceUploadNft';
15
23
 
16
24
  // ── Shared response shapes (Protocol V2 file system) ────────────────────
17
25
 
@@ -52,6 +60,11 @@ export declare function deviceUploadWallpaper(
52
60
  params: CommonParams & DeviceUploadWallpaperParams
53
61
  ): Response<DeviceUploadWallpaperResponse>;
54
62
 
63
+ export declare function deviceUploadNft(
64
+ connectId: string,
65
+ params: CommonParams & DeviceUploadNftParams
66
+ ): Response<DeviceUploadNftResponse>;
67
+
55
68
  export declare function uploadPortfolio(
56
69
  connectId: string,
57
70
  params: {
@@ -210,7 +210,9 @@ export type DeviceStateStatus = {
210
210
  recoveryMode: boolean | null;
211
211
  passphraseProtection: boolean | null;
212
212
  pinProtection: boolean | null;
213
+ /** `attach_to_pin_enabled`:是否至少存在一个 Attach PIN 绑定。 */
213
214
  attachToPinEnabled: boolean | null;
215
+ /** `unlocked_by_attach_to_pin`:本次解锁是否由 Attach PIN 完成。 */
214
216
  unlockedAttachPin: boolean | null;
215
217
  };
216
218
 
@@ -321,6 +323,7 @@ export type NormalizedFeatures = {
321
323
  sdProtection: boolean | null;
322
324
  wipeCodeProtection: boolean | null;
323
325
  passphraseAlwaysOnDevice: boolean | null;
326
+ /** `attach_to_pin_enabled`:是否至少存在一个 Attach PIN 绑定。 */
324
327
  attachToPinEnabled?: boolean | null;
325
328
  safetyChecks: Enum_SafetyCheckLevel | null;
326
329
  autoLockDelayMs: number | null;
@@ -61,9 +61,15 @@ export interface CommonParams {
61
61
 
62
62
  /**
63
63
  * Strictly expected transport protocol. The SDK actively verifies this value and
64
- * rejects a mismatch. If omitted, a cached protocol only influences probe order.
64
+ * rejects a mismatch. After a successful probe, the cached protocol is also strict.
65
65
  */
66
66
  connectProtocol?: HardwareConnectProtocol;
67
+
68
+ /**
69
+ * Ignore a previously bound protocol for this call and actively detect the
70
+ * protocol again. Intended for the first verified connection or explicit recovery.
71
+ */
72
+ forceProtocolDetection?: boolean;
67
73
  }
68
74
 
69
75
  export type Params<T> = CommonParams & T & { bundle?: undefined };
@@ -31,8 +31,6 @@ export type ConnectSettings = {
31
31
  timestamp: number;
32
32
  isFrame?: boolean;
33
33
  preRelease?: boolean;
34
- firmwareManifestMode?: 'sdk-managed' | 'external-only';
35
- preloadedConfig?: RemoteConfigResponse;
36
34
  fetchConfig?: boolean;
37
35
  extension?: string;
38
36
  configFetcher?: (url: string) => Promise<RemoteConfigResponse | null>;
@@ -60,18 +58,55 @@ export type IProtocolV2FirmwareComponent = {
60
58
  fingerprint?: string;
61
59
  /** OKPP payload hash, used to detect same-version package changes. */
62
60
  payloadHash?: string;
63
- expectedSize?: number;
64
61
  version?: IVersionArray;
65
62
  };
66
63
 
64
+ export type IProtocolV2ResourceType =
65
+ | 'images'
66
+ | 'animation'
67
+ | 'wallpaper'
68
+ | 'translations'
69
+ | 'roobert'
70
+ | 'noto';
71
+
72
+ /** Pro2 RES package descriptor. Hashes identify content without a human-managed version. */
73
+ export type IProtocolV2Resource = {
74
+ type: IProtocolV2ResourceType;
75
+ url: string;
76
+ /** Complete okpkg file size in bytes. */
77
+ size: number;
78
+ /** SHA-256 of the complete okpkg file. */
79
+ fileHash: string;
80
+ /** SHA3-512 header_hash from the signed okpkg header. */
81
+ headerHash: string;
82
+ };
83
+
84
+ /** Optional Pro2 startup-resource CRATE installed by the bootloader updater. */
85
+ export type IProtocolV2BootResources = {
86
+ required: false;
87
+ target: 'CRATE';
88
+ url: string;
89
+ /** Complete CRATE file size in bytes. */
90
+ size: number;
91
+ /** SHA-256 of the complete CRATE file. */
92
+ fileHash: string;
93
+ /** SHA3-512 payload_hash from the signed CRATE header. */
94
+ payloadHash: string;
95
+ /** SHA3-512 header_hash from the signed CRATE header. */
96
+ headerHash: string;
97
+ };
98
+
99
+ export type IProtocolV2Resources = {
100
+ stable: IProtocolV2Resource[];
101
+ boot?: IProtocolV2BootResources;
102
+ };
103
+
67
104
  /** Pro2 RESC bundle okpkg descriptor for incremental FileWrite synchronization. */
68
105
  export type IProtocolV2ResourceBundle = {
69
106
  /** Bundle name, such as images, animation, translations, or fonts_roobert. */
70
107
  name: string;
71
108
  /** Download URL. */
72
109
  url: string;
73
- fingerprint?: string;
74
- expectedSize?: number;
75
110
  /** Device target path, such as vol0:/bundles/images/images.okpkg. */
76
111
  devicePath: string;
77
112
  /** okpkg payload_version used to skip matching content after FileRead. */
@@ -93,16 +128,10 @@ export type IFirmwareReleaseInfo = {
93
128
  firmwareType?: EFirmwareType;
94
129
  /** Firmware UI resource */
95
130
  resource?: string;
96
- resourceFingerprint?: string;
97
- resourceExpectedSize?: number;
98
131
  /** Firmware full UI resource */
99
132
  fullResource?: string;
100
- fullResourceFingerprint?: string;
101
- fullResourceExpectedSize?: number;
102
133
  fullResourceRange?: string[];
103
134
  bootloaderResource?: string;
104
- bootloaderFingerprint?: string;
105
- bootloaderExpectedSize?: number;
106
135
  bootloaderVersion?: IVersionArray;
107
136
  displayBootloaderVersion?: IVersionArray;
108
137
  bootloaderRelatedFirmwareVersion?: IVersionArray;
@@ -115,7 +144,6 @@ export type IFirmwareReleaseInfo = {
115
144
  [k in ILocale]: string;
116
145
  };
117
146
  fingerprint: string;
118
- expectedSize?: number;
119
147
  version: IVersionArray;
120
148
  changelog: {
121
149
  [k in ILocale]: string;
@@ -131,7 +159,6 @@ export type IBLEFirmwareReleaseInfo = {
131
159
  webUpdate: string;
132
160
  fingerprint: string;
133
161
  fingerprintWeb: string;
134
- expectedSize?: number;
135
162
  version: IVersionArray;
136
163
  changelog: {
137
164
  [k in ILocale]: string;
@@ -149,6 +176,8 @@ export type DeviceTypeMap = {
149
176
  'firmware-v8'?: IFirmwareReleaseInfo[];
150
177
  'firmware-btc-v8'?: IFirmwareReleaseInfo[];
151
178
  ble: IBLEFirmwareReleaseInfo[];
179
+ /** Independent Pro2 resource release configuration. */
180
+ resources?: IProtocolV2Resources;
152
181
  };
153
182
  };
154
183
 
@@ -37,12 +37,6 @@ export const resolveDeviceSerialNo = (features?: DeviceFeaturesInput): string =>
37
37
  );
38
38
  };
39
39
 
40
- export const resolveDeviceBootloaderMode = (features?: DeviceFeaturesInput): boolean => {
41
- if (!features) return false;
42
- const compatible = asCompatibleFeatures(features);
43
- return (compatible.bootloaderMode ?? compatible.bootloader_mode) === true;
44
- };
45
-
46
40
  export const resolveDeviceType = (features?: DeviceFeaturesInput): IDeviceType => {
47
41
  if (!features || typeof features !== 'object') {
48
42
  return EDeviceType.Unknown;
@@ -106,6 +106,8 @@ export const getPassphraseStateWithRefreshDeviceInfo = async (
106
106
  expectPassphraseState?: string;
107
107
  onlyMainPin?: boolean;
108
108
  initSession?: boolean;
109
+ deriveCardano?: boolean;
110
+ rejectAttachPinForMainWallet?: boolean;
109
111
  }
110
112
  ) => {
111
113
  if (device.isProtocolV2()) {
@@ -121,6 +123,8 @@ export const getPassphraseStateWithRefreshDeviceInfo = async (
121
123
  initSession: options?.initSession,
122
124
  expectedPassphraseState: options?.expectPassphraseState,
123
125
  onlyMainPin: options?.onlyMainPin,
126
+ deriveCardano: options?.deriveCardano,
127
+ rejectAttachPinForMainWallet: options?.rejectAttachPinForMainWallet,
124
128
  });
125
129
  }
126
130
 
@@ -185,6 +189,8 @@ export const getPassphraseState = async (
185
189
  expectPassphraseState?: string;
186
190
  onlyMainPin?: boolean;
187
191
  initSession?: boolean;
192
+ deriveCardano?: boolean;
193
+ rejectAttachPinForMainWallet?: boolean;
188
194
  }
189
195
  ): Promise<{
190
196
  passphraseState: string | undefined;
@@ -205,6 +211,8 @@ export const getPassphraseState = async (
205
211
  initSession: options?.initSession,
206
212
  expectedPassphraseState: options?.expectPassphraseState,
207
213
  onlyMainPin: options?.onlyMainPin,
214
+ deriveCardano: options?.deriveCardano,
215
+ rejectAttachPinForMainWallet: options?.rejectAttachPinForMainWallet,
208
216
  });
209
217
  }
210
218
 
@@ -4,6 +4,12 @@ import { EDeviceType } from '@onekeyfe/hd-shared';
4
4
  import { getDeviceType } from './deviceInfoUtils';
5
5
  import { getDeviceFirmwareVersion } from './deviceVersionUtils';
6
6
  import { PRO2_WALLPAPER_HEIGHT, PRO2_WALLPAPER_WIDTH } from './pro2Wallpaper';
7
+ import {
8
+ PRO2_NFT_IMAGE_HEIGHT,
9
+ PRO2_NFT_IMAGE_WIDTH,
10
+ PRO2_NFT_THUMBNAIL_HEIGHT,
11
+ PRO2_NFT_THUMBNAIL_WIDTH,
12
+ } from './pro2Nft';
7
13
 
8
14
  import type { Features, IDeviceType } from '../types';
9
15
 
@@ -302,6 +308,31 @@ type SizeConfig = {
302
308
  radius?: number;
303
309
  };
304
310
 
311
+ export const getNftSize = ({
312
+ deviceType,
313
+ thumbnail,
314
+ }: {
315
+ deviceType: IDeviceType;
316
+ thumbnail?: boolean;
317
+ }): SizeConfig | undefined => {
318
+ const sizes: Partial<Record<IDeviceType, { full: SizeConfig; thumbnail: SizeConfig }>> = {
319
+ touch: {
320
+ full: { width: 480, height: 800 },
321
+ thumbnail: { width: 238, height: 238 },
322
+ },
323
+ pro: {
324
+ full: { width: 480, height: 800 },
325
+ thumbnail: { width: 226, height: 226, radius: 40 },
326
+ },
327
+ pro2: {
328
+ full: { width: PRO2_NFT_IMAGE_WIDTH, height: PRO2_NFT_IMAGE_HEIGHT },
329
+ thumbnail: { width: PRO2_NFT_THUMBNAIL_WIDTH, height: PRO2_NFT_THUMBNAIL_HEIGHT },
330
+ },
331
+ };
332
+
333
+ return sizes[deviceType]?.[thumbnail ? 'thumbnail' : 'full'];
334
+ };
335
+
305
336
  export const getHomeScreenSize = ({
306
337
  deviceType,
307
338
  homeScreenType,
@@ -312,12 +343,7 @@ export const getHomeScreenSize = ({
312
343
  thumbnail?: boolean;
313
344
  }) => {
314
345
  if (deviceType === EDeviceType.Pro2) {
315
- return thumbnail
316
- ? undefined
317
- : {
318
- width: PRO2_WALLPAPER_WIDTH,
319
- height: PRO2_WALLPAPER_HEIGHT,
320
- };
346
+ return thumbnail ? undefined : { width: PRO2_WALLPAPER_WIDTH, height: PRO2_WALLPAPER_HEIGHT };
321
347
  }
322
348
 
323
349
  const sizes: Partial<
@@ -54,7 +54,12 @@ export { getHDPath, getScriptType, getOutputScriptType } from '../api/helpers/pa
54
54
 
55
55
  export const isBleConnect = (env: string) => env === 'react-native' || env === 'lowlevel';
56
56
 
57
- export { getHomeScreenHex, getHomeScreenDefaultList, getHomeScreenSize } from './homescreen';
57
+ export {
58
+ getHomeScreenHex,
59
+ getHomeScreenDefaultList,
60
+ getHomeScreenSize,
61
+ getNftSize,
62
+ } from './homescreen';
58
63
  export { encodePro2Wallpaper, PRO2_WALLPAPER_HEIGHT, PRO2_WALLPAPER_WIDTH } from './pro2Wallpaper';
59
64
  export type { Pro2WallpaperColorFormat } from './pro2Wallpaper';
60
65
 
@@ -0,0 +1,142 @@
1
+ import { blake2s } from '@noble/hashes/blake2s';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+
4
+ import { invalidParameter } from '../api/helpers/filesystemValidation';
5
+ import { encodePro2Image } from './pro2Wallpaper';
6
+
7
+ export const PRO2_NFT_IMAGE_WIDTH = 540;
8
+ export const PRO2_NFT_IMAGE_HEIGHT = 540;
9
+ export const PRO2_NFT_THUMBNAIL_WIDTH = 263;
10
+ export const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
11
+ export const PRO2_NFT_DIRECTORY = 'vol1:/nft';
12
+ export const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
13
+ export const PRO2_NFT_DEFAULT_PACE_MS = 0;
14
+ export const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15_000;
15
+ export const PRO2_NFT_MIN_CHUNK_SIZE = 64;
16
+ export const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
17
+ export const PRO2_NFT_MAX_ITEMS = 10;
18
+
19
+ export type Pro2NftImage = {
20
+ width: number;
21
+ height: number;
22
+ rgba: Uint8Array | ArrayBuffer;
23
+ };
24
+
25
+ export type Pro2NftBundle = {
26
+ basename: string;
27
+ image: Uint8Array;
28
+ thumbnail: Uint8Array;
29
+ metadata: Uint8Array;
30
+ };
31
+
32
+ const PRO2_NFT_BASENAME_PATTERN = /^nft-[0-9a-f]{8}-[1-9][0-9]*$/;
33
+
34
+ type Pro2NftFileType = 'image' | 'thumbnail' | 'metadata';
35
+
36
+ function parsePro2NftFile(listedPath: string):
37
+ | {
38
+ basename: string;
39
+ fileType: Pro2NftFileType;
40
+ }
41
+ | undefined {
42
+ const filename = listedPath.trim().split('/').at(-1);
43
+ if (!filename) return undefined;
44
+
45
+ let basename: string | undefined;
46
+ let fileType: Pro2NftFileType | undefined;
47
+ if (filename.endsWith('_m.bin')) {
48
+ basename = filename.slice(0, -'_m.bin'.length);
49
+ fileType = 'thumbnail';
50
+ } else if (filename.endsWith('.json')) {
51
+ basename = filename.slice(0, -'.json'.length);
52
+ fileType = 'metadata';
53
+ } else if (filename.endsWith('.bin')) {
54
+ basename = filename.slice(0, -'.bin'.length);
55
+ fileType = 'image';
56
+ }
57
+
58
+ return basename && fileType && PRO2_NFT_BASENAME_PATTERN.test(basename)
59
+ ? { basename, fileType }
60
+ : undefined;
61
+ }
62
+
63
+ export function getCompletePro2NftBasenames(childFiles?: string): Set<string> {
64
+ const filesByBasename = new Map<string, Set<Pro2NftFileType>>();
65
+
66
+ for (const listedPath of childFiles?.split('\n') ?? []) {
67
+ const file = parsePro2NftFile(listedPath);
68
+ if (file) {
69
+ const fileTypes = filesByBasename.get(file.basename) ?? new Set<Pro2NftFileType>();
70
+ fileTypes.add(file.fileType);
71
+ filesByBasename.set(file.basename, fileTypes);
72
+ }
73
+ }
74
+
75
+ return new Set(
76
+ [...filesByBasename.entries()]
77
+ .filter(([, fileTypes]) => fileTypes.size === 3)
78
+ .map(([basename]) => basename)
79
+ );
80
+ }
81
+
82
+ function utf8Length(value: string): number {
83
+ return new TextEncoder().encode(value).byteLength;
84
+ }
85
+
86
+ function assertImage(
87
+ name: string,
88
+ image: Pro2NftImage,
89
+ expectedWidth: number,
90
+ expectedHeight: number
91
+ ) {
92
+ if (image.width !== expectedWidth || image.height !== expectedHeight) {
93
+ throw invalidParameter(
94
+ `Pro2 NFT ${name} dimensions must be ${expectedWidth}x${expectedHeight}.`
95
+ );
96
+ }
97
+ if (!(image.rgba instanceof ArrayBuffer) && !ArrayBuffer.isView(image.rgba)) {
98
+ throw invalidParameter(`Parameter [${name}.rgba] must be an ArrayBuffer or Uint8Array.`);
99
+ }
100
+ }
101
+
102
+ export function buildPro2NftBundle(options: {
103
+ image: Pro2NftImage;
104
+ thumbnail: Pro2NftImage;
105
+ title: string;
106
+ subtitle: string;
107
+ timestampMs: number;
108
+ }): Pro2NftBundle {
109
+ const { image, thumbnail, title, subtitle, timestampMs } = options;
110
+ assertImage('image', image, PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT);
111
+ assertImage('thumbnail', thumbnail, PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT);
112
+ const titleLength = typeof title === 'string' ? utf8Length(title) : 0;
113
+ const subtitleLength =
114
+ typeof subtitle === 'string' ? utf8Length(subtitle) : Number.POSITIVE_INFINITY;
115
+ if (titleLength < 1 || titleLength > 63) {
116
+ throw invalidParameter('Pro2 NFT title must contain 1 to 63 UTF-8 bytes.');
117
+ }
118
+ if (subtitleLength > 95) {
119
+ throw invalidParameter('Pro2 NFT subtitle must contain at most 95 UTF-8 bytes.');
120
+ }
121
+ if (!Number.isSafeInteger(timestampMs) || timestampMs <= 0) {
122
+ throw invalidParameter('Parameter [timestampMs] must be a positive safe integer.');
123
+ }
124
+
125
+ const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
126
+ const encodedThumbnail = encodePro2Image({
127
+ ...thumbnail,
128
+ alphaMode: 'black-background',
129
+ }).data;
130
+ const metadata = new TextEncoder().encode(JSON.stringify({ title, subtitle }));
131
+ if (metadata.byteLength === 0 || metadata.byteLength > 512) {
132
+ throw invalidParameter('Pro2 NFT metadata must contain 1 to 512 UTF-8 bytes.');
133
+ }
134
+
135
+ const hash8 = bytesToHex(blake2s(encodedImage)).slice(0, 8);
136
+ return {
137
+ basename: `nft-${hash8}-${timestampMs}`,
138
+ image: encodedImage,
139
+ thumbnail: encodedThumbnail,
140
+ metadata,
141
+ };
142
+ }
@@ -8,6 +8,8 @@ export const PRO2_WALLPAPER_HEIGHT = 1024;
8
8
 
9
9
  export type Pro2WallpaperColorFormat = 'RGB565' | 'RGB565A8';
10
10
 
11
+ export type Pro2ImageAlphaMode = 'preserve' | 'black-background';
12
+
11
13
  const COLOR_FORMAT_RGB565 = 0x12;
12
14
  const COLOR_FORMAT_RGB565A8 = 0x14;
13
15
 
@@ -36,10 +38,11 @@ function align(value: number, boundary: number): number {
36
38
  return Math.ceil(value / boundary) * boundary;
37
39
  }
38
40
 
39
- export function encodePro2Wallpaper(options: {
41
+ export function encodePro2Image(options: {
40
42
  width: number;
41
43
  height: number;
42
44
  rgba: Uint8Array | ArrayBuffer;
45
+ alphaMode?: Pro2ImageAlphaMode;
43
46
  }): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
44
47
  const { width, height } = options;
45
48
  if (!Number.isInteger(width) || width <= 0 || width > 0xffff) {
@@ -57,11 +60,14 @@ export function encodePro2Wallpaper(options: {
57
60
  );
58
61
  }
59
62
 
63
+ const alphaMode = options.alphaMode ?? 'preserve';
60
64
  let hasTransparency = false;
61
- for (let index = 3; index < rgba.length; index += 4) {
62
- if (rgba[index] !== 0xff) {
63
- hasTransparency = true;
64
- break;
65
+ if (alphaMode === 'preserve') {
66
+ for (let index = 3; index < rgba.length; index += 4) {
67
+ if (rgba[index] !== 0xff) {
68
+ hasTransparency = true;
69
+ break;
70
+ }
65
71
  }
66
72
  }
67
73
 
@@ -85,9 +91,22 @@ export function encodePro2Wallpaper(options: {
85
91
  for (let x = 0; x < width; x += 1) {
86
92
  const sourceOffset = (y * width + x) * 4;
87
93
  const thresholdIndex = ((y & 7) << 3) + (x & 7);
88
- const red = Math.min(rgba[sourceOffset] + RED_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
89
- const green = Math.min(rgba[sourceOffset + 1] + GREEN_THRESHOLD[thresholdIndex], 0xff) & 0xfc;
90
- const blue = Math.min(rgba[sourceOffset + 2] + BLUE_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
94
+ const alpha = rgba[sourceOffset + 3];
95
+ const redChannel =
96
+ alphaMode === 'black-background'
97
+ ? Math.round((rgba[sourceOffset] * alpha) / 0xff)
98
+ : rgba[sourceOffset];
99
+ const greenChannel =
100
+ alphaMode === 'black-background'
101
+ ? Math.round((rgba[sourceOffset + 1] * alpha) / 0xff)
102
+ : rgba[sourceOffset + 1];
103
+ const blueChannel =
104
+ alphaMode === 'black-background'
105
+ ? Math.round((rgba[sourceOffset + 2] * alpha) / 0xff)
106
+ : rgba[sourceOffset + 2];
107
+ const red = Math.min(redChannel + RED_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
108
+ const green = Math.min(greenChannel + GREEN_THRESHOLD[thresholdIndex], 0xff) & 0xfc;
109
+ const blue = Math.min(blueChannel + BLUE_THRESHOLD[thresholdIndex], 0xff) & 0xf8;
91
110
  const rgb565 = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
92
111
  const rgbOffset = 12 + y * stride + x * 2;
93
112
  data[rgbOffset] = rgb565 & 0xff;
@@ -101,3 +120,11 @@ export function encodePro2Wallpaper(options: {
101
120
 
102
121
  return { data, colorFormat };
103
122
  }
123
+
124
+ export function encodePro2Wallpaper(options: {
125
+ width: number;
126
+ height: number;
127
+ rgba: Uint8Array | ArrayBuffer;
128
+ }): { data: Uint8Array; colorFormat: Pro2WallpaperColorFormat } {
129
+ return encodePro2Image(options);
130
+ }
@@ -0,0 +1,63 @@
1
+ import { UI_EVENT, UI_REQUEST } from '../events/ui-request';
2
+
3
+ import type { CoreMessage } from '../events';
4
+
5
+ const DEFAULT_UI_PROGRESS_INTERVAL_MS = 250;
6
+
7
+ type UiProgressThrottleState = {
8
+ lastEmittedAt: number;
9
+ lastProgress?: number;
10
+ };
11
+
12
+ const normalizeProgress = (progress: unknown) => {
13
+ if (typeof progress !== 'number' || !Number.isFinite(progress)) {
14
+ return undefined;
15
+ }
16
+
17
+ return Math.max(0, Math.min(100, Math.floor(progress)));
18
+ };
19
+
20
+ export const createUiProgressMessageFilter = (
21
+ intervalMs = DEFAULT_UI_PROGRESS_INTERVAL_MS,
22
+ now = Date.now
23
+ ) => {
24
+ const stateByType = new Map<string, UiProgressThrottleState>();
25
+
26
+ return (message: CoreMessage) => {
27
+ if (
28
+ !('event' in message) ||
29
+ message.event !== UI_EVENT ||
30
+ (message.type !== UI_REQUEST.DEVICE_PROGRESS && message.type !== UI_REQUEST.FIRMWARE_PROGRESS)
31
+ ) {
32
+ return true;
33
+ }
34
+
35
+ const key =
36
+ message.type === UI_REQUEST.FIRMWARE_PROGRESS
37
+ ? `${message.type}:${message.payload.progressType}`
38
+ : message.type;
39
+ const progress = normalizeProgress(message.payload.progress);
40
+ const currentTime = now();
41
+ const state = stateByType.get(key);
42
+
43
+ if (!state) {
44
+ stateByType.set(key, { lastEmittedAt: currentTime, lastProgress: progress });
45
+ return true;
46
+ }
47
+
48
+ const isBoundary = progress === 0 || progress === 100;
49
+ if (isBoundary && progress === state.lastProgress) {
50
+ return false;
51
+ }
52
+
53
+ const startsNewPhase =
54
+ progress !== undefined && state.lastProgress !== undefined && progress < state.lastProgress;
55
+
56
+ if (!isBoundary && !startsNewPhase && currentTime - state.lastEmittedAt < intervalMs) {
57
+ return false;
58
+ }
59
+
60
+ stateByType.set(key, { lastEmittedAt: currentTime, lastProgress: progress });
61
+ return true;
62
+ };
63
+ };