@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
@@ -12,8 +12,7 @@ const nativeModuleMock = vi.hoisted(() => {
12
12
  getBundleId: vi.fn<() => string | null>(() => "bundle-id"),
13
13
  getCohort: vi.fn<() => string>(() => "123"),
14
14
  getInstallId: vi.fn<() => string>(() => "install-id"),
15
- getUserId: vi.fn<() => string | null>(() => null),
16
- getUsername: vi.fn<() => string | null>(() => null),
15
+ getStorageItem: vi.fn<(key: string) => string | null>(() => null),
17
16
  getManifest,
18
17
  getCrashHistory,
19
18
  getConstants: vi.fn(() => ({
@@ -27,7 +26,7 @@ const nativeModuleMock = vi.hoisted(() => {
27
26
  reload: vi.fn(),
28
27
  resetChannel: vi.fn(),
29
28
  setCohort: vi.fn(),
30
- setUser: vi.fn(),
29
+ setStorageItem: vi.fn<(key: string, value: string | null) => void>(),
31
30
  setBundleURL: vi.fn(),
32
31
  switchChannel: vi.fn(),
33
32
  updateBundle: vi.fn(),
@@ -88,7 +87,9 @@ describe("notifyAppReady", () => {
88
87
  });
89
88
  nativeModuleMock.resetChannel.mockReset();
90
89
  nativeModuleMock.setCohort.mockReset();
91
- nativeModuleMock.setUser.mockReset();
90
+ nativeModuleMock.getStorageItem.mockReset();
91
+ nativeModuleMock.getStorageItem.mockReturnValue(null);
92
+ nativeModuleMock.setStorageItem.mockReset();
92
93
  nativeModuleMock.updateBundle.mockReset();
93
94
  });
94
95
 
@@ -247,9 +248,12 @@ describe("notifyAppReady", () => {
247
248
  toBundleId: "bundle-122",
248
249
  });
249
250
  expect(readNotifyAppReady()).toEqual({
250
- insightsEvent: {
251
+ previousProcessExit: null,
252
+ transition: {
251
253
  fromBundleId: "bundle-123",
254
+ fromReleaseId: null,
252
255
  toBundleId: "bundle-122",
256
+ toReleaseId: null,
253
257
  type: "RECOVERED",
254
258
  updateStrategy: "appVersion",
255
259
  },
@@ -274,13 +278,14 @@ describe("notifyAppReady", () => {
274
278
 
275
279
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
276
280
  expect(readNotifyAppReady()).toEqual({
277
- insightsEvent: null,
281
+ previousProcessExit: null,
282
+ transition: null,
278
283
  pending: false,
279
284
  result: { status: "UNCHANGED" },
280
285
  });
281
286
  });
282
287
 
283
- it("returns UNCHANGED when automatic insights metadata is incomplete", async () => {
288
+ it("returns UNCHANGED when an applied launch report has no bundle ids", async () => {
284
289
  nativeModuleMock.notifyAppReady.mockReturnValue({
285
290
  status: "UPDATE_APPLIED",
286
291
  });
@@ -289,7 +294,8 @@ describe("notifyAppReady", () => {
289
294
 
290
295
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
291
296
  expect(readNotifyAppReady()).toEqual({
292
- insightsEvent: null,
297
+ previousProcessExit: null,
298
+ transition: null,
293
299
  pending: false,
294
300
  result: { status: "UNCHANGED" },
295
301
  });
@@ -302,7 +308,8 @@ describe("notifyAppReady", () => {
302
308
 
303
309
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
304
310
  expect(readNotifyAppReady()).toEqual({
305
- insightsEvent: null,
311
+ previousProcessExit: null,
312
+ transition: null,
306
313
  pending: false,
307
314
  result: { status: "UNCHANGED" },
308
315
  });
@@ -315,7 +322,8 @@ describe("notifyAppReady", () => {
315
322
 
316
323
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
317
324
  expect(readNotifyAppReady()).toEqual({
318
- insightsEvent: null,
325
+ previousProcessExit: null,
326
+ transition: null,
319
327
  pending: true,
320
328
  result: { status: "UNCHANGED" },
321
329
  });
@@ -698,6 +706,49 @@ describe("notifyAppReady", () => {
698
706
  });
699
707
  });
700
708
 
709
+ it("resolves how native delivered a staged bundle", async () => {
710
+ nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
711
+ nativeModuleMock.updateBundle.mockResolvedValue({
712
+ delivery: "patch",
713
+ patchFallback: true,
714
+ });
715
+ const { stageBundle, updateBundle } = await import("./native");
716
+ const params = {
717
+ assets: {},
718
+ bundleId: "bundle-789",
719
+ manifestFileHash: "sig:manifest",
720
+ manifestUrl: "https://example.com/manifest.json",
721
+ status: "UPDATE" as const,
722
+ };
723
+
724
+ await expect(stageBundle(params)).resolves.toEqual({
725
+ delivery: "patch",
726
+ patchFallback: true,
727
+ });
728
+ // Native still reports bundle-123, so the same bundle downloads again.
729
+ await expect(updateBundle(params)).resolves.toBe(true);
730
+ nativeModuleMock.getBundleId.mockReturnValue("bundle-789");
731
+ // Now native reports it: this runtime already staged it.
732
+ await expect(stageBundle(params)).resolves.toBeNull();
733
+ expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(2);
734
+ });
735
+
736
+ it("passes on why the previous process exited from the launch report", async () => {
737
+ nativeModuleMock.notifyAppReady.mockReturnValue({
738
+ previousProcessExit: "LOW_MEMORY",
739
+ status: "UNCHANGED",
740
+ });
741
+
742
+ const { readNotifyAppReady } = await import("./native");
743
+
744
+ expect(readNotifyAppReady()).toEqual({
745
+ previousProcessExit: "LOW_MEMORY",
746
+ transition: null,
747
+ pending: false,
748
+ result: { status: "UNCHANGED" },
749
+ });
750
+ });
751
+
701
752
  it("invalidates cached bundle getters after resetChannel succeeds", async () => {
702
753
  nativeModuleMock.getConstants.mockReturnValue({
703
754
  APP_VERSION: null,
@@ -857,74 +908,49 @@ describe("notifyAppReady", () => {
857
908
  }
858
909
  });
859
910
 
860
- it("passes nullable user identity through to native", async () => {
861
- const { setUser } = await import("./native");
911
+ it("reads and writes the native key-value store", async () => {
912
+ nativeModuleMock.getStorageItem.mockImplementation((key) =>
913
+ key === "plugins/example/state" ? "stored" : null,
914
+ );
915
+ const { getStorageItem, setStorageItem } = await import("./native");
862
916
 
863
- setUser({ userId: "user-123", username: "alice" });
864
- setUser({ userId: 42, username: "bob" });
865
- setUser({});
866
- setUser(null);
917
+ expect(getStorageItem("plugins/example/state")).toBe("stored");
918
+ expect(getStorageItem("plugins/example/missing")).toBeNull();
919
+ setStorageItem("plugins/example/state", "next");
920
+ setStorageItem("plugins/example/state", null);
867
921
 
868
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(
922
+ expect(nativeModuleMock.setStorageItem).toHaveBeenNthCalledWith(
869
923
  1,
870
- "user-123",
871
- "alice",
924
+ "plugins/example/state",
925
+ "next",
926
+ );
927
+ expect(nativeModuleMock.setStorageItem).toHaveBeenNthCalledWith(
928
+ 2,
929
+ "plugins/example/state",
930
+ null,
872
931
  );
873
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(2, "42", "bob");
874
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(3, null, null);
875
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(4, null, null);
876
- });
877
-
878
- it("reads persisted user identity from native when available", async () => {
879
- nativeModuleMock.getUserId.mockReturnValue("user-123");
880
- nativeModuleMock.getUsername.mockReturnValue("alice");
881
-
882
- const { getPersistedUserIdentity } = await import("./native");
883
-
884
- expect(getPersistedUserIdentity()).toEqual({
885
- userId: "user-123",
886
- username: "alice",
887
- });
888
- });
889
-
890
- it("throws when native SDK does not expose persisted user identity getters", async () => {
891
- const nativeModule = nativeModuleMock as typeof nativeModuleMock & {
892
- getUserId?: typeof nativeModuleMock.getUserId;
893
- getUsername?: typeof nativeModuleMock.getUsername;
894
- };
895
- const originalGetUserId = nativeModule.getUserId;
896
- nativeModule.getUserId = null as unknown as Mock<() => string | null>;
897
-
898
- try {
899
- const { getPersistedUserIdentity } = await import("./native");
900
-
901
- expect(() => getPersistedUserIdentity()).toThrow(
902
- "Native module is missing 'getUserId()' or 'getUsername()'",
903
- );
904
- } finally {
905
- nativeModule.getUserId = originalGetUserId;
906
- }
907
932
  });
908
933
 
909
- it("throws when native SDK does not expose setUser", async () => {
910
- const nativeModule = nativeModuleMock as typeof nativeModuleMock & {
911
- setUser?: typeof nativeModuleMock.setUser;
912
- };
913
- const originalSetUser = nativeModule.setUser;
914
- nativeModule.setUser = null as unknown as Mock<
915
- (userId: string | null, username: string | null) => void
916
- >;
917
-
918
- try {
919
- const { setUser } = await import("./native");
920
-
921
- expect(() => setUser({ userId: "user-123" })).toThrow(
922
- "Native module is missing 'setUser()'",
923
- );
924
- } finally {
925
- nativeModule.setUser = originalSetUser;
926
- }
927
- });
934
+ it.each(["getStorageItem", "setStorageItem"] as const)(
935
+ "throws when native SDK does not expose %s",
936
+ async (name) => {
937
+ const nativeModule = nativeModuleMock as Record<string, unknown>;
938
+ const original = nativeModule[name];
939
+ nativeModule[name] = undefined;
940
+
941
+ try {
942
+ const { getStorageItem, setStorageItem } = await import("./native");
943
+
944
+ expect(() =>
945
+ name === "getStorageItem"
946
+ ? getStorageItem("key")
947
+ : setStorageItem("key", "value"),
948
+ ).toThrow(`Native module is missing '${name}()'`);
949
+ } finally {
950
+ nativeModule[name] = original;
951
+ }
952
+ },
953
+ );
928
954
 
929
955
  it("returns the cohort reported by native", async () => {
930
956
  nativeModuleMock.getCohort.mockReturnValue("qa-group");
package/src/native.ts CHANGED
@@ -74,7 +74,10 @@ class HotUpdaterSessionState {
74
74
  private readonly defaultChannel: string;
75
75
  private currentChannel: string;
76
76
  private cachedCohort: string | undefined;
77
- private readonly inflightUpdates = new Map<string, Promise<boolean>>();
77
+ private readonly inflightUpdates = new Map<
78
+ string,
79
+ Promise<BundleDelivery | null>
80
+ >();
78
81
  private lastInstalledBundleId: string | null = null;
79
82
  private readonly activeBundleSnapshotCache = new Map<
80
83
  ActiveBundleSnapshotCacheKey,
@@ -103,11 +106,16 @@ class HotUpdaterSessionState {
103
106
  return this.lastInstalledBundleId === bundleId;
104
107
  }
105
108
 
106
- getInflightUpdate(bundleId: string): Promise<boolean> | undefined {
109
+ getInflightUpdate(
110
+ bundleId: string,
111
+ ): Promise<BundleDelivery | null> | undefined {
107
112
  return this.inflightUpdates.get(bundleId);
108
113
  }
109
114
 
110
- trackInflightUpdate(bundleId: string, promise: Promise<boolean>) {
115
+ trackInflightUpdate(
116
+ bundleId: string,
117
+ promise: Promise<BundleDelivery | null>,
118
+ ) {
111
119
  this.inflightUpdates.set(bundleId, promise);
112
120
  }
113
121
 
@@ -417,14 +425,40 @@ export const commitReleaseSelection = async (input: {
417
425
  return ok;
418
426
  };
419
427
 
428
+ /** How native delivered a staged bundle. */
429
+ export type BundleDelivery = {
430
+ /**
431
+ * "patch" when a bsdiff patch produced a file, "manifest" when only the
432
+ * changed files were downloaded, "archive" for the full archive.
433
+ */
434
+ readonly delivery: "patch" | "manifest" | "archive";
435
+ /** A patch was tried, but the file or the archive was downloaded instead. */
436
+ readonly patchFallback: boolean;
437
+ };
438
+
439
+ const readBundleDelivery = (value: unknown): BundleDelivery | null => {
440
+ if (typeof value !== "object" || value === null) return null;
441
+ const { delivery, patchFallback } = value as Record<string, unknown>;
442
+ if (
443
+ delivery !== "patch" &&
444
+ delivery !== "manifest" &&
445
+ delivery !== "archive"
446
+ ) {
447
+ return null;
448
+ }
449
+ return { delivery, patchFallback: patchFallback === true };
450
+ };
451
+
420
452
  /**
421
- * Downloads files and applies them to the app.
453
+ * Stages a bundle for the next launch. Resolves with how native delivered
454
+ * it, or with null when this runtime had already staged it and nothing was
455
+ * downloaded; rejects when the download or install fails.
422
456
  *
423
- * @param {UpdateParams} params - Parameters object required for bundle update
424
- * @returns {Promise<boolean>} Resolves with true if download was successful
425
457
  * @throws {Error} Rejects with error.code from HotUpdaterErrorCode enum and error.message
426
458
  */
427
- export async function updateBundle(params: UpdateParams): Promise<boolean> {
459
+ export async function stageBundle(
460
+ params: UpdateParams,
461
+ ): Promise<BundleDelivery | null> {
428
462
  const updateBundleId = params.bundleId;
429
463
  const status = params.status;
430
464
  const currentBundleId = status === "UPDATE" ? getFreshBundleId() : undefined;
@@ -437,7 +471,7 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
437
471
  sessionState.hasInstalledBundle(updateBundleId) &&
438
472
  currentBundleId === updateBundleId
439
473
  ) {
440
- return true;
474
+ return null;
441
475
  }
442
476
 
443
477
  const shouldSkipCurrentBundleIdCheck =
@@ -466,7 +500,7 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
466
500
  const promise = (async () => {
467
501
  try {
468
502
  const selection = params.selection;
469
- const ok = await HotUpdaterNative.updateBundle({
503
+ const staged = await HotUpdaterNative.updateBundle({
470
504
  bundleId: updateBundleId,
471
505
  channel: targetChannel,
472
506
  assets: targetAssets as Record<string, ArtifactAsset>,
@@ -475,10 +509,8 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
475
509
  ...(params.archiveUrl ? { archiveUrl: params.archiveUrl } : {}),
476
510
  ...(selection === undefined ? {} : { selection }),
477
511
  });
478
- if (ok) {
479
- sessionState.markBundleInstalled(updateBundleId, targetChannel);
480
- }
481
- return ok;
512
+ sessionState.markBundleInstalled(updateBundleId, targetChannel);
513
+ return readBundleDelivery(staged);
482
514
  } finally {
483
515
  sessionState.clearInflightUpdate(updateBundleId);
484
516
  }
@@ -488,6 +520,18 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
488
520
  return promise;
489
521
  }
490
522
 
523
+ /**
524
+ * Downloads files and applies them to the app.
525
+ *
526
+ * @param {UpdateParams} params - Parameters object required for bundle update
527
+ * @returns {Promise<boolean>} Resolves with true if download was successful
528
+ * @throws {Error} Rejects with error.code from HotUpdaterErrorCode enum and error.message
529
+ */
530
+ export async function updateBundle(params: UpdateParams): Promise<boolean> {
531
+ await stageBundle(params);
532
+ return true;
533
+ }
534
+
491
535
  /**
492
536
  * Fetches the current app version.
493
537
  */
@@ -693,64 +737,31 @@ export const getInstallId = (): string => {
693
737
  return nativeModule.getInstallId();
694
738
  };
695
739
 
696
- export type PersistedUserIdentity = {
697
- userId?: string;
698
- username?: string;
699
- };
700
-
701
- export const getPersistedUserIdentity = (): PersistedUserIdentity => {
702
- const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
703
- getUserId?: () => string | null;
704
- getUsername?: () => string | null;
705
- };
706
-
707
- if (
708
- typeof nativeModule.getUserId !== "function" ||
709
- typeof nativeModule.getUsername !== "function"
710
- ) {
740
+ const requireStorageNativeMethod = <T extends (...args: any[]) => any>(
741
+ name: "getStorageItem" | "setStorageItem",
742
+ ): T => {
743
+ const method = (HotUpdaterNative as unknown as Record<string, unknown>)[name];
744
+ if (typeof method !== "function") {
711
745
  throw new Error(
712
- "[HotUpdater] Native module is missing 'getUserId()' or 'getUsername()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.",
746
+ `[HotUpdater] Native module is missing '${name}()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.`,
713
747
  );
714
748
  }
715
-
716
- const userId = nativeModule.getUserId();
717
- const username = nativeModule.getUsername();
718
-
719
- return {
720
- ...(userId != null ? { userId } : {}),
721
- ...(username != null ? { username } : {}),
722
- };
749
+ return method.bind(HotUpdaterNative) as T;
723
750
  };
724
751
 
725
- export type SetUserParams = {
726
- userId?: string | number | null;
727
- username?: string | null;
752
+ /** Reads a value from the SDK's native key-value store. */
753
+ export const getStorageItem = (key: string): string | null => {
754
+ const value =
755
+ requireStorageNativeMethod<(key: string) => unknown>("getStorageItem")(key);
756
+ return typeof value === "string" ? value : null;
728
757
  };
729
758
 
730
- export function setUser(params: SetUserParams): void;
731
- export function setUser(params: null): void;
732
- export function setUser(params: SetUserParams | null): void {
733
- const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
734
- setUser?: (userId: string | null, username: string | null) => void;
735
- };
736
-
737
- if (typeof nativeModule.setUser !== "function") {
738
- throw new Error(
739
- "[HotUpdater] Native module is missing 'setUser()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.",
740
- );
741
- }
742
-
743
- if (params === null) {
744
- nativeModule.setUser(null, null);
745
- return;
746
- }
747
-
748
- const normalizedUserId =
749
- params.userId === null || params.userId === undefined
750
- ? null
751
- : String(params.userId);
752
- nativeModule.setUser(normalizedUserId, params.username ?? null);
753
- }
759
+ /** Writes a value to the SDK's native key-value store; null removes it. */
760
+ export const setStorageItem = (key: string, value: string | null): void => {
761
+ requireStorageNativeMethod<(key: string, value: string | null) => void>(
762
+ "setStorageItem",
763
+ )(key, value);
764
+ };
754
765
 
755
766
  /**
756
767
  * Result returned by notifyAppReady()
@@ -772,18 +783,20 @@ export type NotifyAppReadyResult =
772
783
  toReleaseId?: string;
773
784
  };
774
785
 
775
- export type NotifyAppReadyInsightsEvent = {
786
+ /** An update applied or recovered at this launch, as native persisted it. */
787
+ export type LaunchTransition = {
776
788
  type: "UPDATE_APPLIED" | "RECOVERED";
777
789
  fromBundleId: string;
778
790
  toBundleId: string;
779
- fromReleaseId?: string | null;
780
- toReleaseId?: string | null;
791
+ fromReleaseId: string | null;
792
+ toReleaseId: string | null;
781
793
  updateStrategy: PersistedUpdateStrategy;
782
794
  };
783
795
 
784
796
  type RawNotifyAppReadyResult = {
785
797
  status?: string;
786
798
  crashedBundleId?: string;
799
+ previousProcessExit?: string;
787
800
  fromReleaseId?: string;
788
801
  fromBundleId?: string;
789
802
  toReleaseId?: string;
@@ -880,9 +893,9 @@ const getNotifyAppReadyTransition = (
880
893
  return null;
881
894
  };
882
895
 
883
- const getNotifyAppReadyInsightsEvent = (
896
+ const getLaunchTransition = (
884
897
  result: RawNotifyAppReadyResult,
885
- ): NotifyAppReadyInsightsEvent | null => {
898
+ ): LaunchTransition | null => {
886
899
  const transition = getNotifyAppReadyTransition(result);
887
900
 
888
901
  if (!transition || !isPersistedUpdateStrategy(result.updateStrategy)) {
@@ -893,12 +906,8 @@ const getNotifyAppReadyInsightsEvent = (
893
906
  type: transition.status,
894
907
  fromBundleId: transition.fromBundleId,
895
908
  toBundleId: transition.toBundleId,
896
- ...(transition.fromReleaseId === null
897
- ? {}
898
- : { fromReleaseId: transition.fromReleaseId }),
899
- ...(transition.toReleaseId === null
900
- ? {}
901
- : { toReleaseId: transition.toReleaseId }),
909
+ fromReleaseId: transition.fromReleaseId,
910
+ toReleaseId: transition.toReleaseId,
902
911
  updateStrategy: result.updateStrategy,
903
912
  };
904
913
  };
@@ -927,14 +936,21 @@ const normalizeNotifyAppReadyResult = (
927
936
 
928
937
  export const readNotifyAppReady = (): {
929
938
  result: NotifyAppReadyResult;
930
- insightsEvent: NotifyAppReadyInsightsEvent | null;
939
+ transition: LaunchTransition | null;
940
+ /** Android 11+: why the previous main process exited, when native knows. */
941
+ previousProcessExit: string | null;
931
942
  pending: boolean;
932
943
  } => {
933
944
  const rawResult = readRawNotifyAppReadyResult();
945
+ const { previousProcessExit } = rawResult;
934
946
 
935
947
  return {
936
948
  result: normalizeNotifyAppReadyResult(rawResult),
937
- insightsEvent: getNotifyAppReadyInsightsEvent(rawResult),
949
+ transition: getLaunchTransition(rawResult),
950
+ previousProcessExit:
951
+ typeof previousProcessExit === "string" && previousProcessExit !== ""
952
+ ? previousProcessExit
953
+ : null,
938
954
  pending: rawResult.status === "PENDING",
939
955
  };
940
956
  };