@hot-updater/react-native 1.0.0-rc.16 → 1.0.0-rc.17

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 (316) hide show
  1. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +491 -388
  2. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +5 -11
  3. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  4. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +290 -12
  5. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +35 -19
  6. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  7. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  8. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  9. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  10. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +110 -52
  11. package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
  12. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  13. package/android/src/newarch/HotUpdaterModule.kt +74 -69
  14. package/android/src/oldarch/HotUpdaterModule.kt +63 -59
  15. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  16. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +1 -1
  17. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +446 -61
  18. package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +220 -0
  19. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +19 -27
  20. package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +71 -0
  21. package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +106 -0
  22. package/android/src/test/java/com/hotupdater/NoBackupContext.kt +11 -0
  23. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +309 -23
  24. package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +57 -0
  25. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +238 -157
  26. package/ios/HotUpdater/Internal/BundleMetadata.swift +3 -53
  27. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  28. package/ios/HotUpdater/Internal/HotUpdater.mm +8 -18
  29. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +48 -29
  30. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  31. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +108 -1
  32. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  33. package/lib/commonjs/appReady.js +111 -0
  34. package/lib/commonjs/appReady.js.map +1 -0
  35. package/lib/commonjs/appReady.spec.js +360 -0
  36. package/lib/commonjs/appReady.spec.js.map +1 -0
  37. package/lib/commonjs/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
  38. package/lib/commonjs/appReady.test-utils.js.map +1 -0
  39. package/lib/commonjs/baseURL.js +16 -0
  40. package/lib/commonjs/baseURL.js.map +1 -0
  41. package/lib/commonjs/checkForUpdate.js +104 -56
  42. package/lib/commonjs/checkForUpdate.js.map +1 -1
  43. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +337 -74
  44. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  45. package/lib/commonjs/clientPlugin.js +49 -0
  46. package/lib/commonjs/clientPlugin.js.map +1 -0
  47. package/lib/commonjs/entryImportGraph.spec.js +50 -0
  48. package/lib/commonjs/entryImportGraph.spec.js.map +1 -0
  49. package/lib/commonjs/fetchJSON.js +2 -6
  50. package/lib/commonjs/fetchJSON.js.map +1 -1
  51. package/lib/commonjs/httpClient.js +18 -165
  52. package/lib/commonjs/httpClient.js.map +1 -1
  53. package/lib/commonjs/httpClient.spec.js +26 -199
  54. package/lib/commonjs/httpClient.spec.js.map +1 -1
  55. package/lib/commonjs/index.js +49 -36
  56. package/lib/commonjs/index.js.map +1 -1
  57. package/lib/commonjs/index.spec.js +108 -123
  58. package/lib/commonjs/index.spec.js.map +1 -1
  59. package/lib/commonjs/native.js +67 -48
  60. package/lib/commonjs/native.js.map +1 -1
  61. package/lib/commonjs/native.spec.js +80 -58
  62. package/lib/commonjs/native.spec.js.map +1 -1
  63. package/lib/commonjs/pluginHost.js +208 -0
  64. package/lib/commonjs/pluginHost.js.map +1 -0
  65. package/lib/commonjs/pluginHost.spec.js +292 -0
  66. package/lib/commonjs/pluginHost.spec.js.map +1 -0
  67. package/lib/commonjs/plugins/insights/eventId.js +129 -0
  68. package/lib/commonjs/plugins/insights/eventId.js.map +1 -0
  69. package/lib/commonjs/plugins/insights/eventId.spec.js +47 -0
  70. package/lib/commonjs/plugins/insights/eventId.spec.js.map +1 -0
  71. package/lib/commonjs/plugins/insights/index.js +272 -0
  72. package/lib/commonjs/plugins/insights/index.js.map +1 -0
  73. package/lib/commonjs/plugins/insights/insights.spec.js +640 -0
  74. package/lib/commonjs/plugins/insights/insights.spec.js.map +1 -0
  75. package/lib/commonjs/plugins/insights/sender.js +128 -0
  76. package/lib/commonjs/plugins/insights/sender.js.map +1 -0
  77. package/lib/commonjs/plugins/insights/sender.spec.js +265 -0
  78. package/lib/commonjs/plugins/insights/sender.spec.js.map +1 -0
  79. package/lib/commonjs/plugins/insights/state.js +145 -0
  80. package/lib/commonjs/plugins/insights/state.js.map +1 -0
  81. package/lib/commonjs/releaseCatalogCache.js +13 -2
  82. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  83. package/lib/commonjs/releaseCatalogCache.spec.js +18 -0
  84. package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -1
  85. package/lib/commonjs/sdkVersion.js +1 -1
  86. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  87. package/lib/commonjs/updateError.js +132 -0
  88. package/lib/commonjs/updateError.js.map +1 -0
  89. package/lib/commonjs/updateError.spec.js +149 -0
  90. package/lib/commonjs/updateError.spec.js.map +1 -0
  91. package/lib/commonjs/wrap.js +43 -25
  92. package/lib/commonjs/wrap.js.map +1 -1
  93. package/lib/commonjs/wrap.progress.spec.js +195 -0
  94. package/lib/commonjs/wrap.progress.spec.js.map +1 -0
  95. package/lib/commonjs/wrap.spec.js +94 -230
  96. package/lib/commonjs/wrap.spec.js.map +1 -1
  97. package/lib/module/appReady.js +103 -0
  98. package/lib/module/appReady.js.map +1 -0
  99. package/lib/module/appReady.spec.js +359 -0
  100. package/lib/module/appReady.spec.js.map +1 -0
  101. package/lib/module/{notifyAppReadyInsights.test-utils.js → appReady.test-utils.js} +4 -3
  102. package/lib/module/appReady.test-utils.js.map +1 -0
  103. package/lib/module/baseURL.js +11 -0
  104. package/lib/module/baseURL.js.map +1 -0
  105. package/lib/module/checkForUpdate.js +103 -57
  106. package/lib/module/checkForUpdate.js.map +1 -1
  107. package/lib/module/checkForUpdate.releaseCatalog.spec.js +337 -74
  108. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  109. package/lib/module/clientPlugin.js +44 -0
  110. package/lib/module/clientPlugin.js.map +1 -0
  111. package/lib/module/entryImportGraph.spec.js +50 -0
  112. package/lib/module/entryImportGraph.spec.js.map +1 -0
  113. package/lib/module/fetchJSON.js +2 -6
  114. package/lib/module/fetchJSON.js.map +1 -1
  115. package/lib/module/httpClient.js +18 -165
  116. package/lib/module/httpClient.js.map +1 -1
  117. package/lib/module/httpClient.spec.js +27 -200
  118. package/lib/module/httpClient.spec.js.map +1 -1
  119. package/lib/module/index.js +44 -38
  120. package/lib/module/index.js.map +1 -1
  121. package/lib/module/index.spec.js +108 -123
  122. package/lib/module/index.spec.js.map +1 -1
  123. package/lib/module/native.js +63 -44
  124. package/lib/module/native.js.map +1 -1
  125. package/lib/module/native.spec.js +80 -58
  126. package/lib/module/native.spec.js.map +1 -1
  127. package/lib/module/pluginHost.js +200 -0
  128. package/lib/module/pluginHost.js.map +1 -0
  129. package/lib/module/pluginHost.spec.js +291 -0
  130. package/lib/module/pluginHost.spec.js.map +1 -0
  131. package/lib/module/plugins/insights/eventId.js +122 -0
  132. package/lib/module/plugins/insights/eventId.js.map +1 -0
  133. package/lib/module/plugins/insights/eventId.spec.js +47 -0
  134. package/lib/module/plugins/insights/eventId.spec.js.map +1 -0
  135. package/lib/module/plugins/insights/index.js +268 -0
  136. package/lib/module/plugins/insights/index.js.map +1 -0
  137. package/lib/module/plugins/insights/insights.spec.js +639 -0
  138. package/lib/module/plugins/insights/insights.spec.js.map +1 -0
  139. package/lib/module/plugins/insights/sender.js +123 -0
  140. package/lib/module/plugins/insights/sender.js.map +1 -0
  141. package/lib/module/plugins/insights/sender.spec.js +265 -0
  142. package/lib/module/plugins/insights/sender.spec.js.map +1 -0
  143. package/lib/module/plugins/insights/state.js +139 -0
  144. package/lib/module/plugins/insights/state.js.map +1 -0
  145. package/lib/module/releaseCatalogCache.js +13 -2
  146. package/lib/module/releaseCatalogCache.js.map +1 -1
  147. package/lib/module/releaseCatalogCache.spec.js +18 -0
  148. package/lib/module/releaseCatalogCache.spec.js.map +1 -1
  149. package/lib/module/sdkVersion.js +1 -1
  150. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  151. package/lib/module/updateError.js +126 -0
  152. package/lib/module/updateError.js.map +1 -0
  153. package/lib/module/updateError.spec.js +149 -0
  154. package/lib/module/updateError.spec.js.map +1 -0
  155. package/lib/module/wrap.js +43 -25
  156. package/lib/module/wrap.js.map +1 -1
  157. package/lib/module/wrap.progress.spec.js +196 -0
  158. package/lib/module/wrap.progress.spec.js.map +1 -0
  159. package/lib/module/wrap.spec.js +93 -229
  160. package/lib/module/wrap.spec.js.map +1 -1
  161. package/lib/typescript/commonjs/appReady.d.ts +23 -0
  162. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/appReady.test-utils.d.ts +9 -0
  164. package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/baseURL.d.ts +4 -0
  166. package/lib/typescript/commonjs/baseURL.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/checkForUpdate.d.ts +15 -2
  168. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  169. package/lib/typescript/commonjs/clientPlugin.d.ts +185 -0
  170. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/fetchJSON.d.ts +2 -3
  172. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  173. package/lib/typescript/commonjs/httpClient.d.ts +2 -36
  174. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/index.d.ts +3 -6
  176. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  177. package/lib/typescript/commonjs/native.d.ts +29 -15
  178. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/pluginHost.d.ts +29 -0
  180. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  181. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +18 -0
  182. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +1 -0
  183. package/lib/typescript/commonjs/plugins/insights/index.d.ts +40 -0
  184. package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/plugins/insights/sender.d.ts +74 -0
  186. package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/plugins/insights/state.d.ts +46 -0
  188. package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +5 -1
  190. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  191. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  192. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +34 -12
  193. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  195. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  196. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  197. package/lib/typescript/commonjs/wrap.types.d.ts +6 -10
  198. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  199. package/lib/typescript/module/appReady.d.ts +23 -0
  200. package/lib/typescript/module/appReady.d.ts.map +1 -0
  201. package/lib/typescript/module/appReady.test-utils.d.ts +9 -0
  202. package/lib/typescript/module/appReady.test-utils.d.ts.map +1 -0
  203. package/lib/typescript/module/baseURL.d.ts +4 -0
  204. package/lib/typescript/module/baseURL.d.ts.map +1 -0
  205. package/lib/typescript/module/checkForUpdate.d.ts +15 -2
  206. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  207. package/lib/typescript/module/clientPlugin.d.ts +185 -0
  208. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  209. package/lib/typescript/module/fetchJSON.d.ts +2 -3
  210. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  211. package/lib/typescript/module/httpClient.d.ts +2 -36
  212. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  213. package/lib/typescript/module/index.d.ts +3 -6
  214. package/lib/typescript/module/index.d.ts.map +1 -1
  215. package/lib/typescript/module/native.d.ts +29 -15
  216. package/lib/typescript/module/native.d.ts.map +1 -1
  217. package/lib/typescript/module/pluginHost.d.ts +29 -0
  218. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  219. package/lib/typescript/module/plugins/insights/eventId.d.ts +18 -0
  220. package/lib/typescript/module/plugins/insights/eventId.d.ts.map +1 -0
  221. package/lib/typescript/module/plugins/insights/index.d.ts +40 -0
  222. package/lib/typescript/module/plugins/insights/index.d.ts.map +1 -0
  223. package/lib/typescript/module/plugins/insights/sender.d.ts +74 -0
  224. package/lib/typescript/module/plugins/insights/sender.d.ts.map +1 -0
  225. package/lib/typescript/module/plugins/insights/state.d.ts +46 -0
  226. package/lib/typescript/module/plugins/insights/state.d.ts.map +1 -0
  227. package/lib/typescript/module/releaseCatalogCache.d.ts +5 -1
  228. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  229. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  230. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +34 -12
  231. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  232. package/lib/typescript/module/updateError.d.ts +26 -0
  233. package/lib/typescript/module/updateError.d.ts.map +1 -0
  234. package/lib/typescript/module/wrap.d.ts.map +1 -1
  235. package/lib/typescript/module/wrap.types.d.ts +6 -10
  236. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  237. package/package.json +14 -2
  238. package/plugins/insights/package.json +8 -0
  239. package/src/appReady.spec.ts +393 -0
  240. package/src/{notifyAppReadyInsights.test-utils.ts → appReady.test-utils.ts} +7 -7
  241. package/src/appReady.ts +149 -0
  242. package/src/baseURL.ts +15 -0
  243. package/src/checkForUpdate.releaseCatalog.spec.ts +370 -76
  244. package/src/checkForUpdate.ts +135 -80
  245. package/src/clientPlugin.ts +231 -0
  246. package/src/entryImportGraph.spec.ts +65 -0
  247. package/src/fetchJSON.ts +3 -10
  248. package/src/httpClient.spec.ts +30 -227
  249. package/src/httpClient.ts +20 -256
  250. package/src/index.spec.ts +117 -93
  251. package/src/index.ts +67 -52
  252. package/src/native.spec.ts +98 -72
  253. package/src/native.ts +93 -77
  254. package/src/pluginHost.spec.ts +297 -0
  255. package/src/pluginHost.ts +262 -0
  256. package/src/plugins/insights/eventId.spec.ts +60 -0
  257. package/src/plugins/insights/eventId.ts +171 -0
  258. package/src/plugins/insights/index.ts +367 -0
  259. package/src/plugins/insights/insights.spec.ts +732 -0
  260. package/src/plugins/insights/sender.spec.ts +287 -0
  261. package/src/plugins/insights/sender.ts +221 -0
  262. package/src/plugins/insights/state.ts +184 -0
  263. package/src/releaseCatalogCache.spec.ts +22 -0
  264. package/src/releaseCatalogCache.ts +19 -4
  265. package/src/sdkVersion.ts +1 -1
  266. package/src/specs/NativeHotUpdater.ts +34 -13
  267. package/src/updateError.spec.ts +144 -0
  268. package/src/updateError.ts +153 -0
  269. package/src/wrap.progress.spec.ts +232 -0
  270. package/src/wrap.spec.ts +100 -283
  271. package/src/wrap.tsx +57 -26
  272. package/src/wrap.types.ts +6 -12
  273. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -225
  274. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  275. package/lib/commonjs/notifyAppReadyInsights.js +0 -188
  276. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  277. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -378
  278. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  279. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  280. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -171
  281. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  282. package/lib/commonjs/uuidv7.js +0 -41
  283. package/lib/commonjs/uuidv7.js.map +0 -1
  284. package/lib/commonjs/uuidv7.spec.js +0 -30
  285. package/lib/commonjs/uuidv7.spec.js.map +0 -1
  286. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -224
  287. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  288. package/lib/module/notifyAppReadyInsights.js +0 -181
  289. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  290. package/lib/module/notifyAppReadyInsights.spec.js +0 -377
  291. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  292. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  293. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -170
  294. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  295. package/lib/module/uuidv7.js +0 -36
  296. package/lib/module/uuidv7.js.map +0 -1
  297. package/lib/module/uuidv7.spec.js +0 -30
  298. package/lib/module/uuidv7.spec.js.map +0 -1
  299. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -25
  300. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  301. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  302. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  303. package/lib/typescript/commonjs/uuidv7.d.ts +0 -10
  304. package/lib/typescript/commonjs/uuidv7.d.ts.map +0 -1
  305. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -25
  306. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  307. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  308. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  309. package/lib/typescript/module/uuidv7.d.ts +0 -10
  310. package/lib/typescript/module/uuidv7.d.ts.map +0 -1
  311. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -242
  312. package/src/notifyAppReadyInsights.spec.ts +0 -362
  313. package/src/notifyAppReadyInsights.transport.spec.ts +0 -196
  314. package/src/notifyAppReadyInsights.ts +0 -293
  315. package/src/uuidv7.spec.ts +0 -25
  316. package/src/uuidv7.ts +0 -38
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { createReleaseCatalogScopeKey, encodeChannelKey } from "@hot-updater/core";
4
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { InvalidUpdateResponseError, UpdateHttpError } from "./updateError.js";
5
6
  const MINIMUM_RELEASE_ID = "00000000-0000-7000-8000-000000000001";
