@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41

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 (317) hide show
  1. package/android/build.gradle +13 -2
  2. package/android/proguard-rules.pro +8 -2
  3. package/android/src/bridge/BridgeReactInstance.kt +38 -0
  4. package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
  5. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
  6. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  7. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
  8. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
  9. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  11. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
  12. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
  13. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  14. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  15. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  16. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  17. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  18. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  19. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  20. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  21. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  22. package/android/src/newarch/HotUpdater.kt +7 -9
  23. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  24. package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
  25. package/android/src/newarch/ReactIntegrationManager.kt +6 -32
  26. package/android/src/oldarch/HotUpdater.kt +7 -9
  27. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  28. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  29. package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
  30. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  31. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  32. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
  33. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
  34. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  35. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  36. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  37. package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
  38. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
  39. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  40. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  41. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  42. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  43. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  44. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  45. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  46. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  47. package/lib/commonjs/appReady.js +96 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +121 -69
  50. package/lib/commonjs/checkForUpdate.js.map +1 -1
  51. package/lib/commonjs/clientPlugin.js +13 -0
  52. package/lib/commonjs/clientPlugin.js.map +1 -0
  53. package/lib/commonjs/error.js +5 -14
  54. package/lib/commonjs/error.js.map +1 -1
  55. package/lib/commonjs/fetchJSON.js +16 -27
  56. package/lib/commonjs/fetchJSON.js.map +1 -1
  57. package/lib/commonjs/fetchUpdateResponse.js +60 -0
  58. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  59. package/lib/commonjs/httpClient.js +64 -95
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +355 -324
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/init.types.js +6 -0
  64. package/lib/commonjs/init.types.js.map +1 -0
  65. package/lib/commonjs/native.js +77 -63
  66. package/lib/commonjs/native.js.map +1 -1
  67. package/lib/commonjs/pluginHost.js +41 -0
  68. package/lib/commonjs/pluginHost.js.map +1 -0
  69. package/lib/commonjs/releaseCatalogCache.js +36 -87
  70. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  71. package/lib/commonjs/sdkVersion.js +1 -1
  72. package/lib/commonjs/sdkVersion.js.map +1 -1
  73. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  74. package/lib/commonjs/store.js +3 -10
  75. package/lib/commonjs/store.js.map +1 -1
  76. package/lib/commonjs/updateError.js +145 -0
  77. package/lib/commonjs/updateError.js.map +1 -0
  78. package/lib/commonjs/wrap.js +46 -38
  79. package/lib/commonjs/wrap.js.map +1 -1
  80. package/lib/module/appReady.js +90 -0
  81. package/lib/module/appReady.js.map +1 -0
  82. package/lib/module/checkForUpdate.js +114 -64
  83. package/lib/module/checkForUpdate.js.map +1 -1
  84. package/lib/module/clientPlugin.js +4 -0
  85. package/lib/module/clientPlugin.js.map +1 -0
  86. package/lib/module/error.js +5 -14
  87. package/lib/module/error.js.map +1 -1
  88. package/lib/module/fetchJSON.js +14 -26
  89. package/lib/module/fetchJSON.js.map +1 -1
  90. package/lib/module/fetchUpdateResponse.js +55 -0
  91. package/lib/module/fetchUpdateResponse.js.map +1 -0
  92. package/lib/module/httpClient.js +63 -94
  93. package/lib/module/httpClient.js.map +1 -1
  94. package/lib/module/index.js +335 -327
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/init.types.js +4 -0
  97. package/lib/module/init.types.js.map +1 -0
  98. package/lib/module/native.js +70 -56
  99. package/lib/module/native.js.map +1 -1
  100. package/lib/module/pluginHost.js +36 -0
  101. package/lib/module/pluginHost.js.map +1 -0
  102. package/lib/module/releaseCatalogCache.js +32 -82
  103. package/lib/module/releaseCatalogCache.js.map +1 -1
  104. package/lib/module/sdkVersion.js +1 -1
  105. package/lib/module/sdkVersion.js.map +1 -1
  106. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  107. package/lib/module/store.js +3 -10
  108. package/lib/module/store.js.map +1 -1
  109. package/lib/module/updateError.js +139 -0
  110. package/lib/module/updateError.js.map +1 -0
  111. package/lib/module/wrap.js +47 -38
  112. package/lib/module/wrap.js.map +1 -1
  113. package/lib/typescript/commonjs/appReady.d.ts +30 -0
  114. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
  116. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  118. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/error.d.ts +5 -14
  120. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  122. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  124. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/httpClient.d.ts +5 -34
  126. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/index.d.ts +124 -116
  128. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  130. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/native.d.ts +32 -25
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
  134. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  136. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/types.d.ts +1 -1
  144. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  146. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  148. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
  150. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  151. package/lib/typescript/module/appReady.d.ts +30 -0
  152. package/lib/typescript/module/appReady.d.ts.map +1 -0
  153. package/lib/typescript/module/checkForUpdate.d.ts +20 -6
  154. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  155. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  156. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  157. package/lib/typescript/module/error.d.ts +5 -14
  158. package/lib/typescript/module/error.d.ts.map +1 -1
  159. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  160. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  161. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  162. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  163. package/lib/typescript/module/httpClient.d.ts +5 -34
  164. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  165. package/lib/typescript/module/index.d.ts +124 -116
  166. package/lib/typescript/module/index.d.ts.map +1 -1
  167. package/lib/typescript/module/init.types.d.ts +21 -0
  168. package/lib/typescript/module/init.types.d.ts.map +1 -0
  169. package/lib/typescript/module/native.d.ts +32 -25
  170. package/lib/typescript/module/native.d.ts.map +1 -1
  171. package/lib/typescript/module/pluginHost.d.ts +8 -0
  172. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  173. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  174. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  175. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  176. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  177. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  179. package/lib/typescript/module/store.d.ts +1 -3
  180. package/lib/typescript/module/store.d.ts.map +1 -1
  181. package/lib/typescript/module/types.d.ts +1 -1
  182. package/lib/typescript/module/types.d.ts.map +1 -1
  183. package/lib/typescript/module/updateError.d.ts +26 -0
  184. package/lib/typescript/module/updateError.d.ts.map +1 -0
  185. package/lib/typescript/module/wrap.d.ts +2 -3
  186. package/lib/typescript/module/wrap.d.ts.map +1 -1
  187. package/lib/typescript/module/wrap.types.d.ts +13 -41
  188. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  189. package/package.json +8 -4
  190. package/src/appReady.ts +147 -0
  191. package/src/checkForUpdate.ts +156 -89
  192. package/src/clientPlugin.ts +21 -0
  193. package/src/error.ts +5 -15
  194. package/src/fetchJSON.ts +14 -30
  195. package/src/fetchUpdateResponse.ts +60 -0
  196. package/src/httpClient.ts +89 -160
  197. package/src/index.ts +413 -425
  198. package/src/init.types.ts +24 -0
  199. package/src/native.ts +101 -105
  200. package/src/pluginHost.ts +52 -0
  201. package/src/releaseCatalogCache.ts +43 -172
  202. package/src/sdkVersion.ts +1 -1
  203. package/src/specs/NativeHotUpdater.ts +34 -48
  204. package/src/store.ts +2 -23
  205. package/src/types.ts +1 -1
  206. package/src/updateError.ts +167 -0
  207. package/src/wrap.tsx +60 -43
  208. package/src/wrap.types.ts +18 -46
  209. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  210. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  211. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  212. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  213. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  214. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  215. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  216. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  217. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  218. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  219. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  220. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  221. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  222. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  223. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  224. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  225. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  226. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  227. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  228. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  229. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  230. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  231. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  232. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  233. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  234. package/lib/commonjs/httpClient.spec.js +0 -102
  235. package/lib/commonjs/httpClient.spec.js.map +0 -1
  236. package/lib/commonjs/index.spec.js +0 -302
  237. package/lib/commonjs/index.spec.js.map +0 -1
  238. package/lib/commonjs/iosInterop.spec.js +0 -18
  239. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  240. package/lib/commonjs/native.spec.js +0 -883
  241. package/lib/commonjs/native.spec.js.map +0 -1
  242. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  243. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  244. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  245. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  246. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  247. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  248. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  249. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  250. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  251. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  252. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  253. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  254. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  255. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  256. package/lib/commonjs/store.notification.spec.js +0 -76
  257. package/lib/commonjs/store.notification.spec.js.map +0 -1
  258. package/lib/commonjs/store.spec.js +0 -316
  259. package/lib/commonjs/store.spec.js.map +0 -1
  260. package/lib/commonjs/wrap.spec.js +0 -329
  261. package/lib/commonjs/wrap.spec.js.map +0 -1
  262. package/lib/module/catalogCacheNative.spec.js +0 -31
  263. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  264. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  265. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  266. package/lib/module/httpClient.spec.js +0 -102
  267. package/lib/module/httpClient.spec.js.map +0 -1
  268. package/lib/module/index.spec.js +0 -301
  269. package/lib/module/index.spec.js.map +0 -1
  270. package/lib/module/iosInterop.spec.js +0 -18
  271. package/lib/module/iosInterop.spec.js.map +0 -1
  272. package/lib/module/native.spec.js +0 -882
  273. package/lib/module/native.spec.js.map +0 -1
  274. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  275. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  276. package/lib/module/notifyAppReadyInsights.js +0 -128
  277. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  278. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  279. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  280. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  281. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  282. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  283. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  284. package/lib/module/releaseCatalogCache.spec.js +0 -195
  285. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  286. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  287. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  288. package/lib/module/store.notification.spec.js +0 -77
  289. package/lib/module/store.notification.spec.js.map +0 -1
  290. package/lib/module/store.spec.js +0 -315
  291. package/lib/module/store.spec.js.map +0 -1
  292. package/lib/module/wrap.spec.js +0 -330
  293. package/lib/module/wrap.spec.js.map +0 -1
  294. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  295. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  296. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  297. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  298. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  299. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  300. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  301. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  302. package/src/catalogCacheNative.spec.ts +0 -63
  303. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  304. package/src/httpClient.spec.ts +0 -132
  305. package/src/index.spec.ts +0 -337
  306. package/src/iosInterop.spec.ts +0 -30
  307. package/src/native.spec.ts +0 -945
  308. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  309. package/src/notifyAppReadyInsights.spec.ts +0 -243
  310. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  311. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  312. package/src/notifyAppReadyInsights.ts +0 -194
  313. package/src/releaseCatalogCache.spec.ts +0 -282
  314. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  315. package/src/store.notification.spec.ts +0 -92
  316. package/src/store.spec.ts +0 -372
  317. package/src/wrap.spec.ts +0 -406
