@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
package/src/wrap.spec.ts CHANGED
@@ -4,9 +4,11 @@ import { cleanup, render, waitFor } from "@testing-library/react";
4
4
  import { createElement, type ComponentType } from "react";
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
6
 
7
+ import { createNotifyReadResult } from "./appReady.test-utils";
8
+ import type { HotUpdaterClientPlugin } from "./clientPlugin";
7
9
  import type {
8
10
  ActiveUpdateState,
9
- NotifyAppReadyInsightsEvent,
11
+ LaunchTransition,
10
12
  NotifyAppReadyResult,
11
13
  } from "./native";
12
14
  import type { HotUpdaterOptions } from "./wrap";
@@ -17,42 +19,31 @@ vi.mock("react-native", () => ({
17
19
  },
18
20
  }));
19
21
 
20
- const createNotifyReadResult = (
21
- result: NotifyAppReadyResult = { status: "UNCHANGED" },
22
- insightsEvent: NotifyAppReadyInsightsEvent | null = null,
23
- pending = false,
24
- ): {
25
- insightsEvent: NotifyAppReadyInsightsEvent | null;
26
- pending: boolean;
27
- result: NotifyAppReadyResult;
28
- } => ({
29
- insightsEvent,
30
- pending,
31
- result,
22
+ const mocks = vi.hoisted(() => {
23
+ Reflect.set(globalThis, "HotUpdater", { SDK_VERSION: "test-sdk-version" });
24
+ return {
25
+ addListener: vi.fn(() => () => {}),
26
+ checkForUpdate: vi.fn(),
27
+ getAppVersion: vi.fn(() => "1.0.0"),
28
+ getBundleId: vi.fn(() => "bundle-id"),
29
+ getUpdateId: vi.fn(() => "release-id"),
30
+ getChannel: vi.fn(() => "production"),
31
+ getActiveUpdateState: vi.fn<() => ActiveUpdateState>(),
32
+ getCohort: vi.fn(() => "123"),
33
+ getFingerprintHash: vi.fn(() => "fingerprint-hash"),
34
+ getInstallId: vi.fn(() => "install-id"),
35
+ readNotifyAppReady: vi.fn<
36
+ () => {
37
+ transition: LaunchTransition | null;
38
+ previousProcessExit: string | null;
39
+ pending: boolean;
40
+ result: NotifyAppReadyResult;
41
+ }
42
+ >(() => createNotifyReadResult()),
43
+ reload: vi.fn(),
44
+ };
32
45
  });
33
46
 
34
- const mocks = vi.hoisted(() => ({
35
- addListener: vi.fn(() => () => {}),
36
- checkForUpdate: vi.fn(),
37
- getAppVersion: vi.fn(() => "1.0.0"),
38
- getBundleId: vi.fn(() => "bundle-id"),
39
- getUpdateId: vi.fn(() => "release-id"),
40
- getChannel: vi.fn(() => "production"),
41
- getActiveUpdateState: vi.fn<() => ActiveUpdateState>(),
42
- getCohort: vi.fn(() => "123"),
43
- getFingerprintHash: vi.fn(() => "fingerprint-hash"),
44
- getInstallId: vi.fn(() => "install-id"),
45
- getPersistedUserIdentity: vi.fn(() => ({})),
46
- readNotifyAppReady: vi.fn<
47
- () => {
48
- insightsEvent: NotifyAppReadyInsightsEvent | null;
49
- pending: boolean;
50
- result: NotifyAppReadyResult;
51
- }
52
- >(() => createNotifyReadResult()),
53
- reload: vi.fn(),
54
- }));
55
-
56
47
  vi.mock("./checkForUpdate", () => ({
57
48
  checkForUpdate: mocks.checkForUpdate,
58
49
  }));
@@ -67,24 +58,30 @@ vi.mock("./native", () => ({
67
58
  getCohort: mocks.getCohort,
68
59
  getFingerprintHash: mocks.getFingerprintHash,
69
60
  getInstallId: mocks.getInstallId,
70
- getPersistedUserIdentity: mocks.getPersistedUserIdentity,
71
61
  readNotifyAppReady: mocks.readNotifyAppReady,
72
62
  reload: mocks.reload,
73
63
  }));
74
64
 
75
- const createClient = (
76
- sendInsightsEvent = vi.fn().mockResolvedValue(undefined),
77
- ) => ({
65
+ const createClient = () => ({
78
66
  client: {
79
67
  createSession: vi.fn(async () => ({
80
68
  fetchReleaseCatalog: vi.fn(),
81
69
  resolveArtifact: vi.fn(),
82
- sendInsightsEvent,
83
70
  })),
84
71
  },
85
- sendInsightsEvent,
86
72
  });
87
73
 
74
+ const configureRecorder = async () => {
75
+ const { configurePlugins } = await import("./pluginHost");
76
+ const onAppReady = vi.fn();
77
+ const plugin: HotUpdaterClientPlugin = {
78
+ id: "recorder",
79
+ setup: () => ({ onAppReady }),
80
+ };
81
+ configurePlugins([plugin], { baseURL: "https://updates.example.com" });
82
+ return onAppReady;
83
+ };
84
+
88
85
  describe("HotUpdater wrap initialization", () => {
89
86
  beforeEach(() => {
90
87
  vi.resetModules();
@@ -98,7 +95,6 @@ describe("HotUpdater wrap initialization", () => {
98
95
  mocks.checkForUpdate.mockResolvedValue(null);
99
96
  mocks.addListener.mockReturnValue(() => {});
100
97
  mocks.getAppVersion.mockReturnValue("1.0.0");
101
- mocks.getPersistedUserIdentity.mockReturnValue({});
102
98
  mocks.getBundleId.mockReturnValue("bundle-id");
103
99
  mocks.getUpdateId.mockReturnValue("release-id");
104
100
  mocks.getChannel.mockReturnValue("production");
@@ -115,8 +111,44 @@ describe("HotUpdater wrap initialization", () => {
115
111
 
116
112
  afterEach(cleanup);
117
113
 
118
- it("does not report No change while an optional download is in progress", async () => {
119
- const { client, sendInsightsEvent } = createClient();
114
+ it("reports the console ID when up to date and the launch to plugins", async () => {
115
+ const onAppReady = await configureRecorder();
116
+ const onUpdateProcessCompleted = vi.fn();
117
+ const { client } = createClient();
118
+ const { wrap } = await import("./wrap");
119
+ const WrappedComponent = wrap({
120
+ client,
121
+ onUpdateProcessCompleted,
122
+ updateStrategy: "appVersion",
123
+ })(() => null);
124
+
125
+ render(createElement(WrappedComponent));
126
+
127
+ await waitFor(() =>
128
+ expect(onUpdateProcessCompleted).toHaveBeenCalledWith({
129
+ id: "release-id",
130
+ message: null,
131
+ shouldForceUpdate: false,
132
+ status: "UP_TO_DATE",
133
+ }),
134
+ );
135
+ expect(onAppReady).toHaveBeenCalledExactlyOnceWith({
136
+ status: "UNCHANGED",
137
+ channel: "production",
138
+ bundleId: "bundle-id",
139
+ releaseId: null,
140
+ previousProcessExit: null,
141
+ });
142
+ expect(mocks.checkForUpdate).toHaveBeenCalledWith({
143
+ client,
144
+ onError: undefined,
145
+ requestHeaders: undefined,
146
+ requestTimeout: undefined,
147
+ updateStrategy: "appVersion",
148
+ });
149
+ });
150
+
151
+ it("completes an optional update while its download continues", async () => {
120
152
  let complete!: (success: boolean) => void;
121
153
  const download = vi.fn(
122
154
  () =>
@@ -134,49 +166,23 @@ describe("HotUpdater wrap initialization", () => {
134
166
  const { wrap } = await import("./wrap");
135
167
  const onUpdateProcessCompleted = vi.fn();
136
168
  const Wrapped = wrap({
137
- client,
138
- insights: true,
169
+ client: createClient().client,
139
170
  updateStrategy: "appVersion",
140
171
  onUpdateProcessCompleted,
141
172
  })(() => null);
173
+
142
174
  render(createElement(Wrapped));
175
+
143
176
  await waitFor(() => expect(download).toHaveBeenCalledOnce());
144
- expect(onUpdateProcessCompleted).toHaveBeenCalledOnce();
145
- expect(sendInsightsEvent).not.toHaveBeenCalled();
177
+ expect(onUpdateProcessCompleted).toHaveBeenCalledExactlyOnceWith({
178
+ id: "next",
179
+ message: null,
180
+ shouldForceUpdate: false,
181
+ status: "UPDATE",
182
+ });
146
183
  complete(true);
147
184
  });
148
185
 
149
- it("reports the console ID when up to date while retaining file IDs in insights", async () => {
150
- const onUpdateProcessCompleted = vi.fn();
151
- const { client, sendInsightsEvent } = createClient();
152
- const { wrap } = await import("./wrap");
153
- const WrappedComponent = wrap({
154
- insights: true,
155
- client,
156
- onUpdateProcessCompleted,
157
- updateStrategy: "appVersion",
158
- })(() => null);
159
-
160
- render(createElement(WrappedComponent));
161
-
162
- await waitFor(() =>
163
- expect(onUpdateProcessCompleted).toHaveBeenCalledWith({
164
- id: "release-id",
165
- message: null,
166
- shouldForceUpdate: false,
167
- status: "UP_TO_DATE",
168
- }),
169
- );
170
- await waitFor(() =>
171
- expect(sendInsightsEvent).toHaveBeenCalledWith(
172
- expect.objectContaining({
173
- toBundleId: "bundle-id",
174
- type: "UNCHANGED",
175
- }),
176
- ),
177
- );
178
- });
179
-
180
186
  it("returns void from init and defers notifyAppReady to the next frame", async () => {
181
187
  vi.useFakeTimers();
182
188
 
@@ -187,8 +193,9 @@ describe("HotUpdater wrap initialization", () => {
187
193
  },
188
194
  );
189
195
  vi.stubGlobal("requestAnimationFrame", requestAnimationFrame);
196
+ const onAppReady = await configureRecorder();
190
197
 
191
- const { client, sendInsightsEvent } = createClient();
198
+ const { client } = createClient();
192
199
  const { init } = await import("./wrap");
193
200
 
194
201
  const result = init({
@@ -201,214 +208,13 @@ describe("HotUpdater wrap initialization", () => {
201
208
 
202
209
  expect(result).toBeUndefined();
203
210
  expect(mocks.readNotifyAppReady).not.toHaveBeenCalled();
204
- expect(sendInsightsEvent).not.toHaveBeenCalled();
205
-
211
+ expect(onAppReady).not.toHaveBeenCalled();
206
212
  expect(requestAnimationFrame).toHaveBeenCalled();
207
213
 
208
214
  await vi.runOnlyPendingTimersAsync();
209
215
 
210
216
  expect(mocks.readNotifyAppReady).toHaveBeenCalledWith();
211
- expect(sendInsightsEvent).not.toHaveBeenCalled();
212
- });
213
-
214
- it("waits for native launch verification before sending insights", async () => {
215
- vi.useFakeTimers();
216
-
217
- vi.stubGlobal(
218
- "requestAnimationFrame",
219
- vi.fn((callback: (timestamp: number) => void) => {
220
- setTimeout(() => callback(0), 0);
221
- return 1;
222
- }),
223
- );
224
-
225
- mocks.readNotifyAppReady
226
- .mockReturnValueOnce(createNotifyReadResult(undefined, null, true))
227
- .mockReturnValueOnce(
228
- createNotifyReadResult(
229
- {
230
- fromBundleId: "bundle-a",
231
- status: "UPDATE_APPLIED",
232
- toBundleId: "bundle-b",
233
- },
234
- {
235
- fromBundleId: "bundle-a",
236
- toBundleId: "bundle-b",
237
- type: "UPDATE_APPLIED",
238
- updateStrategy: "appVersion",
239
- },
240
- ),
241
- );
242
-
243
- const { client, sendInsightsEvent } = createClient();
244
- const { init } = await import("./wrap");
245
-
246
- init({ insights: true, client });
247
-
248
- await vi.runAllTimersAsync();
249
-
250
- expect(mocks.readNotifyAppReady).toHaveBeenCalledTimes(2);
251
- expect(sendInsightsEvent).toHaveBeenCalledTimes(1);
252
- });
253
-
254
- it("sends automatic insights only from init when enabled", async () => {
255
- vi.useFakeTimers();
256
-
257
- vi.stubGlobal(
258
- "requestAnimationFrame",
259
- vi.fn((callback: (timestamp: number) => void) => {
260
- setTimeout(() => callback(0), 0);
261
- return 1;
262
- }),
263
- );
264
-
265
- mocks.readNotifyAppReady.mockReturnValue(
266
- createNotifyReadResult(
267
- {
268
- fromBundleId: "bundle-a",
269
- status: "UPDATE_APPLIED",
270
- toBundleId: "bundle-b",
271
- },
272
- {
273
- fromBundleId: "bundle-a",
274
- toBundleId: "bundle-b",
275
- type: "UPDATE_APPLIED",
276
- updateStrategy: "fingerprint",
277
- },
278
- ),
279
- );
280
-
281
- mocks.getPersistedUserIdentity.mockReturnValue({
282
- userId: "user-123",
283
- username: "alice",
284
- });
285
-
286
- const { client, sendInsightsEvent } = createClient();
287
- const { init } = await import("./wrap");
288
-
289
- init({
290
- insights: true,
291
- requestHeaders: {
292
- Authorization: "Bearer token",
293
- },
294
- requestTimeout: 1000,
295
- client,
296
- });
297
-
298
- await vi.runOnlyPendingTimersAsync();
299
-
300
- expect(sendInsightsEvent).toHaveBeenCalledWith({
301
- appVersion: "1.0.0",
302
- channel: "production",
303
- cohort: "123",
304
- fingerprintHash: "fingerprint-hash",
305
- fromBundleId: "bundle-a",
306
- fromReleaseId: null,
307
- installId: "install-id",
308
- platform: "ios",
309
- requestHeaders: {
310
- Authorization: "Bearer token",
311
- },
312
- requestTimeout: 1000,
313
- toBundleId: "bundle-b",
314
- toReleaseId: null,
315
- type: "UPDATE_APPLIED",
316
- updateStrategy: "fingerprint",
317
- userId: "user-123",
318
- username: "alice",
319
- });
320
- });
321
-
322
- it("guards automatic insights to a single delivery attempt per runtime", async () => {
323
- vi.useFakeTimers();
324
-
325
- vi.stubGlobal(
326
- "requestAnimationFrame",
327
- vi.fn((callback: (timestamp: number) => void) => {
328
- setTimeout(() => callback(0), 0);
329
- return 1;
330
- }),
331
- );
332
-
333
- mocks.readNotifyAppReady.mockReturnValue(
334
- createNotifyReadResult(
335
- {
336
- fromBundleId: "bundle-a",
337
- status: "RECOVERED",
338
- toBundleId: "bundle-b",
339
- },
340
- {
341
- fromBundleId: "bundle-a",
342
- toBundleId: "bundle-b",
343
- type: "RECOVERED",
344
- updateStrategy: "appVersion",
345
- },
346
- ),
347
- );
348
-
349
- const { client, sendInsightsEvent } = createClient();
350
- const { init } = await import("./wrap");
351
-
352
- init({ insights: true, client });
353
- init({ insights: true, client });
354
-
355
- await vi.runOnlyPendingTimersAsync();
356
-
357
- expect(sendInsightsEvent).toHaveBeenCalledTimes(1);
358
- });
359
-
360
- it("warns without interrupting app readiness when insights transport fails", async () => {
361
- vi.useFakeTimers();
362
-
363
- const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
364
- const error = new Error("Expected HTTP 204 from /events, received 404");
365
- const onError = vi.fn();
366
- const onNotifyAppReady = vi.fn();
367
- vi.stubGlobal(
368
- "requestAnimationFrame",
369
- vi.fn((callback: (timestamp: number) => void) => {
370
- setTimeout(() => callback(0), 0);
371
- return 1;
372
- }),
373
- );
374
- mocks.readNotifyAppReady.mockReturnValue(
375
- createNotifyReadResult(
376
- {
377
- fromBundleId: "bundle-a",
378
- status: "UPDATE_APPLIED",
379
- toBundleId: "bundle-b",
380
- },
381
- {
382
- fromBundleId: "bundle-a",
383
- toBundleId: "bundle-b",
384
- type: "UPDATE_APPLIED",
385
- updateStrategy: "appVersion",
386
- },
387
- ),
388
- );
389
- const { client } = createClient(vi.fn().mockRejectedValue(error));
390
- const { init } = await import("./wrap");
391
-
392
- init({
393
- insights: true,
394
- onError,
395
- onNotifyAppReady,
396
- client,
397
- });
398
-
399
- await vi.runOnlyPendingTimersAsync();
400
-
401
- expect(onError).not.toHaveBeenCalled();
402
- expect(warn).toHaveBeenCalledWith(
403
- "[HotUpdater] Automatic notifyAppReady insights failed:",
404
- error,
405
- );
406
- expect(onNotifyAppReady).toHaveBeenCalledWith({
407
- fromBundleId: "bundle-a",
408
- status: "UPDATE_APPLIED",
409
- toBundleId: "bundle-b",
410
- });
411
- warn.mockRestore();
217
+ expect(onAppReady).toHaveBeenCalledOnce();
412
218
  });
413
219
 
414
220
  it("does not accept a manual wrap HOC", async () => {
@@ -440,6 +246,17 @@ describe("HotUpdater wrap initialization", () => {
440
246
  updateStrategy: "appVersion",
441
247
  } satisfies HotUpdaterOptions;
442
248
 
249
+ const assertRemovedOptionStaysRejected = () => {
250
+ const withInsights: HotUpdaterOptions = {
251
+ baseURL: "https://updates.example.com",
252
+ updateStrategy: "appVersion",
253
+ // @ts-expect-error Insights is the insights() plugin, not an option.
254
+ insights: true,
255
+ };
256
+ void withInsights;
257
+ };
258
+
259
+ expect(assertRemovedOptionStaysRejected).toBeTypeOf("function");
443
260
  expect(autoOptions.updateStrategy).toBe("appVersion");
444
261
  });
445
262
  });
package/src/wrap.tsx CHANGED
@@ -1,11 +1,12 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
 
3
+ import { handleNotifyAppReady } from "./appReady";
3
4
  import { checkForUpdate } from "./checkForUpdate";
4
5
  import { useEventCallback } from "./hooks/useEventCallback";
5
6
  import { getUpdateId, reload } from "./native";
6
- import { handleNotifyAppReady, reportNoChange } from "./notifyAppReadyInsights";
7
7
  import { useHotUpdaterStore } from "./store";
8
8
  import type {
9
+ HotUpdaterFallbackComponentProps,
9
10
  InternalInitOptions,
10
11
  InternalWrapOptions,
11
12
  UpdateStatus,
@@ -32,11 +33,46 @@ export function wrap(
32
33
  ) => React.ComponentType<P> {
33
34
  const { reloadOnForceUpdate = true, ...restOptions } = options;
34
35
 
36
+ // Progress is subscribed only where it is rendered or reported, so progress
37
+ // events don't re-render the wrapped app.
38
+ const ProgressReporter = ({
39
+ onProgress,
40
+ }: {
41
+ onProgress: (progress: number) => void;
42
+ }) => {
43
+ const progress = useHotUpdaterStore((state) => state.progress);
44
+
45
+ useEffect(() => {
46
+ onProgress(progress);
47
+ }, [progress]);
48
+
49
+ return null;
50
+ };
51
+
52
+ const FallbackWithProgress = ({
53
+ Fallback,
54
+ status,
55
+ message,
56
+ }: {
57
+ Fallback: React.FC<HotUpdaterFallbackComponentProps>;
58
+ status: HotUpdaterFallbackComponentProps["status"];
59
+ message: string | null;
60
+ }) => {
61
+ const progressState = useHotUpdaterStore((state) => state);
62
+
63
+ return (
64
+ <Fallback
65
+ artifactType={progressState.artifactType}
66
+ details={progressState.details}
67
+ progress={progressState.progress}
68
+ status={status}
69
+ message={message}
70
+ />
71
+ );
72
+ };
73
+
35
74
  return <P extends object>(WrappedComponent: React.ComponentType<P>) => {
36
75
  const HotUpdaterHOC: React.FC<P> = (props: P) => {
37
- const progressState = useHotUpdaterStore((state) => state);
38
- const progress = progressState.progress;
39
-
40
76
  const [message, setMessage] = useState<string | null>(null);
41
77
  const [updateStatus, setUpdateStatus] =
42
78
  useState<UpdateStatus>("CHECK_FOR_UPDATE");
@@ -45,12 +81,8 @@ export function wrap(
45
81
  try {
46
82
  setUpdateStatus("CHECK_FOR_UPDATE");
47
83
 
48
- const readiness = handleNotifyAppReady({
49
- ...restOptions,
50
- reportUnchanged: false,
51
- });
84
+ const readiness = handleNotifyAppReady(restOptions);
52
85
  const updateInfo = await checkForUpdate({
53
- insights: restOptions.insights,
54
86
  client: restOptions.client,
55
87
  updateStrategy: restOptions.updateStrategy,
56
88
  requestHeaders: restOptions.requestHeaders,
@@ -58,12 +90,10 @@ export function wrap(
58
90
  onError: restOptions.onError,
59
91
  });
60
92
 
61
- const launch = await readiness;
93
+ await readiness;
62
94
  setMessage(updateInfo?.message ?? null);
63
95
 
64
96
  if (!updateInfo) {
65
- if (launch?.status === "UNCHANGED")
66
- void reportNoChange(restOptions);
67
97
  restOptions.onUpdateProcessCompleted?.({
68
98
  status: "UP_TO_DATE",
69
99
  shouldForceUpdate: false,
@@ -118,32 +148,33 @@ export function wrap(
118
148
  }
119
149
  });
120
150
 
121
- useEffect(() => {
122
- restOptions.onProgress?.(progress);
123
- }, [progress]);
124
-
125
151
  // Start update check
126
152
  useEffect(() => {
127
153
  initHotUpdater();
128
154
  }, []);
129
155
 
130
- if (
156
+ const content =
131
157
  restOptions.fallbackComponent &&
132
- updateStatus !== "UPDATE_PROCESS_COMPLETED"
133
- ) {
134
- const Fallback = restOptions.fallbackComponent;
135
- return (
136
- <Fallback
137
- artifactType={progressState.artifactType}
138
- details={progressState.details}
139
- progress={progress}
158
+ updateStatus !== "UPDATE_PROCESS_COMPLETED" ? (
159
+ <FallbackWithProgress
160
+ Fallback={restOptions.fallbackComponent}
140
161
  status={updateStatus}
141
162
  message={message}
142
163
  />
164
+ ) : (
165
+ <WrappedComponent {...props} />
143
166
  );
167
+
168
+ if (!restOptions.onProgress) {
169
+ return content;
144
170
  }
145
171
 
146
- return <WrappedComponent {...props} />;
172
+ return (
173
+ <>
174
+ {content}
175
+ <ProgressReporter onProgress={restOptions.onProgress} />
176
+ </>
177
+ );
147
178
  };
148
179
 
149
180
  return HotUpdaterHOC as React.ComponentType<P>;
package/src/wrap.types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type React from "react";
2
2
 
3
+ import type { HotUpdaterClientPlugin } from "./clientPlugin";
3
4
  import type { HotUpdaterError } from "./error";
4
5
  import type { HotUpdaterHttpClient } from "./httpClient";
5
6
  import type { NotifyAppReadyResult } from "./native";
@@ -28,17 +29,12 @@ export type HotUpdaterFallbackComponentProps = {
28
29
 
29
30
  interface CommonHotUpdaterOptions {
30
31
  /**
31
- * Sends app-ready transitions and same-Bundle Release adoptions to the
32
- * configured server. Defaults to `true`; set to `false` to disable client
33
- * reporting. Debug builds (`__DEV__`) send nothing unless you pass
34
- * `{ debug: true }`, so development sessions stay out of production data.
32
+ * Client plugins, such as `insights()` from
33
+ * `@hot-updater/react-native/plugins/insights`. Each plugin's `setup` runs
34
+ * once, and its hooks observe launches, update checks, downloads, and
35
+ * update failures. Plugin ids must be unique.
35
36
  */
36
- insights?:
37
- | boolean
38
- | {
39
- /** Also report from debug builds. Defaults to `false`. */
40
- debug?: boolean;
41
- };
37
+ plugins?: readonly HotUpdaterClientPlugin[];
42
38
  /** Base URL of a server exposing the Hot Updater v1 client HTTP protocol. */
43
39
  baseURL: HotUpdaterBaseURL;
44
40
  requestHeaders?: Record<string, string>;
@@ -60,8 +56,6 @@ export type HotUpdaterInitOptions = CommonHotUpdaterOptions;
60
56
  export type HotUpdaterOptions = AutoUpdateOptions;
61
57
 
62
58
  type InternalCommonOptions = {
63
- /** Whether this runtime reports, with the debug-build default applied. */
64
- insights?: boolean;
65
59
  client: HotUpdaterHttpClient;
66
60
  requestHeaders?: Record<string, string>;
67
61
  requestTimeout?: number;