6
7
  const RELEASE_ID = "00000000-0000-7000-8000-000000000002";
7
8
  const ACTIVE_RELEASE_ID = "00000000-0000-7000-8000-000000000003";
@@ -15,31 +16,38 @@ const SCOPE_KEY = createReleaseCatalogScopeKey({
15
16
  platform: "ios",
16
17
  strategy: "APP_VERSION"
17
18
  });
18
- const mocks = vi.hoisted(() => ({
19
- addListener: vi.fn(() => () => {}),
20
- acceptReleaseCatalog: vi.fn(() => true),
21
- commitReleaseSelection: vi.fn(async () => true),
22
- getActiveUpdateState: vi.fn(() => ({
23
- activeSelection: null,
24
- highestSeenCatalogs: {},
25
- stableSelection: null,
26
- verificationPending: false
27
- })),
28
- getAppVersion: vi.fn(() => "1.2"),
29
- getBundleId: vi.fn(() => CURRENT_BUNDLE_ID),
30
- getChannel: vi.fn(() => CHANNEL),
31
- getCohort: vi.fn(() => "123"),
32
- getCrashHistory: vi.fn(() => []),
33
- getDefaultChannel: vi.fn(() => CHANNEL),
34
- getFingerprintHash: vi.fn(() => null),
35
- getInstallId: vi.fn(() => "install-id"),
36
- getMinBundleId: vi.fn(() => MINIMUM_RELEASE_ID),
37
- getPersistedUserIdentity: vi.fn(() => ({})),
38
- isChannelSwitched: vi.fn(() => false),
39
- isReleaseSelectionCurrent: vi.fn(() => true),
40
- resetChannel: vi.fn(),
41
- updateBundle: vi.fn(async () => true)
42
- }));
19
+ const mocks = vi.hoisted(() => {
20
+ Reflect.set(globalThis, "HotUpdater", {
21
+ SDK_VERSION: "test-sdk-version"
22
+ });
23
+ return {
24
+ addListener: vi.fn(() => () => {}),
25
+ acceptReleaseCatalog: vi.fn(() => true),
26
+ commitReleaseSelection: vi.fn(async () => true),
27
+ getActiveUpdateState: vi.fn(() => ({
28
+ activeSelection: null,
29
+ highestSeenCatalogs: {},
30
+ stableSelection: null,
31
+ verificationPending: false
32
+ })),
33
+ getAppVersion: vi.fn(() => "1.2"),
34
+ getBundleId: vi.fn(() => CURRENT_BUNDLE_ID),
35
+ getChannel: vi.fn(() => CHANNEL),
36
+ getCohort: vi.fn(() => "123"),
37
+ getCrashHistory: vi.fn(() => []),
38
+ getDefaultChannel: vi.fn(() => CHANNEL),
39
+ getFingerprintHash: vi.fn(() => null),
40
+ getInstallId: vi.fn(() => "install-id"),
41
+ getMinBundleId: vi.fn(() => MINIMUM_RELEASE_ID),
42
+ isChannelSwitched: vi.fn(() => false),
43
+ isReleaseSelectionCurrent: vi.fn(() => true),
44
+ resetChannel: vi.fn(),
45
+ stageBundle: vi.fn(async () => ({
46
+ delivery: "manifest",
47
+ patchFallback: false
48
+ }))
49
+ };
50
+ });
43
51
  vi.mock("react-native", () => ({
44
52
  Platform: {
45
53
  OS: "ios"
@@ -74,11 +82,9 @@ const createClient = (catalog = createCatalog()) => {
74
82
  archiveUrl: "https://updates.example.com/bundle.tar.br"
75
83
  };
76
84
  const resolveArtifact = vi.fn(async () => artifact);
77
- const sendInsightsEvent = vi.fn(async () => undefined);
78
85
  const session = {
79
86
  fetchReleaseCatalog,
80
- resolveArtifact,
81
- sendInsightsEvent
87
+ resolveArtifact
82
88
  };
83
89
  const client = {
84
90
  createSession: vi.fn(async () => session)
@@ -86,10 +92,33 @@ const createClient = (catalog = createCatalog()) => {
86
92
  return {
87
93
  client,
88
94
  fetchReleaseCatalog,
89
- resolveArtifact,
90
- sendInsightsEvent
95
+ resolveArtifact
91
96
  };
92
97
  };
98
+ /** Records what checks report to plugins, in order. */
99
+ const recordPluginEvents = async () => {
100
+ const {
101
+ configurePlugins
102
+ } = await import("./pluginHost");
103
+ const events = [];
104
+ configurePlugins([{
105
+ id: "recorder",
106
+ setup: () => ({
107
+ onUpdateCheck: result => {
108
+ events.push(["onUpdateCheck", result]);
109
+ },
110
+ onBundleDownloaded: info => {
111
+ events.push(["onBundleDownloaded", info]);
112
+ },
113
+ onUpdateError: error => {
114
+ events.push(["onUpdateError", error]);
115
+ }
116
+ })
117
+ }], {
118
+ baseURL: "https://updates.example.com"
119
+ });
120
+ return events;
121
+ };
93
122
  describe("checkForUpdate Release catalog protocol", () => {
94
123
  beforeEach(() => {
95
124
  vi.resetModules();
@@ -104,58 +133,74 @@ describe("checkForUpdate Release catalog protocol", () => {
104
133
  });
105
134
  mocks.isReleaseSelectionCurrent.mockReturnValue(true);
106
135
  mocks.commitReleaseSelection.mockResolvedValue(true);
107
- mocks.updateBundle.mockResolvedValue(true);
136
+ mocks.stageBundle.mockResolvedValue({
137
+ delivery: "manifest",
138
+ patchFallback: false
139
+ });
108
140
  });
109
- it("reports an optional download only after staging, without treating the target as running", async () => {
141
+ it("reports the check, then the download only after staging", async () => {
110
142
  const {
111
143
  checkForUpdate
112
144
  } = await import("./checkForUpdate");
145
+ const events = await recordPluginEvents();
113
146
  const {
114
- client,
115
- sendInsightsEvent
147
+ client
116
148
  } = createClient();
117
149
  let complete;
118
- mocks.updateBundle.mockImplementationOnce(() => new Promise(resolve => {
150
+ mocks.stageBundle.mockImplementationOnce(() => new Promise(resolve => {
119
151
  complete = resolve;
120
152
  }));
121
153
  const update = await checkForUpdate({
122
154
  client,
123
- insights: true,
124
155
  updateStrategy: "appVersion"
125
156
  });
126
157
  expect(update?.shouldForceUpdate).toBe(false);
158
+ expect(events).toEqual([["onUpdateCheck", {
159
+ status: "UPDATE_AVAILABLE",
160
+ channel: CHANNEL,
161
+ fromBundleId: CURRENT_BUNDLE_ID,
162
+ fromReleaseId: null,
163
+ toBundleId: TARGET_BUNDLE_ID,
164
+ toReleaseId: RELEASE_ID,
165
+ transitionKind: "INSTALL",
166
+ updateStatus: "UPDATE",
167
+ shouldForceUpdate: false,
168
+ updateStrategy: "appVersion"
169
+ }]]);
127
170
  const pending = update.updateBundle();
128
- await vi.waitFor(() => expect(mocks.updateBundle).toHaveBeenCalledOnce());
129
- expect(sendInsightsEvent).not.toHaveBeenCalled();
130
- complete(true);
171
+ await vi.waitFor(() => expect(mocks.stageBundle).toHaveBeenCalledOnce());
172
+ expect(events).toHaveLength(1);
173
+ complete({
174
+ delivery: "patch",
175
+ patchFallback: true
176
+ });
131
177
  await expect(pending).resolves.toBe(true);
132
- expect(sendInsightsEvent).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
133
- type: "UPDATE_DOWNLOADED",
178
+ expect(events[1]).toEqual(["onBundleDownloaded", {
179
+ channel: CHANNEL,
134
180
  fromBundleId: CURRENT_BUNDLE_ID,
135
- toBundleId: TARGET_BUNDLE_ID,
136
181
  fromReleaseId: null,
182
+ toBundleId: TARGET_BUNDLE_ID,
137
183
  toReleaseId: RELEASE_ID,
138
- updateStrategy: "appVersion"
139
- }));
140
- await update.updateBundle();
141
- expect(sendInsightsEvent).toHaveBeenCalledOnce();
184
+ updateStrategy: "appVersion",
185
+ delivery: "patch",
186
+ patchFallback: true
187
+ }]);
142
188
  });
143
- it.each([false, "failure"])("does not report a download when staging returns %s", async result => {
189
+ it.each(["nothing new", "failure"])("does not report a download when staging brings %s", async result => {
144
190
  const {
145
191
  checkForUpdate
146
192
  } = await import("./checkForUpdate");
193
+ const events = await recordPluginEvents();
147
194
  const {
148
- client,
149
- sendInsightsEvent
195
+ client
150
196
  } = createClient();
151
- if (result === false) mocks.updateBundle.mockResolvedValueOnce(false);else mocks.updateBundle.mockRejectedValueOnce(new Error("download failed"));
197
+ if (result === "nothing new") mocks.stageBundle.mockResolvedValueOnce(null);else mocks.stageBundle.mockRejectedValueOnce(new Error("download failed"));
152
198
  const update = await checkForUpdate({
153
199
  client,
154
- insights: true,
155
200
  updateStrategy: "appVersion"
156
201
  });
157
202
  await update.updateBundle().catch(() => false);
158
- expect(sendInsightsEvent).not.toHaveBeenCalled();
203
+ expect(events.map(([name]) => name)).not.toContain("onBundleDownloaded");
159
204
  });
160
205
  it("selects locally and defers Bundle artifact resolution until install", async () => {
161
206
  const {
@@ -192,7 +237,7 @@ describe("checkForUpdate Release catalog protocol", () => {
192
237
  requestTimeout: undefined,
193
238
  targetBundleId: TARGET_BUNDLE_ID
194
239
  });
195
- expect(mocks.updateBundle).toHaveBeenCalledWith(expect.objectContaining({
240
+ expect(mocks.stageBundle).toHaveBeenCalledWith(expect.objectContaining({
196
241
  bundleId: TARGET_BUNDLE_ID,
197
242
  archiveUrl: "https://updates.example.com/bundle.tar.br",
198
243
  selection: expect.objectContaining({
@@ -230,10 +275,10 @@ describe("checkForUpdate Release catalog protocol", () => {
230
275
  shouldForceUpdate: true
231
276
  }]
232
277
  });
278
+ const events = await recordPluginEvents();
233
279
  const {
234
280
  client,
235
- resolveArtifact,
236
- sendInsightsEvent
281
+ resolveArtifact
237
282
  } = createClient(forceCatalog);
238
283
  const result = await checkForUpdate({
239
284
  client,
@@ -246,8 +291,8 @@ describe("checkForUpdate Release catalog protocol", () => {
246
291
  });
247
292
  await expect(result?.updateBundle()).resolves.toBe(true);
248
293
  expect(resolveArtifact).not.toHaveBeenCalled();
249
- expect(mocks.updateBundle).not.toHaveBeenCalled();
250
- expect(sendInsightsEvent).not.toHaveBeenCalled();
294
+ expect(mocks.stageBundle).not.toHaveBeenCalled();
295
+ expect(events.map(([name]) => name)).not.toContain("onBundleDownloaded");
251
296
  expect(mocks.commitReleaseSelection).toHaveBeenCalledWith({
252
297
  guard: expect.objectContaining({
253
298
  generation: 2,
@@ -259,7 +304,7 @@ describe("checkForUpdate Release catalog protocol", () => {
259
304
  })
260
305
  });
261
306
  });
262
- it("reports same-file selection as UNCHANGED with the selected release identity", async () => {
307
+ it("reports a same-file adoption as UNCHANGED with the adopted Release", async () => {
263
308
  const active = {
264
309
  catalogId: CATALOG_ID,
265
310
  bundleId: TARGET_BUNDLE_ID,
@@ -281,24 +326,22 @@ describe("checkForUpdate Release catalog protocol", () => {
281
326
  const {
282
327
  checkForUpdate
283
328
  } = await import("./checkForUpdate");
329
+ const events = await recordPluginEvents();
284
330
  const {
285
- client,
286
- sendInsightsEvent
331
+ client
287
332
  } = createClient();
288
333
  const result = await checkForUpdate({
289
- insights: true,
290
334
  client,
291
335
  updateStrategy: "appVersion"
292
336
  });
293
337
  await result?.updateBundle();
294
- expect(sendInsightsEvent).toHaveBeenCalledWith(expect.objectContaining({
295
- fromBundleId: null,
296
- updateStrategy: null,
297
- fromReleaseId: MINIMUM_RELEASE_ID,
298
- toBundleId: TARGET_BUNDLE_ID,
299
- toReleaseId: RELEASE_ID,
300
- type: "UNCHANGED"
301
- }));
338
+ expect(events.at(-1)).toEqual(["onUpdateCheck", {
339
+ status: "UNCHANGED",
340
+ channel: CHANNEL,
341
+ bundleId: TARGET_BUNDLE_ID,
342
+ releaseId: RELEASE_ID,
343
+ previousReleaseId: MINIMUM_RELEASE_ID
344
+ }]);
302
345
  });
303
346
  it("selects a lower-id Release when explicitly switching scopes", async () => {
304
347
  const betaChannel = "beta";
@@ -382,7 +425,7 @@ describe("checkForUpdate Release catalog protocol", () => {
382
425
  });
383
426
  await expect(result?.updateBundle()).resolves.toBe(true);
384
427
  expect(resolveArtifact).not.toHaveBeenCalled();
385
- expect(mocks.updateBundle).not.toHaveBeenCalled();
428
+ expect(mocks.stageBundle).not.toHaveBeenCalled();
386
429
  expect(mocks.commitReleaseSelection).toHaveBeenCalledWith({
387
430
  guard: expect.objectContaining({
388
431
  catalogHash: CATALOG_HASH,
@@ -413,7 +456,7 @@ describe("checkForUpdate Release catalog protocol", () => {
413
456
  updateStrategy: "appVersion"
414
457
  });
415
458
  await expect(result?.updateBundle()).rejects.toBeInstanceOf(StaleReleaseCatalogError);
416
- expect(mocks.updateBundle).not.toHaveBeenCalled();
459
+ expect(mocks.stageBundle).not.toHaveBeenCalled();
417
460
  });
418
461
  it("installs an older enabled Release as a forced rollback", async () => {
419
462
  const active = {
@@ -462,7 +505,7 @@ describe("checkForUpdate Release catalog protocol", () => {
462
505
  transitionKind: "INSTALL"
463
506
  });
464
507
  await expect(result?.updateBundle()).resolves.toBe(true);
465
- expect(mocks.updateBundle).toHaveBeenCalledWith(expect.objectContaining({
508
+ expect(mocks.stageBundle).toHaveBeenCalledWith(expect.objectContaining({
466
509
  bundleId: TARGET_BUNDLE_ID,
467
510
  status: "ROLLBACK"
468
511
  }));
@@ -563,7 +606,7 @@ describe("checkForUpdate Release catalog protocol", () => {
563
606
  })
564
607
  }));
565
608
  expect(resolveArtifact).not.toHaveBeenCalled();
566
- expect(mocks.updateBundle).not.toHaveBeenCalled();
609
+ expect(mocks.stageBundle).not.toHaveBeenCalled();
567
610
  });
568
611
  it("does not surface a rollback when the app already uses built-in bytes", async () => {
569
612
  mocks.getBundleId.mockReturnValueOnce(MINIMUM_RELEASE_ID);
@@ -627,7 +670,7 @@ describe("checkForUpdate Release catalog protocol", () => {
627
670
  }));
628
671
  expect(mocks.acceptReleaseCatalog).not.toHaveBeenCalled();
629
672
  expect(mocks.commitReleaseSelection).not.toHaveBeenCalled();
630
- expect(mocks.updateBundle).not.toHaveBeenCalled();
673
+ expect(mocks.stageBundle).not.toHaveBeenCalled();
631
674
  });
632
675
  it("rejects an unexpected Catalog identity before accepting catalog state", async () => {
633
676
  const active = {
@@ -668,5 +711,225 @@ describe("checkForUpdate Release catalog protocol", () => {
668
711
  }));
669
712
  expect(mocks.acceptReleaseCatalog).not.toHaveBeenCalled();
670
713
  });
714
+ it("reports a check that found nothing to install as UNCHANGED", async () => {
715
+ mocks.getBundleId.mockReturnValueOnce(MINIMUM_RELEASE_ID);
716
+ const {
717
+ checkForUpdate
718
+ } = await import("./checkForUpdate");
719
+ const events = await recordPluginEvents();
720
+ const {
721
+ client
722
+ } = createClient(createCatalog({
723
+ releases: [],
724
+ rollbackReleases: []
725
+ }));
726
+ await expect(checkForUpdate({
727
+ client,
728
+ updateStrategy: "appVersion"
729
+ })).resolves.toBeNull();
730
+ expect(events).toEqual([["onUpdateCheck", {
731
+ status: "UNCHANGED",
732
+ channel: CHANNEL,
733
+ bundleId: MINIMUM_RELEASE_ID,
734
+ releaseId: null,
735
+ previousReleaseId: null
736
+ }]]);
737
+ });
738
+ it("treats a scope without a catalog as no update, not a failure", async () => {
739
+ const onError = vi.fn();
740
+ const {
741
+ checkForUpdate
742
+ } = await import("./checkForUpdate");
743
+ const events = await recordPluginEvents();
744
+ const {
745
+ client,
746
+ fetchReleaseCatalog
747
+ } = createClient();
748
+ fetchReleaseCatalog.mockResolvedValueOnce(null);
749
+ await expect(checkForUpdate({
750
+ client,
751
+ onError,
752
+ updateStrategy: "appVersion"
753
+ })).resolves.toBeNull();
754
+ expect(onError).not.toHaveBeenCalled();
755
+ expect(mocks.acceptReleaseCatalog).not.toHaveBeenCalled();
756
+ expect(events).toEqual([["onUpdateCheck", expect.objectContaining({
757
+ status: "UNCHANGED",
758
+ channel: CHANNEL
759
+ })]]);
760
+ });
761
+ it.each([{
762
+ label: "an HTTP status",
763
+ error: () => new UpdateHttpError(503, "Service Unavailable"),
764
+ expected: {
765
+ reason: "http",
766
+ httpStatus: 503
767
+ }
768
+ }, {
769
+ label: "no response",
770
+ error: () => new TypeError("Network request failed"),
771
+ expected: {
772
+ reason: "network"
773
+ }
774
+ }, {
775
+ label: "a 404 without the no-catalog mark",
776
+ error: () => new UpdateHttpError(404, "Not Found"),
777
+ expected: {
778
+ reason: "http",
779
+ httpStatus: 404
780
+ }
781
+ }, {
782
+ label: "a timeout",
783
+ error: () => new Error("Request timed out"),
784
+ expected: {
785
+ reason: "network",
786
+ transport: "timeout"
787
+ }
788
+ }, {
789
+ label: "an invalid catalog",
790
+ error: () => new InvalidUpdateResponseError("Received an invalid Release catalog"),
791
+ expected: {
792
+ reason: "invalid_response"
793
+ }
794
+ }])("reports a check failed by $label", async ({
795
+ error,
796
+ expected
797
+ }) => {
798
+ const {
799
+ checkForUpdate
800
+ } = await import("./checkForUpdate");
801
+ const events = await recordPluginEvents();
802
+ const {
803
+ client,
804
+ fetchReleaseCatalog
805
+ } = createClient();
806
+ const cause = error();
807
+ fetchReleaseCatalog.mockRejectedValueOnce(cause);
808
+ await expect(checkForUpdate({
809
+ client,
810
+ updateStrategy: "appVersion"
811
+ })).resolves.toBeNull();
812
+ expect(events).toEqual([["onUpdateError", {
813
+ stage: "check",
814
+ resource: "catalog",
815
+ ...expected,
816
+ channel: CHANNEL,
817
+ bundleId: CURRENT_BUNDLE_ID,
818
+ releaseId: null,
819
+ updateStrategy: "appVersion",
820
+ cause
821
+ }]]);
822
+ });
823
+ it.each([{
824
+ label: "an artifact request answered 500",
825
+ fail: resolveArtifact => resolveArtifact.mockRejectedValueOnce(new UpdateHttpError(500, "Internal Server Error")),
826
+ expected: {
827
+ stage: "download",
828
+ reason: "http",
829
+ resource: "artifact",
830
+ httpStatus: 500
831
+ }
832
+ }, {
833
+ label: "native extraction",
834
+ fail: () => mocks.stageBundle.mockRejectedValueOnce(Object.assign(new Error("Failed to extract archive"), {
835
+ code: "UNKNOWN_ERROR",
836
+ userInfo: {
837
+ reason: "extract",
838
+ stage: "install"
839
+ }
840
+ })),
841
+ expected: {
842
+ stage: "install",
843
+ reason: "extract"
844
+ }
845
+ }, {
846
+ label: "a native HTTP status",
847
+ fail: () => mocks.stageBundle.mockRejectedValueOnce(Object.assign(new Error("Failed to download bundle"), {
848
+ code: "DOWNLOAD_FAILED",
849
+ userInfo: {
850
+ httpStatus: 403,
851
+ originCode: "ExpiredToken",
852
+ reason: "http",
853
+ resource: "archive",
854
+ stage: "download"
855
+ }
856
+ })),
857
+ expected: {
858
+ stage: "download",
859
+ reason: "http",
860
+ resource: "archive",
861
+ httpStatus: 403,
862
+ originCode: "ExpiredToken"
863
+ }
864
+ }, {
865
+ label: "a native connection loss",
866
+ fail: () => mocks.stageBundle.mockRejectedValueOnce(Object.assign(new Error("The network connection was lost."), {
867
+ code: "DOWNLOAD_FAILED",
868
+ userInfo: {
869
+ reason: "network",
870
+ resource: "file",
871
+ stage: "download",
872
+ transport: "connection"
873
+ }
874
+ })),
875
+ expected: {
876
+ stage: "download",
877
+ reason: "network",
878
+ resource: "file",
879
+ transport: "connection"
880
+ }
881
+ }])("reports an install failed by $label and rethrows it", async ({
882
+ fail,
883
+ expected
884
+ }) => {
885
+ const {
886
+ checkForUpdate
887
+ } = await import("./checkForUpdate");
888
+ const events = await recordPluginEvents();
889
+ const {
890
+ client,
891
+ resolveArtifact
892
+ } = createClient();
893
+ fail(resolveArtifact);
894
+ const result = await checkForUpdate({
895
+ client,
896
+ updateStrategy: "appVersion"
897
+ });
898
+ await expect(result.updateBundle()).rejects.toThrow();
899
+ expect(events.filter(([name]) => name === "onUpdateError")).toEqual([["onUpdateError", expect.objectContaining({
900
+ ...expected,
901
+ channel: CHANNEL,
902
+ bundleId: CURRENT_BUNDLE_ID,
903
+ targetBundleId: TARGET_BUNDLE_ID,
904
+ targetReleaseId: RELEASE_ID,
905
+ updateStrategy: "appVersion"
906
+ })]]);
907
+ });
908
+ it.each([{
909
+ label: "a native rejection without a failure class",
910
+ fail: () => mocks.stageBundle.mockRejectedValueOnce(Object.assign(new Error("Release catalog selection is stale"), {
911
+ code: "UNKNOWN_ERROR"
912
+ }))
913
+ }, {
914
+ label: "a selection that became stale",
915
+ fail: () => mocks.isReleaseSelectionCurrent.mockReturnValueOnce(false)
916
+ }])("does not report $label as an update failure", async ({
917
+ fail
918
+ }) => {
919
+ const {
920
+ checkForUpdate
921
+ } = await import("./checkForUpdate");
922
+ const events = await recordPluginEvents();
923
+ const {
924
+ client
925
+ } = createClient();
926
+ const result = await checkForUpdate({
927
+ client,
928
+ updateStrategy: "appVersion"
929
+ });
930
+ fail();
931
+ await expect(result.updateBundle()).rejects.toThrow();
932
+ expect(events.map(([name]) => name)).toEqual(["onUpdateCheck"]);
933
+ });
671
934
  });
672
935
  //# sourceMappingURL=checkForUpdate.releaseCatalog.spec.js.map