@@ -1,194 +0,0 @@
1
- import { Platform } from "react-native";
2
-
3
- import type { HotUpdaterError } from "./error";
4
- import type { InsightsEventParams, HotUpdaterHttpClient } from "./httpClient";
5
- import {
6
- getAppVersion,
7
- getBundleId,
8
- getChannel,
9
- getCohort,
10
- getFingerprintHash,
11
- getInstallId,
12
- getPersistedUserIdentity,
13
- type NotifyAppReadyInsightsEvent,
14
- type NotifyAppReadyResult,
15
- readNotifyAppReady,
16
- } from "./native";
17
-
18
- export type NotifyAppReadyOptions = {
19
- insights?: boolean;
20
- client: HotUpdaterHttpClient;
21
- requestHeaders?: Record<string, string>;
22
- requestTimeout?: number;
23
- onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
24
- onError?: (error: HotUpdaterError | Error | unknown) => void;
25
- };
26
-
27
- type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
28
-
29
- let didAttemptAutomaticInsights = false;
30
-
31
- const waitForNextFrame = () =>
32
- new Promise<void>((resolve) => {
33
- const requestAnimationFrame = (
34
- globalThis as typeof globalThis & {
35
- requestAnimationFrame?: RequestAnimationFrame;
36
- }
37
- )?.requestAnimationFrame;
38
-
39
- if (requestAnimationFrame) {
40
- requestAnimationFrame(() => resolve());
41
- return;
42
- }
43
-
44
- void Promise.resolve().then(resolve);
45
- });
46
-
47
- const assertNever = (value: never): never => {
48
- throw new Error(`[HotUpdater] Unexpected notifyAppReady status: ${value}`);
49
- };
50
-
51
- const buildNotifyAppReadyInsightsParams = (
52
- nativeResult: NotifyAppReadyResult,
53
- insightsEvent: NotifyAppReadyInsightsEvent | null,
54
- options: Pick<NotifyAppReadyOptions, "requestHeaders" | "requestTimeout">,
55
- ): InsightsEventParams => {
56
- const appVersion = getAppVersion();
57
-
58
- if (!appVersion) {
59
- throw new Error(
60
- "[HotUpdater] Automatic insights requires a non-null native app version.",
61
- );
62
- }
63
-
64
- const { userId, username } = getPersistedUserIdentity();
65
- const installId = getInstallId();
66
-
67
- if (!installId) {
68
- throw new Error(
69
- "[HotUpdater] Automatic insights requires a non-null native install id.",
70
- );
71
- }
72
-
73
- const platform: "ios" | "android" =
74
- Platform.OS === "android" ? "android" : "ios";
75
-
76
- const commonParams = {
77
- appVersion,
78
- channel: getChannel(),
79
- cohort: getCohort(),
80
- fingerprintHash: getFingerprintHash(),
81
- fromReleaseId: insightsEvent?.fromReleaseId ?? null,
82
- installId,
83
- platform,
84
- requestHeaders: options.requestHeaders,
85
- requestTimeout: options.requestTimeout,
86
- toReleaseId: insightsEvent?.toReleaseId ?? null,
87
- ...(userId != null ? { userId } : {}),
88
- ...(username != null ? { username } : {}),
89
- };
90
-
91
- switch (nativeResult.status) {
92
- case "UNCHANGED": {
93
- const bundleId = getBundleId();
94
-
95
- if (!bundleId) {
96
- throw new Error(
97
- "[HotUpdater] Automatic insights requires a non-null current bundle id.",
98
- );
99
- }
100
-
101
- return {
102
- ...commonParams,
103
- fromBundleId: null,
104
- toBundleId: bundleId,
105
- type: "UNCHANGED",
106
- updateStrategy: null,
107
- };
108
- }
109
- case "UPDATE_APPLIED":
110
- case "RECOVERED":
111
- if (!insightsEvent) {
112
- throw new Error(
113
- "[HotUpdater] Native launch report is missing persisted metadata required for automatic insights.",
114
- );
115
- }
116
-
117
- switch (insightsEvent.type) {
118
- case "UPDATE_APPLIED":
119
- return {
120
- ...commonParams,
121
- fromBundleId: insightsEvent.fromBundleId,
122
- toBundleId: insightsEvent.toBundleId,
123
- type: "UPDATE_APPLIED",
124
- updateStrategy: insightsEvent.updateStrategy,
125
- };
126
- case "RECOVERED":
127
- return {
128
- ...commonParams,
129
- fromBundleId: insightsEvent.fromBundleId,
130
- toBundleId: insightsEvent.toBundleId,
131
- type: "RECOVERED",
132
- updateStrategy: insightsEvent.updateStrategy,
133
- };
134
- default:
135
- return assertNever(insightsEvent.type);
136
- }
137
- default:
138
- return assertNever(nativeResult);
139
- }
140
- };
141
-
142
- const maybeSendAutomaticInsights = async (
143
- options: NotifyAppReadyOptions,
144
- nativeResult: NotifyAppReadyResult,
145
- insightsEvent: NotifyAppReadyInsightsEvent | null,
146
- ): Promise<void> => {
147
- if (!options.insights || didAttemptAutomaticInsights) {
148
- return;
149
- }
150
-
151
- didAttemptAutomaticInsights = true;
152
-
153
- if (nativeResult.status !== "UNCHANGED" && !insightsEvent) {
154
- throw new Error(
155
- "[HotUpdater] Native launch report is missing persisted metadata required for automatic insights.",
156
- );
157
- }
158
-
159
- const session = await options.client.createSession();
160
- await session.sendInsightsEvent(
161
- buildNotifyAppReadyInsightsParams(nativeResult, insightsEvent, options),
162
- );
163
- };
164
-
165
- export const handleNotifyAppReady = async (
166
- options: NotifyAppReadyOptions,
167
- ): Promise<void> => {
168
- try {
169
- let nativeReadResult: ReturnType<typeof readNotifyAppReady>;
170
- do {
171
- await waitForNextFrame();
172
- nativeReadResult = readNotifyAppReady();
173
- } while (nativeReadResult.pending);
174
-
175
- const { insightsEvent, result: nativeResult } = nativeReadResult;
176
-
177
- try {
178
- await maybeSendAutomaticInsights(options, nativeResult, insightsEvent);
179
- } catch (error) {
180
- const warning = error instanceof Error ? error : new Error(String(error));
181
- console.warn(
182
- "[HotUpdater] Automatic notifyAppReady insights failed:",
183
- warning,
184
- );
185
- }
186
-
187
- options.onNotifyAppReady?.(nativeResult);
188
- } catch (error) {
189
- const normalizedError =
190
- error instanceof Error ? error : new Error(String(error));
191
- options.onError?.(error);
192
- console.warn("[HotUpdater] Failed to notify app ready:", normalizedError);
193
- }
194
- };
@@ -1,282 +0,0 @@
1
- import {
2
- createReleaseCatalogScopeKey,
3
- encodeChannelKey,
4
- MAX_COMPILED_CATALOG_BYTES,
5
- type ReleaseCatalog,
6
- } from "@hot-updater/core";
7
- import { beforeEach, describe, expect, it, vi } from "vitest";
8
-
9
- import {
10
- createReleaseCatalogCachePartition,
11
- fetchReleaseCatalogWithCache,
12
- MAX_RELEASE_CATALOG_CACHE_ENTRY_BYTES,
13
- MAX_RELEASE_CATALOG_WIRE_BYTES,
14
- } from "./releaseCatalogCache";
15
-
16
- const cache = vi.hoisted(() => new Map<string, string>());
17
- const nativeMocks = vi.hoisted(() => ({
18
- read: vi.fn(async (partition: string) => cache.get(partition) ?? null),
19
- remove: vi.fn(async (partition: string) => {
20
- cache.delete(partition);
21
- }),
22
- write: vi.fn(async (partition: string, payload: string) => {
23
- cache.set(partition, payload);
24
- return true;
25
- }),
26
- }));
27
-
28
- vi.mock("./catalogCacheNative", () => ({
29
- readNativeReleaseCatalogCache: nativeMocks.read,
30
- removeNativeReleaseCatalogCache: nativeMocks.remove,
31
- writeNativeReleaseCatalogCache: nativeMocks.write,
32
- }));
33
-
34
- const CATALOG_ID = "project-a";
35
- const CHANNEL_KEY = encodeChannelKey("production");
36
- const SCOPE_KEY = createReleaseCatalogScopeKey({
37
- channelKey: CHANNEL_KEY,
38
- platform: "ios",
39
- strategy: "APP_VERSION",
40
- });
41
- const URL = `https://updates.example.com/release-catalogs/app-version/ios/${CHANNEL_KEY}/1.0.0`;
42
- const EXPECTED_SCOPE = {
43
- channelKey: CHANNEL_KEY,
44
- platform: "ios",
45
- strategy: "APP_VERSION",
46
- } as const;
47
-
48
- const catalog: ReleaseCatalog = {
49
- catalogId: CATALOG_ID,
50
- catalogHash: `sha256:${"a".repeat(64)}`,
51
- fallbackPolicy: "BUILTIN_IF_ACTIVE_INELIGIBLE",
52
- generation: 1,
53
- releases: [
54
- {
55
- bundleId: "00000000-0000-7000-8000-000000000002",
56
- kind: "BUNDLE",
57
- message: null,
58
- releaseId: "00000000-0000-7000-8000-000000000001",
59
- rolloutCohortCount: 1000,
60
- shouldForceUpdate: false,
61
- targetCohorts: [],
62
- },
63
- ],
64
- schemaVersion: 1,
65
- scopeKey: SCOPE_KEY,
66
- };
67
-
68
- const input = (
69
- overrides: Partial<Parameters<typeof fetchReleaseCatalogWithCache>[0]> = {},
70
- ) => ({
71
- baseURL: "https://updates.example.com",
72
- expectedScope: EXPECTED_SCOPE,
73
- requestHeaders: { "X-API-Key": "api-key-a" },
74
- url: URL,
75
- ...overrides,
76
- });
77
-
78
- const response = (status: number, body?: string, etag?: string) =>
79
- new Response(body, {
80
- headers: etag === undefined ? undefined : { ETag: etag },
81
- status,
82
- statusText: status === 200 ? "OK" : "Request failed",
83
- });
84
-
85
- const requestHeadersAt = (
86
- fetchMock: ReturnType<typeof vi.fn>,
87
- index: number,
88
- ) => {
89
- const request = fetchMock.mock.calls[index]?.[1] as RequestInit | undefined;
90
- return new Headers(request?.headers);
91
- };
92
-
93
- describe("Release Catalog persistent cache", () => {
94
- beforeEach(() => {
95
- cache.clear();
96
- vi.clearAllMocks();
97
- });
98
-
99
- it("persists a validated 200 and reuses it across resolver instances on 304", async () => {
100
- const fetchMock = vi
101
- .fn()
102
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"v1"'))
103
- .mockResolvedValueOnce(response(304));
104
- vi.stubGlobal("fetch", fetchMock);
105
-
106
- await expect(fetchReleaseCatalogWithCache(input())).resolves.toEqual(
107
- catalog,
108
- );
109
- expect(nativeMocks.write).toHaveBeenCalledOnce();
110
-
111
- const parseSpy = vi.spyOn(JSON, "parse");
112
- await expect(fetchReleaseCatalogWithCache(input())).resolves.toEqual(
113
- catalog,
114
- );
115
- expect(requestHeadersAt(fetchMock, 1).get("if-none-match")).toBe('"v1"');
116
- expect(parseSpy).not.toHaveBeenCalled();
117
- expect(fetchMock).toHaveBeenCalledTimes(2);
118
- });
119
-
120
- it("repairs an invalid cache after an unsatisfiable 304 with one unconditional request", async () => {
121
- const partition = createReleaseCatalogCachePartition(input());
122
- cache.set(partition, "invalid-envelope");
123
- const fetchMock = vi
124
- .fn()
125
- .mockResolvedValueOnce(response(304))
126
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"v2"'));
127
- vi.stubGlobal("fetch", fetchMock);
128
-
129
- await expect(fetchReleaseCatalogWithCache(input())).resolves.toEqual(
130
- catalog,
131
- );
132
-
133
- expect(nativeMocks.remove).toHaveBeenCalledWith(partition);
134
- expect(fetchMock).toHaveBeenCalledTimes(2);
135
- expect(requestHeadersAt(fetchMock, 0).has("if-none-match")).toBe(false);
136
- expect(requestHeadersAt(fetchMock, 1).has("if-none-match")).toBe(false);
137
- expect(cache.get(partition)).toContain('"v2"');
138
- });
139
-
140
- it("isolates base URL, scope, and case-insensitive API-key partitions", async () => {
141
- const betaScope = createReleaseCatalogScopeKey({
142
- channelKey: encodeChannelKey("beta"),
143
- platform: "ios",
144
- strategy: "APP_VERSION",
145
- });
146
- const fetchMock = vi
147
- .fn()
148
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"v1"'))
149
- .mockResolvedValueOnce(response(304))
150
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"key-b"'))
151
- .mockResolvedValueOnce(
152
- response(
153
- 200,
154
- JSON.stringify({ ...catalog, scopeKey: betaScope }),
155
- '"beta"',
156
- ),
157
- );
158
- vi.stubGlobal("fetch", fetchMock);
159
-
160
- await fetchReleaseCatalogWithCache(input());
161
- await fetchReleaseCatalogWithCache(
162
- input({ requestHeaders: { "x-api-key": "api-key-a" } }),
163
- );
164
- await fetchReleaseCatalogWithCache(
165
- input({ requestHeaders: { "x-api-key": "api-key-b" } }),
166
- );
167
- await fetchReleaseCatalogWithCache(
168
- input({
169
- baseURL: "https://other.example.com",
170
- expectedScope: {
171
- channelKey: encodeChannelKey("beta"),
172
- platform: "ios",
173
- strategy: "APP_VERSION",
174
- },
175
- url: "https://other.example.com/release-catalogs/app-version/ios/YmV0YQ/1.0.0",
176
- }),
177
- );
178
-
179
- expect(requestHeadersAt(fetchMock, 1).get("if-none-match")).toBe('"v1"');
180
- expect(requestHeadersAt(fetchMock, 2).has("if-none-match")).toBe(false);
181
- expect(requestHeadersAt(fetchMock, 3).has("if-none-match")).toBe(false);
182
- expect(cache.size).toBe(3);
183
- });
184
-
185
- it.each([401, 403, 500])(
186
- "does not use or repair cached data after HTTP %s",
187
- async (status) => {
188
- const fetchMock = vi
189
- .fn()
190
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"v1"'))
191
- .mockResolvedValueOnce(response(status));
192
- vi.stubGlobal("fetch", fetchMock);
193
- await fetchReleaseCatalogWithCache(input());
194
-
195
- await expect(fetchReleaseCatalogWithCache(input())).rejects.toThrow(
196
- "Request failed",
197
- );
198
- expect(fetchMock).toHaveBeenCalledTimes(2);
199
- },
200
- );
201
-
202
- it("does not use cached data as a fallback on network failure", async () => {
203
- const fetchMock = vi
204
- .fn()
205
- .mockResolvedValueOnce(response(200, JSON.stringify(catalog), '"v1"'))
206
- .mockRejectedValueOnce(new Error("offline"));
207
- vi.stubGlobal("fetch", fetchMock);
208
- await fetchReleaseCatalogWithCache(input());
209
-
210
- await expect(fetchReleaseCatalogWithCache(input())).rejects.toThrow(
211
- "offline",
212
- );
213
- expect(fetchMock).toHaveBeenCalledTimes(2);
214
- });
215
-
216
- it("rejects and never persists a catalog with the wrong canonical scope", async () => {
217
- const fetchMock = vi
218
- .fn()
219
- .mockResolvedValueOnce(
220
- response(
221
- 200,
222
- JSON.stringify({ ...catalog, scopeKey: "wrong-scope" }),
223
- '"wrong"',
224
- ),
225
- );
226
- vi.stubGlobal("fetch", fetchMock);
227
-
228
- await expect(fetchReleaseCatalogWithCache(input())).rejects.toThrow(
229
- "invalid Release catalog",
230
- );
231
- expect(nativeMocks.write).not.toHaveBeenCalled();
232
- });
233
-
234
- it("rejects a catalog with no persisted identity", async () => {
235
- const fetchMock = vi
236
- .fn()
237
- .mockResolvedValueOnce(
238
- response(
239
- 200,
240
- JSON.stringify({ ...catalog, catalogId: "" }),
241
- '"missing-identity"',
242
- ),
243
- );
244
- vi.stubGlobal("fetch", fetchMock);
245
-
246
- await expect(fetchReleaseCatalogWithCache(input())).rejects.toThrow(
247
- "invalid Release catalog",
248
- );
249
- expect(nativeMocks.write).not.toHaveBeenCalled();
250
- });
251
-
252
- it("accepts a legal compiled-boundary catalog plus its wire envelope", async () => {
253
- const template = {
254
- ...catalog,
255
- releases: [{ ...catalog.releases[0]!, message: "" }],
256
- };
257
- const templateBody = JSON.stringify(template);
258
- const padding = "x".repeat(
259
- MAX_COMPILED_CATALOG_BYTES + 1 - templateBody.length,
260
- );
261
- const body = JSON.stringify({
262
- ...template,
263
- releases: [{ ...template.releases[0]!, message: padding }],
264
- });
265
- expect(body.length).toBe(MAX_COMPILED_CATALOG_BYTES + 1);
266
- expect(body.length).toBeLessThanOrEqual(MAX_RELEASE_CATALOG_WIRE_BYTES);
267
- const fetchMock = vi
268
- .fn()
269
- .mockResolvedValueOnce(response(200, body, '"boundary"'));
270
- vi.stubGlobal("fetch", fetchMock);
271
-
272
- await expect(fetchReleaseCatalogWithCache(input())).resolves.toEqual(
273
- JSON.parse(body),
274
- );
275
-
276
- const persisted = nativeMocks.write.mock.calls[0]?.[1];
277
- expect(persisted).toBeDefined();
278
- expect(new TextEncoder().encode(persisted).byteLength).toBeLessThanOrEqual(
279
- MAX_RELEASE_CATALOG_CACHE_ENTRY_BYTES,
280
- );
281
- });
282
- });
@@ -1,80 +0,0 @@
1
- import {
2
- createReleaseCatalogScopeKey,
3
- encodeChannelKey,
4
- type ReleaseCatalog,
5
- } from "@hot-updater/core";
6
- import { beforeEach, describe, expect, it, vi } from "vitest";
7
-
8
- const nativeModule = vi.hoisted(
9
- () => ({}) as Record<string, ReturnType<typeof vi.fn>>,
10
- );
11
-
12
- vi.mock("./specs/NativeHotUpdater", () => ({ default: nativeModule }));
13
-
14
- import { fetchReleaseCatalogWithCache } from "./releaseCatalogCache";
15
-
16
- const catalogId = "project-a";
17
- const scopeKey = createReleaseCatalogScopeKey({
18
- channelKey: encodeChannelKey("production"),
19
- platform: "ios",
20
- strategy: "APP_VERSION",
21
- });
22
- const input = {
23
- baseURL: "https://updates.example.com",
24
- expectedScope: {
25
- channelKey: encodeChannelKey("production"),
26
- platform: "ios",
27
- strategy: "APP_VERSION",
28
- } as const,
29
- requestHeaders: { "x-api-key": "key" },
30
- url: "https://updates.example.com/catalog",
31
- };
32
- const catalog: ReleaseCatalog = {
33
- catalogId,
34
- catalogHash: `sha256:${"a".repeat(64)}`,
35
- fallbackPolicy: "BUILTIN_IF_ACTIVE_INELIGIBLE",
36
- generation: 1,
37
- releases: [],
38
- schemaVersion: 1,
39
- scopeKey,
40
- };
41
-
42
- describe("Release Catalog native cache contract", () => {
43
- beforeEach(() => {
44
- for (const key of Object.keys(nativeModule)) delete nativeModule[key];
45
- });
46
-
47
- it("fails before fetching when the v1 native cache API is missing", async () => {
48
- const fetchMock = vi.fn();
49
- vi.stubGlobal("fetch", fetchMock);
50
-
51
- await expect(fetchReleaseCatalogWithCache(input)).rejects.toThrow(
52
- "Native module is missing 'getReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.",
53
- );
54
- expect(fetchMock).not.toHaveBeenCalled();
55
- });
56
-
57
- it("uses the network when native cache I/O fails", async () => {
58
- nativeModule.getReleaseCatalogCache = vi
59
- .fn()
60
- .mockRejectedValue(new Error("read"));
61
- nativeModule.setReleaseCatalogCache = vi
62
- .fn()
63
- .mockRejectedValue(new Error("write"));
64
- nativeModule.removeReleaseCatalogCache = vi
65
- .fn()
66
- .mockRejectedValue(new Error("remove"));
67
- const fetchMock = vi.fn().mockResolvedValue(
68
- new Response(JSON.stringify(catalog), {
69
- headers: { ETag: '"v1"' },
70
- status: 200,
71
- }),
72
- );
73
- vi.stubGlobal("fetch", fetchMock);
74
-
75
- await expect(fetchReleaseCatalogWithCache(input)).resolves.toEqual(catalog);
76
- expect(fetchMock).toHaveBeenCalledOnce();
77
- expect(nativeModule.getReleaseCatalogCache).toHaveBeenCalledOnce();
78
- expect(nativeModule.setReleaseCatalogCache).toHaveBeenCalledOnce();
79
- });
80
- });
@@ -1,92 +0,0 @@
1
- // @vitest-environment jsdom
2
-
3
- import { cleanup, render } from "@testing-library/react";
4
- import { act, createElement, useLayoutEffect, useState } from "react";
5
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
-
7
- const listeners = vi.hoisted(
8
- () => new Map<string, (event: Record<string, unknown>) => void>(),
9
- );
10
-
11
- vi.mock("./native", () => ({
12
- addListener: vi.fn(
13
- (eventName: string, listener: (event: Record<string, unknown>) => void) => {
14
- listeners.set(eventName, listener);
15
- return () => {
16
- listeners.delete(eventName);
17
- };
18
- },
19
- ),
20
- }));
21
-
22
- describe("hotUpdaterStore notification scheduling", () => {
23
- beforeEach(() => {
24
- listeners.clear();
25
- vi.resetModules();
26
- });
27
-
28
- afterEach(() => {
29
- cleanup();
30
- vi.unstubAllGlobals();
31
- });
32
-
33
- it("keeps progress notifications from exceeding React's maximum update depth", async () => {
34
- // Given
35
- const frameCallbacks: ((timestamp: number) => void)[] = [];
36
- vi.stubGlobal(
37
- "requestAnimationFrame",
38
- (callback: (timestamp: number) => void) => {
39
- frameCallbacks.push(callback);
40
- return frameCallbacks.length;
41
- },
42
- );
43
- const { hotUpdaterStore } = await import("./store");
44
- const emitProgress = listeners.get("onProgress");
45
- const Consumer = () => {
46
- const [progress, setProgress] = useState(0);
47
-
48
- useLayoutEffect(() => {
49
- const unsubscribe = hotUpdaterStore.subscribe(() => {
50
- setProgress(hotUpdaterStore.getSnapshot().progress);
51
- });
52
- return () => {
53
- unsubscribe();
54
- };
55
- }, []);
56
-
57
- useLayoutEffect(() => {
58
- if (progress >= 1) {
59
- return;
60
- }
61
-
62
- const nextProgress = Math.min(1, progress + 0.01);
63
- emitProgress?.({
64
- artifactType: "archive",
65
- downloadedBytes: nextProgress * 1_000_000,
66
- progress: nextProgress,
67
- totalBytes: 1_000_000,
68
- });
69
- }, [progress]);
70
-
71
- return null;
72
- };
73
-
74
- // When
75
- render(createElement(Consumer));
76
- for (
77
- let frame = 0;
78
- frame < 200 && hotUpdaterStore.getSnapshot().progress < 1;
79
- frame++
80
- ) {
81
- const frameCallback = frameCallbacks.shift();
82
- expect(frameCallback).toBeTypeOf("function");
83
- await new Promise<void>((resolve) => setImmediate(resolve));
84
- await act(async () => {
85
- frameCallback?.(0);
86
- });
87
- }
88
-
89
- // Then
90
- expect(hotUpdaterStore.getSnapshot().progress).toBe(1);
91
- });
92
- });