@onekeyfe/hd-core 1.2.0-alpha.65 → 1.2.0-alpha.66

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 (314) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +313 -0
  2. package/__tests__/DeviceCommands.test.ts +59 -13
  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__/core-initialization.test.ts +23 -0
  7. package/__tests__/device-connector-protocol.test.ts +81 -0
  8. package/__tests__/device-lifecycle-events.test.ts +105 -3
  9. package/__tests__/device-pool-state.test.ts +22 -0
  10. package/__tests__/device-settings.test.ts +39 -18
  11. package/__tests__/device-state-contract.test.ts +1 -0
  12. package/__tests__/device-state-mapper.test.ts +13 -1
  13. package/__tests__/device-utils.test.ts +48 -1
  14. package/__tests__/deviceSettings.test.ts +11 -1
  15. package/__tests__/deviceUploadNft.test.ts +316 -0
  16. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +31 -59
  17. package/__tests__/get-device-state.test.ts +132 -12
  18. package/__tests__/homescreen.test.ts +41 -1
  19. package/__tests__/kaspa-use-tweak-pro2.test.ts +2 -2
  20. package/__tests__/kaspaSignTransaction.test.ts +3 -26
  21. package/__tests__/logBlockEvent.test.ts +1 -1
  22. package/__tests__/method-protocol-support.test.ts +19 -0
  23. package/__tests__/open-wallet-session.test.ts +568 -37
  24. package/__tests__/pro2Nft.test.ts +108 -0
  25. package/__tests__/protocol-binding.test.ts +89 -0
  26. package/__tests__/protocol-v2-resources.test.ts +340 -0
  27. package/__tests__/protocol-v2-ui-lifecycle.test.ts +91 -0
  28. package/__tests__/protocol-v2-unlock-policy.test.ts +300 -37
  29. package/__tests__/protocol-v2.test.ts +1506 -1097
  30. package/__tests__/protocolV2FileWrite.test.ts +152 -0
  31. package/__tests__/protocolV2UiInteraction.test.ts +63 -0
  32. package/__tests__/public-pro2-api-boundary.test.ts +1 -0
  33. package/__tests__/search-devices.test.ts +12 -3
  34. package/__tests__/tron-sign-message-init.test.ts +2 -2
  35. package/__tests__/uiProgressThrottle.test.ts +74 -0
  36. package/__tests__/uiPromiseRegistry.test.ts +115 -0
  37. package/dist/api/BaseMethod.d.ts +3 -1
  38. package/dist/api/BaseMethod.d.ts.map +1 -1
  39. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -1
  40. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  41. package/dist/api/DetectDeviceConnectProtocol.d.ts +7 -0
  42. package/dist/api/DetectDeviceConnectProtocol.d.ts.map +1 -0
  43. package/dist/api/FirmwareUpdate.d.ts.map +1 -1
  44. package/dist/api/FirmwareUpdateV2.d.ts +0 -11
  45. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  46. package/dist/api/FirmwareUpdateV3.d.ts +3 -7
  47. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  48. package/dist/api/FirmwareUpdateV4.d.ts +13 -27
  49. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  50. package/dist/api/GetPassphraseState.d.ts.map +1 -1
  51. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  52. package/dist/api/SearchDevices.d.ts.map +1 -1
  53. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  54. package/dist/api/device/DeviceSettings.d.ts.map +1 -1
  55. package/dist/api/device/DeviceUnlock.d.ts +2 -2
  56. package/dist/api/device/DeviceUnlock.d.ts.map +1 -1
  57. package/dist/api/device/DeviceUpdateBootloader.d.ts +1 -5
  58. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  59. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +4 -11
  60. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  61. package/dist/api/firmware/getBinary.d.ts +0 -7
  62. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  63. package/dist/api/firmware/updateBootloader.d.ts +0 -2
  64. package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
  65. package/dist/api/firmware/uploadFirmware.d.ts +1 -9
  66. package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
  67. package/dist/api/helpers/protocolV2FileWrite.d.ts +9 -0
  68. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  69. package/dist/api/index.d.ts +2 -0
  70. package/dist/api/index.d.ts.map +1 -1
  71. package/dist/api/kaspa/KaspaGetAddress.d.ts +1 -1
  72. package/dist/api/kaspa/KaspaSignTransaction.d.ts +1 -1
  73. package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -1
  74. package/dist/api/polkadot/PolkadotGetAddress.d.ts +1 -1
  75. package/dist/api/polkadot/networks.d.ts +1 -1
  76. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +31 -0
  77. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -0
  78. package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -1
  79. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  80. package/dist/api/solana/SolSignMessage.d.ts +1 -1
  81. package/dist/api/solana/SolSignOffchainMessage.d.ts +1 -1
  82. package/dist/api/solana/SolSignTransaction.d.ts +2 -2
  83. package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
  84. package/dist/api/sui/SuiGetAddress.d.ts +1 -1
  85. package/dist/api/sui/SuiGetPublicKey.d.ts +1 -1
  86. package/dist/api/sui/SuiSignMessage.d.ts +1 -1
  87. package/dist/api/sui/SuiSignTransaction.d.ts +1 -1
  88. package/dist/api/ton/TonGetAddress.d.ts +1 -1
  89. package/dist/api/ton/TonSignMessage.d.ts +1 -1
  90. package/dist/api/ton/TonSignProof.d.ts +1 -1
  91. package/dist/api/tron/TronSignMessage.d.ts +2 -2
  92. package/dist/api/tron/TronSignTransaction.d.ts +1 -1
  93. package/dist/core/deviceEventRegistration.d.ts +2 -0
  94. package/dist/core/deviceEventRegistration.d.ts.map +1 -1
  95. package/dist/core/index.d.ts.map +1 -1
  96. package/dist/core/uiPromiseRegistry.d.ts +6 -0
  97. package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
  98. package/dist/data-manager/DataManager.d.ts +9 -4
  99. package/dist/data-manager/DataManager.d.ts.map +1 -1
  100. package/dist/data-manager/TransportManager.d.ts.map +1 -1
  101. package/dist/data-manager/connectSettings.d.ts.map +1 -1
  102. package/dist/device/Device.d.ts +29 -6
  103. package/dist/device/Device.d.ts.map +1 -1
  104. package/dist/device/DeviceCommands.d.ts +4 -4
  105. package/dist/device/DeviceCommands.d.ts.map +1 -1
  106. package/dist/device/DeviceConnector.d.ts +2 -1
  107. package/dist/device/DeviceConnector.d.ts.map +1 -1
  108. package/dist/device/DevicePool.d.ts.map +1 -1
  109. package/dist/deviceProfile/protocolV2DeviceIdentity.d.ts.map +1 -1
  110. package/dist/events/device.d.ts +4 -0
  111. package/dist/events/device.d.ts.map +1 -1
  112. package/dist/events/logBlockEvent.d.ts.map +1 -1
  113. package/dist/events/ui-promise.d.ts +4 -2
  114. package/dist/events/ui-promise.d.ts.map +1 -1
  115. package/dist/events/ui-request.d.ts +31 -2
  116. package/dist/events/ui-request.d.ts.map +1 -1
  117. package/dist/events/ui-response.d.ts +10 -2
  118. package/dist/events/ui-response.d.ts.map +1 -1
  119. package/dist/index.d.ts +243 -248
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +3051 -3774
  122. package/dist/inject.d.ts +6 -1
  123. package/dist/inject.d.ts.map +1 -1
  124. package/dist/lowLevelInject.d.ts.map +1 -1
  125. package/dist/protocols/protocol-v2/features.d.ts +0 -4
  126. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  127. package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
  128. package/dist/protocols/protocol-v2/resources.d.ts +30 -0
  129. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -0
  130. package/dist/protocols/protocol-v2/uiInteraction.d.ts +7 -4
  131. package/dist/protocols/protocol-v2/uiInteraction.d.ts.map +1 -1
  132. package/dist/protocols/protocol-v2/unlockPolicy.d.ts +1 -3
  133. package/dist/protocols/protocol-v2/unlockPolicy.d.ts.map +1 -1
  134. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +22 -0
  135. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -0
  136. package/dist/protocols/protocol-v2/walletSession.d.ts +6 -1
  137. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  138. package/dist/topLevelInject.d.ts.map +1 -1
  139. package/dist/types/api/checkAllFirmwareRelease.d.ts +3 -5
  140. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  141. package/dist/types/api/detectDeviceConnectProtocol.d.ts +4 -0
  142. package/dist/types/api/detectDeviceConnectProtocol.d.ts.map +1 -0
  143. package/dist/types/api/deviceUnlock.d.ts +5 -1
  144. package/dist/types/api/deviceUnlock.d.ts.map +1 -1
  145. package/dist/types/api/deviceUpdateBootloader.d.ts +0 -6
  146. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  147. package/dist/types/api/export.d.ts +2 -4
  148. package/dist/types/api/export.d.ts.map +1 -1
  149. package/dist/types/api/firmwareUpdate.d.ts +4 -71
  150. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  151. package/dist/types/api/index.d.ts +7 -10
  152. package/dist/types/api/index.d.ts.map +1 -1
  153. package/dist/types/api/protocolV2.d.ts +3 -0
  154. package/dist/types/api/protocolV2.d.ts.map +1 -1
  155. package/dist/types/device.d.ts +2 -2
  156. package/dist/types/device.d.ts.map +1 -1
  157. package/dist/types/params.d.ts +1 -0
  158. package/dist/types/params.d.ts.map +1 -1
  159. package/dist/types/settings.d.ts +38 -21
  160. package/dist/types/settings.d.ts.map +1 -1
  161. package/dist/utils/deviceFeaturesCompat.d.ts +0 -1
  162. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  163. package/dist/utils/deviceFeaturesUtils.d.ts +4 -0
  164. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  165. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  166. package/dist/utils/deviceSettings.d.ts.map +1 -1
  167. package/dist/utils/homescreen.d.ts +4 -0
  168. package/dist/utils/homescreen.d.ts.map +1 -1
  169. package/dist/utils/index.d.ts +1 -1
  170. package/dist/utils/index.d.ts.map +1 -1
  171. package/dist/utils/patch.d.ts +1 -1
  172. package/dist/utils/patch.d.ts.map +1 -1
  173. package/dist/utils/pro2Nft.d.ts +31 -0
  174. package/dist/utils/pro2Nft.d.ts.map +1 -0
  175. package/dist/utils/pro2Wallpaper.d.ts +10 -0
  176. package/dist/utils/pro2Wallpaper.d.ts.map +1 -1
  177. package/dist/utils/uiProgressThrottle.d.ts +3 -0
  178. package/dist/utils/uiProgressThrottle.d.ts.map +1 -0
  179. package/package.json +4 -4
  180. package/src/api/BaseMethod.ts +8 -10
  181. package/src/api/CheckAllFirmwareRelease.ts +59 -60
  182. package/src/api/DetectDeviceConnectProtocol.ts +18 -0
  183. package/src/api/FirmwareUpdate.ts +7 -15
  184. package/src/api/FirmwareUpdateV2.ts +126 -316
  185. package/src/api/FirmwareUpdateV3.ts +63 -265
  186. package/src/api/FirmwareUpdateV4.ts +597 -1005
  187. package/src/api/GetPassphraseState.ts +7 -1
  188. package/src/api/OpenWalletSession.ts +71 -17
  189. package/src/api/SearchDevices.ts +3 -1
  190. package/src/api/allnetwork/AllNetworkGetAddress.ts +21 -21
  191. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +36 -11
  192. package/src/api/device/DeviceSettings.ts +5 -4
  193. package/src/api/device/DeviceUnlock.ts +8 -3
  194. package/src/api/device/DeviceUpdateBootloader.ts +6 -125
  195. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +27 -83
  196. package/src/api/firmware/updateBootloader.ts +4 -19
  197. package/src/api/firmware/uploadFirmware.ts +39 -144
  198. package/src/api/helpers/protocolV2FileWrite.ts +202 -63
  199. package/src/api/index.ts +2 -0
  200. package/src/api/kaspa/KaspaGetAddress.ts +1 -1
  201. package/src/api/kaspa/KaspaSignTransaction.ts +2 -13
  202. package/src/api/polkadot/networks.ts +3 -3
  203. package/src/api/protocol-v2/DeviceUploadNft.ts +197 -0
  204. package/src/api/protocol-v2/ProtocolInfoRequest.ts +3 -1
  205. package/src/api/protocol-v2/helpers.ts +1 -1
  206. package/src/api/solana/SolSignMessage.ts +1 -1
  207. package/src/api/solana/SolSignOffchainMessage.ts +1 -1
  208. package/src/api/solana/SolSignTransaction.ts +2 -2
  209. package/src/api/sui/SuiGetAddress.ts +1 -1
  210. package/src/api/sui/SuiGetPublicKey.ts +1 -1
  211. package/src/api/sui/SuiSignMessage.ts +1 -1
  212. package/src/api/sui/SuiSignTransaction.ts +1 -1
  213. package/src/api/ton/TonGetAddress.ts +1 -1
  214. package/src/api/ton/TonSignMessage.ts +1 -1
  215. package/src/api/ton/TonSignProof.ts +1 -1
  216. package/src/api/tron/TronSignMessage.ts +2 -2
  217. package/src/api/tron/TronSignTransaction.ts +1 -1
  218. package/src/core/deviceEventRegistration.ts +4 -0
  219. package/src/core/index.ts +213 -111
  220. package/src/core/uiPromiseRegistry.ts +63 -0
  221. package/src/data/messages/messages-protocol-v2.json +25 -0
  222. package/src/data-manager/DataManager.ts +113 -41
  223. package/src/data-manager/TransportManager.ts +6 -0
  224. package/src/data-manager/connectSettings.ts +0 -11
  225. package/src/device/Device.ts +274 -49
  226. package/src/device/DeviceCommands.ts +31 -4
  227. package/src/device/DeviceConnector.ts +33 -13
  228. package/src/device/DevicePool.ts +21 -7
  229. package/src/deviceProfile/buildDeviceFeatures.ts +2 -2
  230. package/src/deviceProfile/protocolV2DeviceIdentity.ts +3 -0
  231. package/src/events/device.ts +4 -0
  232. package/src/events/logBlockEvent.ts +1 -0
  233. package/src/events/ui-promise.ts +4 -2
  234. package/src/events/ui-request.ts +36 -2
  235. package/src/events/ui-response.ts +12 -2
  236. package/src/index.ts +0 -5
  237. package/src/inject.ts +60 -24
  238. package/src/lowLevelInject.ts +7 -8
  239. package/src/protocols/protocol-v2/features.ts +10 -7
  240. package/src/protocols/protocol-v2/firmware.ts +2 -0
  241. package/src/protocols/protocol-v2/resources.ts +390 -0
  242. package/src/protocols/protocol-v2/uiInteraction.ts +46 -6
  243. package/src/protocols/protocol-v2/unlockPolicy.ts +1 -105
  244. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +131 -0
  245. package/src/protocols/protocol-v2/walletSession.ts +214 -36
  246. package/src/topLevelInject.ts +7 -8
  247. package/src/types/api/checkAllFirmwareRelease.ts +3 -5
  248. package/src/types/api/detectDeviceConnectProtocol.ts +6 -0
  249. package/src/types/api/deviceUnlock.ts +12 -1
  250. package/src/types/api/deviceUpdateBootloader.ts +0 -6
  251. package/src/types/api/export.ts +1 -18
  252. package/src/types/api/firmwareUpdate.ts +6 -79
  253. package/src/types/api/index.ts +13 -14
  254. package/src/types/api/protocolV2.ts +13 -0
  255. package/src/types/device.ts +15 -3
  256. package/src/types/params.ts +7 -1
  257. package/src/types/settings.ts +60 -22
  258. package/src/utils/deviceFeaturesCompat.ts +5 -6
  259. package/src/utils/deviceFeaturesUtils.ts +14 -3
  260. package/src/utils/deviceInfoUtils.ts +2 -0
  261. package/src/utils/deviceSettings.ts +3 -0
  262. package/src/utils/homescreen.ts +32 -6
  263. package/src/utils/index.ts +6 -1
  264. package/src/utils/pro2Nft.ts +142 -0
  265. package/src/utils/pro2Wallpaper.ts +35 -8
  266. package/src/utils/uiProgressThrottle.ts +63 -0
  267. package/__tests__/device-initialize-timeout.test.ts +0 -56
  268. package/__tests__/firmware-update/check-all-firmware-release.test.ts +0 -175
  269. package/__tests__/firmware-update/firmware-artifact-source.test.ts +0 -119
  270. package/__tests__/firmware-update/firmware-host-binding.test.ts +0 -70
  271. package/__tests__/firmware-update/firmware-preparation-error.test.ts +0 -27
  272. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +0 -200
  273. package/__tests__/firmware-update/firmware-update-capabilities.test.ts +0 -13
  274. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +0 -394
  275. package/__tests__/firmware-update/firmware-update-plan.test.ts +0 -593
  276. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +0 -213
  277. package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +0 -58
  278. package/__tests__/firmware-update/firmware-upload-source.test.ts +0 -70
  279. package/dist/api/firmware/FirmwareArtifactSource.d.ts +0 -25
  280. package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +0 -1
  281. package/dist/api/firmware/FirmwareHostBinding.d.ts +0 -6
  282. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +0 -1
  283. package/dist/api/firmware/FirmwarePreparationError.d.ts +0 -3
  284. package/dist/api/firmware/FirmwarePreparationError.d.ts.map +0 -1
  285. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +0 -3
  286. package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +0 -1
  287. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -25
  288. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +0 -1
  289. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +0 -32
  290. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +0 -1
  291. package/dist/api/firmware/progressThrottle.d.ts +0 -3
  292. package/dist/api/firmware/progressThrottle.d.ts.map +0 -1
  293. package/dist/protocols/protocol-v2/lockedError.d.ts +0 -2
  294. package/dist/protocols/protocol-v2/lockedError.d.ts.map +0 -1
  295. package/dist/protocols/protocol-v2/unlockRetry.d.ts +0 -10
  296. package/dist/protocols/protocol-v2/unlockRetry.d.ts.map +0 -1
  297. package/dist/types/api/firmwareUpdateCapabilities.d.ts +0 -9
  298. package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +0 -1
  299. package/dist/types/api/firmwareUpdatePlan.d.ts +0 -28
  300. package/dist/types/api/firmwareUpdatePlan.d.ts.map +0 -1
  301. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +0 -42
  302. package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +0 -1
  303. package/src/api/firmware/FirmwareArtifactSource.ts +0 -278
  304. package/src/api/firmware/FirmwareHostBinding.ts +0 -100
  305. package/src/api/firmware/FirmwarePreparationError.ts +0 -12
  306. package/src/api/firmware/FirmwareUpdateCapabilities.ts +0 -9
  307. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -772
  308. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +0 -449
  309. package/src/api/firmware/progressThrottle.ts +0 -44
  310. package/src/protocols/protocol-v2/lockedError.ts +0 -10
  311. package/src/protocols/protocol-v2/unlockRetry.ts +0 -109
  312. package/src/types/api/firmwareUpdateCapabilities.ts +0 -9
  313. package/src/types/api/firmwareUpdatePlan.ts +0 -38
  314. package/src/types/api/firmwareUpdatePreparedPlan.ts +0 -53
@@ -5,6 +5,7 @@ import CheckAllFirmwareRelease, {
5
5
  } from '../src/api/CheckAllFirmwareRelease';
6
6
  import { DataManager } from '../src/data-manager';
7
7
  import { createCoreApi } from '../src/inject';
8
+ import { PROTOCOL_V2_RESOURCE_DEVICE_PATHS } from '../src/protocols/protocol-v2/resources';
8
9
 
9
10
  import type { CoreApi } from '../src/types/api';
10
11
  import type { DeviceStateVersions, IFirmwareReleaseInfo } from '../src/types';
@@ -72,7 +73,74 @@ const release: IFirmwareReleaseInfo = {
72
73
  ],
73
74
  };
74
75
 
76
+ const stableResources = ['images', 'animation', 'wallpaper', 'translations', 'roobert', 'noto'].map(
77
+ (type, index) => ({
78
+ type,
79
+ url: `https://example.com/${type}.okpkg`,
80
+ size: 0x52a0 + index + 1,
81
+ fileHash: 'a'.repeat(64),
82
+ headerHash: index.toString(16).padStart(128, '0'),
83
+ })
84
+ );
85
+
86
+ const createFilesystemTypedCall = (missingAll = false) => {
87
+ const resourceByPath = new Map(
88
+ stableResources.map(resource => [
89
+ PROTOCOL_V2_RESOURCE_DEVICE_PATHS[
90
+ resource.type as keyof typeof PROTOCOL_V2_RESOURCE_DEVICE_PATHS
91
+ ],
92
+ resource,
93
+ ])
94
+ );
95
+ return jest.fn((requestType: string, _responseType: string, payload: Record<string, any>) => {
96
+ if (requestType === 'ResourceInventoryGet') {
97
+ throw new Error('ResourceInventoryGet is unavailable on released firmware');
98
+ }
99
+ if (requestType === 'FilesystemPathInfoQuery') {
100
+ const resource = resourceByPath.get(payload.path);
101
+ return {
102
+ message: {
103
+ exist: Boolean(resource) && !missingAll,
104
+ directory: false,
105
+ size: missingAll ? 0 : resource?.size,
106
+ },
107
+ };
108
+ }
109
+ if (requestType === 'FilesystemFileRead') {
110
+ const resource = resourceByPath.get(payload.file.path);
111
+ if (!resource || missingAll) throw new Error('missing resource');
112
+ const header = new Uint8Array(0x52a0);
113
+ const view = new DataView(header.buffer);
114
+ 'OKPP'.split('').forEach((char, index) => {
115
+ header[index] = char.charCodeAt(0);
116
+ });
117
+ 'RESC'.split('').forEach((char, index) => {
118
+ header[0x08 + index] = char.charCodeAt(0);
119
+ });
120
+ view.setUint32(0x0c, header.byteLength, true);
121
+ for (let index = 0; index < resource.headerHash.length / 2; index++) {
122
+ header[0x240 + index] = Number.parseInt(
123
+ resource.headerHash.slice(index * 2, index * 2 + 2),
124
+ 16
125
+ );
126
+ }
127
+ const offset = Number(payload.file.offset);
128
+ const chunkLength = Number(payload.chunk_len);
129
+ return {
130
+ message: {
131
+ data: header.slice(offset, offset + chunkLength),
132
+ },
133
+ };
134
+ }
135
+ throw new Error(`Unexpected request: ${requestType}`);
136
+ });
137
+ };
138
+
75
139
  describe('checkAllFirmwareRelease Protocol V2 support', () => {
140
+ afterEach(() => {
141
+ jest.restoreAllMocks();
142
+ });
143
+
76
144
  test('builds ordered firmwareUpdateV4 targets from recommended component versions', () => {
77
145
  const result = buildProtocolV2FirmwareRelease({
78
146
  currentVersions,
@@ -84,7 +152,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
84
152
  status: 'required',
85
153
  hasUpgrade: true,
86
154
  required: true,
87
- targetsToUpdate: ['boot', 'app_v1', 'resource'],
155
+ targetsToUpdate: ['boot', 'app_v1'],
88
156
  components: [
89
157
  { configKey: 'bootloader', status: 'outdated', updateTarget: 'boot' },
90
158
  { configKey: 'applicationP1', status: 'outdated', updateTarget: 'app_v1' },
@@ -142,6 +210,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
142
210
  applicationP1Hash: 'aa'.repeat(32),
143
211
  applicationP2Hash: 'bb'.repeat(32),
144
212
  },
213
+ checkFirmwareHash: true,
145
214
  remotePayloadHashes: {
146
215
  applicationP1: 'aa'.repeat(64),
147
216
  applicationP2: 'cc'.repeat(64),
@@ -160,6 +229,37 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
160
229
  });
161
230
  });
162
231
 
232
+ test('uses version-only comparison for same-version packages by default', () => {
233
+ const packageSet: IFirmwareReleaseInfo = {
234
+ ...release,
235
+ required: false,
236
+ resourceBundles: undefined,
237
+ installOrder: ['applicationP1'],
238
+ components: {
239
+ applicationP1: {
240
+ target: 'APPLICATION_P1',
241
+ version: [1, 0, 0],
242
+ url: 'https://example.com/application-p1.okpkg',
243
+ payloadHash: 'cc'.repeat(64),
244
+ },
245
+ },
246
+ };
247
+
248
+ expect(
249
+ buildProtocolV2FirmwareRelease({
250
+ currentVersions: { ...currentVersions, applicationP1: '1.0.0' },
251
+ currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
252
+ firmwareType: EFirmwareType.Universal,
253
+ release: packageSet,
254
+ })
255
+ ).toMatchObject({
256
+ status: 'valid',
257
+ hasUpgrade: false,
258
+ targetsToUpdate: [],
259
+ components: [{ configKey: 'applicationP1', status: 'valid' }],
260
+ });
261
+ });
262
+
163
263
  test('keeps same-version hash-aware components unknown without metadata payloadHash', () => {
164
264
  const packageSet: IFirmwareReleaseInfo = {
165
265
  ...release,
@@ -180,6 +280,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
180
280
  buildProtocolV2FirmwareRelease({
181
281
  currentVersions: { ...currentVersions, applicationP1: '1.0.0' },
182
282
  currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
283
+ checkFirmwareHash: true,
183
284
  firmwareType: EFirmwareType.Universal,
184
285
  release: packageSet,
185
286
  })
@@ -215,6 +316,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
215
316
  const result = buildProtocolV2FirmwareRelease({
216
317
  currentVersions,
217
318
  currentVerification: { applicationP1Hash: 'aa'.repeat(32) },
319
+ checkFirmwareHash: true,
218
320
  remotePayloadHashes: { applicationP1: 'cc'.repeat(64) },
219
321
  firmwareType: EFirmwareType.Universal,
220
322
  release: packageSet,
@@ -223,7 +325,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
223
325
  expect(result.targetsToUpdate).toEqual(['app_v1', 'app_v2']);
224
326
  });
225
327
 
226
- test('uses the existing method on Protocol V2 and keeps its public API name', async () => {
328
+ test('does not read vol0 resource inventory while Protocol V2 is in Application mode', async () => {
227
329
  const method = new CheckAllFirmwareRelease({
228
330
  id: 1,
229
331
  payload: {
@@ -240,6 +342,7 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
240
342
  status: { mode: 'normal' },
241
343
  versions: currentVersions,
242
344
  });
345
+ const typedCall = createFilesystemTypedCall(true);
243
346
  method.device = {
244
347
  isProtocolV2: () => true,
245
348
  features: {
@@ -247,32 +350,124 @@ describe('checkAllFirmwareRelease Protocol V2 support', () => {
247
350
  firmwareVersion: '1.0.0',
248
351
  },
249
352
  getDeviceState,
353
+ getCommands: () => ({ typedCall }),
250
354
  } as unknown as CheckAllFirmwareRelease['device'];
251
355
  jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
356
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
252
357
 
253
358
  await expect(method.run()).resolves.toMatchObject({
254
359
  protocol: 'V2',
255
360
  deviceType: 'pro2',
256
361
  status: 'required',
257
- targetsToUpdate: ['boot', 'app_v1', 'resource'],
362
+ resourceStatus: 'unknown',
363
+ targetsToUpdate: ['boot', 'app_v1'],
258
364
  firmware: {
259
365
  status: 'required',
260
366
  },
261
367
  });
368
+ expect(typedCall).not.toHaveBeenCalled();
369
+ expect(getDeviceState).toHaveBeenCalledWith({
370
+ refreshSections: ['identity', 'versions'],
371
+ });
262
372
  expect(method.getSupportedProtocols()).toEqual(['V1', 'V2']);
263
373
  });
264
374
 
375
+ test('requests and compares firmware hashes only when explicitly enabled', async () => {
376
+ const packageSet: IFirmwareReleaseInfo = {
377
+ ...release,
378
+ required: false,
379
+ resourceBundles: undefined,
380
+ installOrder: ['applicationP1'],
381
+ components: {
382
+ applicationP1: {
383
+ target: 'APPLICATION_P1',
384
+ version: [1, 0, 0],
385
+ url: 'https://example.com/application-p1.okpkg',
386
+ payloadHash: 'cc'.repeat(64),
387
+ },
388
+ },
389
+ };
390
+ const method = new CheckAllFirmwareRelease({
391
+ id: 1,
392
+ payload: {
393
+ method: 'checkAllFirmwareRelease',
394
+ firmwareType: EFirmwareType.Universal,
395
+ checkFirmwareHash: true,
396
+ },
397
+ });
398
+ method.init();
399
+ const getDeviceState = jest.fn().mockResolvedValue({
400
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
401
+ status: { mode: 'normal' },
402
+ versions: { ...currentVersions, applicationP1: '1.0.0' },
403
+ verification: { applicationP1Hash: 'aa'.repeat(32) },
404
+ });
405
+ method.device = {
406
+ isProtocolV2: () => true,
407
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
408
+ getDeviceState,
409
+ getCommands: () => ({ typedCall: jest.fn() }),
410
+ } as unknown as CheckAllFirmwareRelease['device'];
411
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(packageSet);
412
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(undefined);
413
+
414
+ await expect(method.run()).resolves.toMatchObject({
415
+ status: 'outdated',
416
+ targetsToUpdate: ['app_v1'],
417
+ });
418
+ expect(getDeviceState).toHaveBeenCalledWith({
419
+ refreshSections: ['identity', 'versions', 'verification'],
420
+ });
421
+ });
422
+
423
+ test.each(['bootloader', 'romloader'] as const)(
424
+ 'uses filesystem inventory in %s mode instead of forcing all resources',
425
+ async mode => {
426
+ const method = new CheckAllFirmwareRelease({
427
+ id: 1,
428
+ payload: {
429
+ method: 'checkAllFirmwareRelease',
430
+ firmwareType: EFirmwareType.Universal,
431
+ },
432
+ });
433
+ method.init();
434
+ const typedCall = createFilesystemTypedCall();
435
+ method.device = {
436
+ isProtocolV2: () => true,
437
+ features: { deviceType: 'pro2', firmwareVersion: '1.0.0' },
438
+ getDeviceState: jest.fn().mockResolvedValue({
439
+ identity: { deviceType: 'pro2', firmwareType: EFirmwareType.Universal },
440
+ status: { mode },
441
+ versions: currentVersions,
442
+ }),
443
+ getCommands: () => ({ typedCall }),
444
+ } as unknown as CheckAllFirmwareRelease['device'];
445
+ jest.spyOn(DataManager, 'getFirmwareLatestRelease').mockReturnValue(release);
446
+ jest.spyOn(DataManager, 'getProtocolV2Resources').mockReturnValue(stableResources as any);
447
+
448
+ await expect(method.run()).resolves.toMatchObject({
449
+ protocol: 'V2',
450
+ resourceStatus: 'valid',
451
+ targetsToUpdate: ['boot', 'app_v1'],
452
+ });
453
+ expect(typedCall.mock.calls.some(call => call[0] === 'ResourceInventoryGet')).toBe(false);
454
+ expect(typedCall.mock.calls.some(call => call[0] === 'FilesystemFileRead')).toBe(true);
455
+ }
456
+ );
457
+
265
458
  test('continues forwarding the existing public method', async () => {
266
459
  const call = jest.fn().mockResolvedValue({ success: true, payload: {} });
267
460
  const api = createCoreApi(call as CoreApi['call']) as CoreApi;
268
461
 
269
462
  await api.checkAllFirmwareRelease('pro2-connect-id', {
463
+ checkFirmwareHash: true,
270
464
  firmwareType: EFirmwareType.Universal,
271
465
  retryCount: 0,
272
466
  });
273
467
 
274
468
  expect(call).toHaveBeenCalledWith({
275
469
  connectId: 'pro2-connect-id',
470
+ checkFirmwareHash: true,
276
471
  firmwareType: EFirmwareType.Universal,
277
472
  retryCount: 0,
278
473
  method: 'checkAllFirmwareRelease',
@@ -1,61 +1,19 @@
1
- import DataManager from '../src/data-manager/DataManager';
2
1
  import { parseConnectSettings } from '../src/data-manager/connectSettings';
3
2
 
4
- import type { RemoteConfigResponse } from '../src/types';
5
-
6
- jest.mock('axios', () => ({
7
- get: jest.fn(),
8
- }));
9
-
10
3
  jest.mock('../src/data/config', () => ({
11
4
  getSDKVersion: jest.fn(() => '1.0.0'),
12
5
  DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
13
6
  }));
14
7
 
15
- const mockAxiosGet: jest.Mock = jest.requireMock('axios').get;
16
-
17
8
  describe('parseConnectSettings', () => {
18
- const preloadedConfig = {
19
- bridge: {},
20
- classic: { firmware: [], ble: [] },
21
- classic1s: { firmware: [], ble: [] },
22
- classicpure: { firmware: [], ble: [] },
23
- mini: { firmware: [], ble: [] },
24
- touch: { firmware: [], ble: [] },
25
- pro: { firmware: [], ble: [] },
26
- pro2: { firmware: [], ble: [] },
27
- } as unknown as RemoteConfigResponse;
28
-
29
- it('keeps a serializable preloaded config in parsed settings', () => {
30
- const settings = parseConnectSettings({
31
- firmwareManifestMode: 'sdk-managed',
32
- preloadedConfig,
33
- });
34
-
35
- expect(settings.firmwareManifestMode).toBe('sdk-managed');
36
- expect(settings.preloadedConfig).toBe(preloadedConfig);
37
- });
38
-
39
- it('uses the preloaded config before the SDK network client', async () => {
40
- const settings = parseConnectSettings({
41
- firmwareManifestMode: 'sdk-managed',
42
- preloadedConfig,
43
- });
44
-
45
- await DataManager.load(settings);
46
-
47
- expect(mockAxiosGet).not.toHaveBeenCalled();
48
- expect(DataManager.deviceMap.classic).toEqual({ firmware: [], ble: [] });
49
- });
9
+ it('keeps custom configFetcher in parsed settings', () => {
10
+ const configFetcher = jest.fn();
50
11
 
51
- it('fails closed without a preloaded config in external-only mode', async () => {
52
12
  const settings = parseConnectSettings({
53
- firmwareManifestMode: 'external-only',
13
+ fetchConfig: true,
14
+ configFetcher,
54
15
  });
55
16
 
56
- await expect(DataManager.load(settings)).rejects.toThrow(
57
- 'External firmware config snapshot is required'
58
- );
59
- expect(mockAxiosGet).not.toHaveBeenCalled();
17
+ expect(settings.configFetcher).toBe(configFetcher);
60
18
  });
61
19
  });
@@ -0,0 +1,23 @@
1
+ import { DataManager } from '../src/data-manager';
2
+ import { init } from '../src/core';
3
+
4
+ import type { ConnectSettings } from '../src/types';
5
+
6
+ jest.mock('../src/data/config', () => ({
7
+ getSDKVersion: jest.fn(() => '1.0.0-test'),
8
+ DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0-test/',
9
+ }));
10
+
11
+ describe('Core initialization', () => {
12
+ afterEach(() => {
13
+ jest.restoreAllMocks();
14
+ });
15
+
16
+ it('does not create a partially initialized core when settings loading fails', async () => {
17
+ jest.spyOn(DataManager, 'load').mockRejectedValue(new Error('settings load failed'));
18
+
19
+ await expect(init({} as ConnectSettings, class TestTransport {})).rejects.toThrow(
20
+ 'settings load failed'
21
+ );
22
+ });
23
+ });
@@ -0,0 +1,81 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+
3
+ import DeviceConnector from '../src/device/DeviceConnector';
4
+
5
+ jest.mock('../src/data-manager/TransportManager', () => ({
6
+ __esModule: true,
7
+ default: {
8
+ load: jest.fn(),
9
+ getTransport: jest.fn(),
10
+ },
11
+ }));
12
+
13
+ jest.mock('../src/data-manager', () => ({
14
+ DataManager: {
15
+ getSettings: jest.fn(() => 'desktop-web-ble'),
16
+ isBleConnect: jest.fn(() => true),
17
+ },
18
+ }));
19
+
20
+ jest.mock('../src/device/DevicePool', () => ({
21
+ DevicePool: {
22
+ setConnector: jest.fn(),
23
+ },
24
+ }));
25
+
26
+ const transportManagerMock: {
27
+ default: {
28
+ getTransport: jest.Mock;
29
+ };
30
+ } = jest.requireMock('../src/data-manager/TransportManager');
31
+
32
+ describe('DeviceConnector protocol validation', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+
37
+ it('reports an explicit transport error when connector initialization ran too early', async () => {
38
+ transportManagerMock.default.getTransport.mockReturnValue(undefined);
39
+ const connector = new DeviceConnector();
40
+
41
+ await expect(connector.enumerate()).rejects.toMatchObject({
42
+ errorCode: HardwareErrorCode.TransportNotConfigured,
43
+ });
44
+ });
45
+
46
+ it('uses the actively probed protocol returned by acquire when the cache was cleared', async () => {
47
+ const acquire = jest.fn().mockResolvedValue({
48
+ id: 'pro2-id',
49
+ path: 'pro2-id',
50
+ protocolType: 'V2',
51
+ });
52
+ transportManagerMock.default.getTransport.mockReturnValue({
53
+ acquire,
54
+ getProtocolType: jest.fn(() => undefined),
55
+ });
56
+ const connector = new DeviceConnector();
57
+
58
+ await expect(connector.acquire('pro2-id', undefined, undefined, 'V2')).resolves.toEqual(
59
+ expect.objectContaining({ protocolType: 'V2' })
60
+ );
61
+ expect(acquire).toHaveBeenCalledWith(
62
+ expect.objectContaining({ uuid: 'pro2-id', expectedProtocol: 'V2' })
63
+ );
64
+ });
65
+
66
+ it('still rejects a real mismatch reported by the active protocol probe', async () => {
67
+ transportManagerMock.default.getTransport.mockReturnValue({
68
+ acquire: jest.fn().mockResolvedValue({
69
+ id: 'classic-id',
70
+ path: 'classic-id',
71
+ protocolType: 'V1',
72
+ }),
73
+ getProtocolType: jest.fn(() => 'V1'),
74
+ });
75
+ const connector = new DeviceConnector();
76
+
77
+ await expect(connector.acquire('classic-id', undefined, undefined, 'V2')).rejects.toThrow(
78
+ 'Device protocol mismatch: expected V2, detected V1'
79
+ );
80
+ });
81
+ });
@@ -1,3 +1,4 @@
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
1
2
  import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
2
3
 
3
4
  import { initConnector, initCore } from '../src/core';
@@ -95,7 +96,7 @@ describe('public device lifecycle events', () => {
95
96
  expect(messages.filter(message => message.type === DEVICE.DISCONNECT)).toHaveLength(1);
96
97
  });
97
98
 
98
- test('uses a cached protocol as a fallback-capable acquire hint', async () => {
99
+ test('uses a verified cached protocol as a strict expected protocol', async () => {
99
100
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
100
101
  const device = Device.fromDescriptor({
101
102
  id: 'ble-id',
@@ -103,16 +104,117 @@ describe('public device lifecycle events', () => {
103
104
  commType: 'ble',
104
105
  protocolType: 'V1',
105
106
  } as never);
106
- const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V2' });
107
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V1' });
107
108
  device.deviceConnector = { acquire } as never;
108
109
 
109
110
  await device.acquire();
110
111
 
111
- expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, undefined, 'V1');
112
+ expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, 'V1', undefined);
113
+ expect(device.getProtocol()).toBe('V1');
114
+ expect(device.originalDescriptor.protocolType).toBe('V1');
115
+ });
116
+
117
+ test('bypasses a cached protocol only for explicit active detection', async () => {
118
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
119
+ const device = Device.fromDescriptor({
120
+ id: 'ble-id',
121
+ path: 'ble-id',
122
+ commType: 'ble',
123
+ protocolType: 'V1',
124
+ } as never);
125
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id', protocolType: 'V2' });
126
+ device.deviceConnector = { acquire } as never;
127
+
128
+ await device.acquire(undefined, { forceProtocolDetection: true });
129
+
130
+ expect(acquire).toHaveBeenCalledWith('ble-id', undefined, true, undefined, undefined);
112
131
  expect(device.getProtocol()).toBe('V2');
113
132
  expect(device.originalDescriptor.protocolType).toBe('V2');
114
133
  });
115
134
 
135
+ test('does not reuse a stale protocol when active detection returns no protocol', async () => {
136
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
137
+ const device = Device.fromDescriptor({
138
+ id: 'ble-id',
139
+ path: 'ble-id',
140
+ commType: 'ble',
141
+ protocolType: 'V1',
142
+ } as never);
143
+ const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-id' });
144
+ const release = jest.fn().mockResolvedValue(undefined);
145
+ device.deviceConnector = { acquire, release } as never;
146
+ await expect(
147
+ device.acquire(undefined, {
148
+ forceProtocolDetection: true,
149
+ throwOnRunPromiseError: true,
150
+ })
151
+ ).rejects.toMatchObject({ errorCode: HardwareErrorCode.RuntimeError });
152
+ expect(device.originalDescriptor.protocolType).toBe('V1');
153
+ expect(device.hasDeviceAcquire()).toBe(false);
154
+ expect(release).toHaveBeenCalledWith('ble-id', false);
155
+ });
156
+
157
+ test('actively reacquires an already connected device without initializing it', async () => {
158
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('webusb' as never);
159
+ const device = Device.fromDescriptor({
160
+ id: 'usb-device',
161
+ path: 'usb-device',
162
+ session: 'old-session',
163
+ protocolType: 'V1',
164
+ } as never);
165
+ device.mainId = 'old-session';
166
+ device.commands = { disposed: false } as never;
167
+ const release = jest.spyOn(device, 'release').mockImplementation(() => {
168
+ device.mainId = null;
169
+ device.originalDescriptor.session = null;
170
+ device.commands.disposed = true;
171
+ return Promise.resolve();
172
+ });
173
+ const acquire = jest.spyOn(device, 'acquire').mockImplementation(() => {
174
+ device.mainId = 'new-session';
175
+ device.originalDescriptor.session = 'new-session';
176
+ device.originalDescriptor.protocolType = 'V2';
177
+ device.commands.disposed = false;
178
+ return Promise.resolve();
179
+ });
180
+ const initialize = jest.spyOn(device, 'initialize').mockResolvedValue(undefined);
181
+
182
+ await device.run(() => Promise.resolve(undefined), {
183
+ forceProtocolDetection: true,
184
+ skipInitialize: true,
185
+ });
186
+
187
+ expect(release).toHaveBeenCalled();
188
+ expect(acquire).toHaveBeenCalledWith(undefined, {
189
+ forceProtocolDetection: true,
190
+ });
191
+ expect(initialize).not.toHaveBeenCalled();
192
+ expect(device.getProtocol()).toBe('V2');
193
+ });
194
+
195
+ test('keeps the freshly probed React Native BLE session until the call completes', async () => {
196
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
197
+ const device = Device.fromDescriptor({
198
+ id: 'ble-device',
199
+ path: 'ble-device',
200
+ commType: 'ble',
201
+ protocolType: 'V1',
202
+ } as never);
203
+ device.mainId = 'ble-device';
204
+ device.commands = { disposed: false } as never;
205
+ (device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
206
+ const release = jest.spyOn(device, 'release').mockResolvedValue(undefined);
207
+ const run = jest.fn().mockResolvedValue(undefined);
208
+
209
+ await device.run(run, {
210
+ forceProtocolDetection: true,
211
+ skipInitialize: true,
212
+ });
213
+
214
+ expect(run).toHaveBeenCalledTimes(1);
215
+ expect(release).toHaveBeenCalledTimes(1);
216
+ });
217
+
116
218
  test('keeps an explicit connectProtocol as a strict expected protocol', async () => {
117
219
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
118
220
  const device = Device.fromDescriptor({
@@ -38,6 +38,28 @@ describe('DevicePool state lifecycle', () => {
38
38
  expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
39
39
  });
40
40
 
41
+ test('actively re-detects a cached device when protocol detection is forced', async () => {
42
+ const descriptor = { path: 'cached-path', protocolType: 'V1' } as any;
43
+ const device = Device.fromDescriptor(descriptor);
44
+ const acquire = jest.spyOn(device, 'acquire').mockImplementation(() => {
45
+ device.originalDescriptor.protocolType = 'V2';
46
+ return Promise.resolve(true);
47
+ });
48
+ const initialize = jest.spyOn(device, 'initialize').mockResolvedValue(undefined);
49
+ const release = jest.spyOn(device, 'release').mockResolvedValue(undefined);
50
+ DevicePool.devicesCache = { 'cached-id': device };
51
+
52
+ const result = await DevicePool.getDevices([descriptor], 'cached-id', {
53
+ forceProtocolDetection: true,
54
+ });
55
+
56
+ expect(acquire).toHaveBeenCalledWith(undefined, { forceProtocolDetection: true });
57
+ expect(initialize).toHaveBeenCalledWith({ forceProtocolDetection: true });
58
+ expect(release).toHaveBeenCalledTimes(1);
59
+ expect(device.originalDescriptor.protocolType).toBe('V2');
60
+ expect(result.deviceList).toEqual([device]);
61
+ });
62
+
41
63
  test('keeps a discovered device when its Protocol V2 label cannot be read', async () => {
42
64
  const labelError = new Error('settings unavailable');
43
65
  const getDeviceState = jest