@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
@@ -26,6 +26,16 @@ class HotUpdaterModule internal constructor(
26
26
 
27
27
  override fun getName(): String = NAME
28
28
 
29
+ private fun readSafeInteger(
30
+ map: ReadableMap,
31
+ key: String,
32
+ ): Long? {
33
+ if (!map.hasKey(key) || map.isNull(key)) return null
34
+ val value = runCatching { map.getDouble(key) }.getOrNull() ?: return null
35
+ if (!value.isFinite() || value % 1.0 != 0.0) return null
36
+ return value.toLong().takeIf { it in 0..9_007_199_254_740_991L }
37
+ }
38
+
29
39
  override fun onCatalystInstanceDestroy() {
30
40
  super.onCatalystInstanceDestroy()
31
41
  // Cancel all ongoing coroutines when module is destroyed
@@ -37,12 +47,12 @@ class HotUpdaterModule internal constructor(
37
47
  */
38
48
  private fun getInstance(): HotUpdaterImpl = HotUpdater.getInstance(mReactApplicationContext)
39
49
 
40
- private fun parseChangedAssets(params: ReadableMap): Map<String, ChangedAssetDescriptor>? {
41
- if (!params.hasKey("changedAssets") || params.isNull("changedAssets")) {
50
+ private fun parseAssets(params: ReadableMap): Map<String, ChangedAssetDescriptor>? {
51
+ if (!params.hasKey("assets") || params.isNull("assets")) {
42
52
  return null
43
53
  }
44
54
 
45
- val changedAssetsMap = params.getMap("changedAssets") ?: return null
55
+ val changedAssetsMap = params.getMap("assets") ?: return null
46
56
  val parsedAssets = linkedMapOf<String, ChangedAssetDescriptor>()
47
57
  val iterator = changedAssetsMap.keySetIterator()
48
58
 
@@ -58,6 +68,7 @@ class HotUpdaterModule internal constructor(
58
68
  val baseFileHash = patchMap.getString("baseFileHash")
59
69
  val patchFileHash = patchMap.getString("patchFileHash")
60
70
  val patchUrl = patchMap.getString("patchUrl")
71
+ val byteSize = readSafeInteger(patchMap, "byteSize")
61
72
 
62
73
  if (
63
74
  algorithm != null &&
@@ -72,6 +83,7 @@ class HotUpdaterModule internal constructor(
72
83
  baseFileHash = baseFileHash,
73
84
  patchFileHash = patchFileHash,
74
85
  patchUrl = patchUrl,
86
+ byteSize = byteSize,
75
87
  )
76
88
  } else {
77
89
  null
@@ -166,90 +178,105 @@ class HotUpdaterModule internal constructor(
166
178
  try {
167
179
  val bundleId = params.getString("bundleId")
168
180
  if (bundleId == null || bundleId.isEmpty()) {
169
- promise.reject("MISSING_BUNDLE_ID", "Missing or empty 'bundleId'")
181
+ promise.rejectUpdateBundle(HotUpdaterException.missingBundleId())
170
182
  return@launch
171
183
  }
172
184
 
173
- val fileUrl = params.getString("fileUrl")
174
-
175
- // Validate fileUrl format if provided
176
- if (fileUrl != null && fileUrl.isNotEmpty()) {
185
+ val manifestUrl = params.getString("manifestUrl")
186
+ val manifestFileHash = params.getString("manifestFileHash")
187
+ val assets = parseAssets(params)
188
+ if (manifestUrl.isNullOrEmpty() || manifestFileHash.isNullOrEmpty() || assets == null) {
189
+ promise.rejectUpdateBundle(HotUpdaterException.invalidManifestParams())
190
+ return@launch
191
+ }
192
+ try {
193
+ java.net.URL(manifestUrl)
194
+ } catch (e: java.net.MalformedURLException) {
195
+ promise.rejectUpdateBundle(
196
+ HotUpdaterException.invalidFileUrl("manifestUrl", manifestUrl, UpdateFailureResource.MANIFEST),
197
+ )
198
+ return@launch
199
+ }
200
+ val channel = params.getString("channel")
201
+ val archiveUrl =
202
+ if (params.hasKey("archiveUrl") && !params.isNull("archiveUrl")) {
203
+ params.getString("archiveUrl")
204
+ } else {
205
+ null
206
+ }
207
+ if (archiveUrl != null) {
177
208
  try {
178
- java.net.URL(fileUrl)
209
+ java.net.URL(archiveUrl)
179
210
  } catch (e: java.net.MalformedURLException) {
180
- promise.reject("INVALID_FILE_URL", "Invalid 'fileUrl' provided: $fileUrl")
211
+ promise.rejectUpdateBundle(
212
+ HotUpdaterException.invalidFileUrl("archiveUrl", archiveUrl, UpdateFailureResource.ARCHIVE),
213
+ )
181
214
  return@launch
182
215
  }
183
216
  }
184
-
185
- val fileHash = params.getString("fileHash")
186
- val manifestUrl = params.getString("manifestUrl")
187
- val manifestFileHash = params.getString("manifestFileHash")
188
- val changedAssets = parseChangedAssets(params)
189
- val channel = params.getString("channel")
190
217
  val selection = parseSelection(params)
191
218
 
192
219
  val impl = getInstance()
193
220
 
194
- impl.updateBundle(
195
- bundleId,
196
- fileUrl,
197
- fileHash,
198
- manifestUrl,
199
- manifestFileHash,
200
- changedAssets,
201
- channel,
202
- selection,
203
- ) { progress ->
204
- // Post to Main thread for React Native event emission
205
- Handler(Looper.getMainLooper()).post {
206
- try {
207
- val progressParams =
208
- Arguments.createMap().apply {
209
- putDouble("progress", progress.progress)
210
- putString("artifactType", progress.artifactType)
211
- progress.downloadedBytes?.let { putDouble("downloadedBytes", it.toDouble()) }
212
- progress.totalBytes?.let { putDouble("totalBytes", it.toDouble()) }
213
- progress.details?.let { details ->
214
- val files = Arguments.createArray()
215
- details.files.forEach { file ->
216
- files.pushMap(
221
+ val result =
222
+ impl.updateBundle(
223
+ bundleId,
224
+ manifestUrl,
225
+ manifestFileHash,
226
+ assets,
227
+ channel,
228
+ selection,
229
+ archiveUrl,
230
+ ) { progress ->
231
+ // Post to Main thread for React Native event emission
232
+ Handler(Looper.getMainLooper()).post {
233
+ try {
234
+ val progressParams =
235
+ Arguments.createMap().apply {
236
+ putDouble("progress", progress.progress)
237
+ putString("artifactType", progress.artifactType)
238
+ progress.details?.let { details ->
239
+ val files = Arguments.createArray()
240
+ details.files.forEach { file ->
241
+ files.pushMap(
242
+ Arguments.createMap().apply {
243
+ putString("path", file.path)
244
+ putString("downloadPath", file.downloadPath)
245
+ putString("status", file.status)
246
+ putDouble("progress", file.progress)
247
+ putInt("order", file.order)
248
+ file.downloadedBytes?.let { putDouble("downloadedBytes", it.toDouble()) }
249
+ file.totalBytes?.let { putDouble("totalBytes", it.toDouble()) }
250
+ },
251
+ )
252
+ }
253
+
254
+ putMap(
255
+ "details",
217
256
  Arguments.createMap().apply {
218
- putString("path", file.path)
219
- putString("downloadPath", file.downloadPath)
220
- putString("status", file.status)
221
- putDouble("progress", file.progress)
222
- putInt("order", file.order)
223
- file.downloadedBytes?.let { putDouble("downloadedBytes", it.toDouble()) }
224
- file.totalBytes?.let { putDouble("totalBytes", it.toDouble()) }
257
+ putInt("totalFilesCount", details.totalFilesCount)
258
+ putInt("completedFilesCount", details.completedFilesCount)
259
+ putArray("files", files)
225
260
  },
226
261
  )
227
262
  }
228
-
229
- putMap(
230
- "details",
231
- Arguments.createMap().apply {
232
- putInt("totalFilesCount", details.totalFilesCount)
233
- putInt("completedFilesCount", details.completedFilesCount)
234
- putArray("files", files)
235
- },
236
- )
237
263
  }
238
- }
239
-
240
- this@HotUpdaterModule
241
- .mReactApplicationContext
242
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
243
- ?.emit("onProgress", progressParams)
244
- } catch (e: Exception) {
245
- Log.w("HotUpdater", "Failed to emit progress (bridge may be unavailable): ${e.message}")
264
+
265
+ this@HotUpdaterModule
266
+ .mReactApplicationContext
267
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
268
+ ?.emit("onProgress", progressParams)
269
+ } catch (e: Exception) {
270
+ Log.w("HotUpdater", "Failed to emit progress (bridge may be unavailable): ${e.message}")
271
+ }
246
272
  }
247
273
  }
248
- }
249
- promise.resolve(true)
274
+ promise.resolve(result.toWritableMap())
250
275
  } catch (e: HotUpdaterException) {
251
- promise.reject(e.code, e.message)
276
+ promise.rejectUpdateBundle(e)
252
277
  } catch (e: Exception) {
278
+ // Not an update failure (a stale Release selection, for example),
279
+ // so the rejection carries no classification.
253
280
  promise.reject("UNKNOWN_ERROR", e.message ?: "An unknown error occurred")
254
281
  }
255
282
  }
@@ -414,17 +441,14 @@ class HotUpdaterModule internal constructor(
414
441
  override fun getInstallId(): String = getInstance().getInstallId()
415
442
 
416
443
  @ReactMethod(isBlockingSynchronousMethod = true)
417
- override fun getUserId(): String? = getInstance().getUserId()
418
-
419
- @ReactMethod(isBlockingSynchronousMethod = true)
420
- override fun getUsername(): String? = getInstance().getUsername()
444
+ override fun getStorageItem(key: String): String? = getInstance().getStorageItem(key)
421
445
 
422
446
  @ReactMethod
423
- override fun setUser(
424
- userId: String?,
425
- username: String?,
447
+ override fun setStorageItem(
448
+ key: String,
449
+ value: String?,
426
450
  ) {
427
- getInstance().setUser(userId, username)
451
+ getInstance().setStorageItem(key, value)
428
452
  }
429
453
 
430
454
  @ReactMethod
@@ -62,13 +62,11 @@ abstract class HotUpdaterSpec internal constructor(
62
62
 
63
63
  abstract fun getInstallId(): String
64
64
 
65
- abstract fun getUserId(): String?
65
+ abstract fun getStorageItem(key: String): String?
66
66
 
67
- abstract fun getUsername(): String?
68
-
69
- abstract fun setUser(
70
- userId: String?,
71
- username: String?,
67
+ abstract fun setStorageItem(
68
+ key: String,
69
+ value: String?,
72
70
  )
73
71
 
74
72
  abstract fun resetChannel(promise: Promise)
@@ -0,0 +1,71 @@
1
+ package com.hotupdater
2
+
3
+ import com.facebook.react.ReactApplication
4
+ import com.facebook.react.bridge.JSExceptionHandler
5
+ import com.facebook.react.bridge.ReactContext
6
+
7
+ /**
8
+ * Routes JavaScript errors of the running React instance to crash recovery.
9
+ * The old architecture reaches the instance through ReactNativeHost, which only
10
+ * an old architecture build compiles.
11
+ */
12
+ internal object JavaScriptExceptionHooks {
13
+ private val patchedInstanceManagerIds = mutableSetOf<Int>()
14
+ private val patchedCatalystInstanceIds = mutableSetOf<Int>()
15
+
16
+ fun install(application: ReactApplication) {
17
+ val instanceManager = application.reactNativeHost.reactInstanceManager
18
+ val managerIdentity = System.identityHashCode(instanceManager)
19
+ if (patchedInstanceManagerIds.add(managerIdentity)) {
20
+ val exceptionHandlerField =
21
+ HotUpdaterRecoveryManager.findField(instanceManager.javaClass, "mJSExceptionHandler")
22
+ if (exceptionHandlerField == null) {
23
+ patchedInstanceManagerIds.remove(managerIdentity)
24
+ return
25
+ }
26
+
27
+ exceptionHandlerField.isAccessible = true
28
+ val previousHandler = exceptionHandlerField.get(instanceManager) as? JSExceptionHandler
29
+ exceptionHandlerField.set(
30
+ instanceManager,
31
+ HotUpdaterRecoveryManager.RecoveryJSExceptionHandler(previousHandler),
32
+ )
33
+ }
34
+
35
+ instanceManager.currentReactContext?.let { reactContext ->
36
+ HotUpdaterRecoveryManager.patchReactContextExceptionHandler(reactContext)
37
+ patchCatalystInstanceExceptionHandler(reactContext)
38
+ }
39
+ }
40
+
41
+ private fun patchCatalystInstanceExceptionHandler(reactContext: ReactContext) {
42
+ val catalystInstance =
43
+ try {
44
+ reactContext.catalystInstance
45
+ } catch (_: Exception) {
46
+ null
47
+ } ?: return
48
+
49
+ val catalystIdentity = System.identityHashCode(catalystInstance)
50
+ if (!patchedCatalystInstanceIds.add(catalystIdentity)) {
51
+ return
52
+ }
53
+
54
+ val exceptionHandlerField =
55
+ HotUpdaterRecoveryManager.findField(catalystInstance.javaClass, "mJSExceptionHandler")
56
+ if (exceptionHandlerField == null) {
57
+ patchedCatalystInstanceIds.remove(catalystIdentity)
58
+ return
59
+ }
60
+
61
+ exceptionHandlerField.isAccessible = true
62
+ val previousHandler = exceptionHandlerField.get(catalystInstance) as? JSExceptionHandler
63
+ if (previousHandler is HotUpdaterRecoveryManager.RecoveryJSExceptionHandler) {
64
+ return
65
+ }
66
+ exceptionHandlerField.set(
67
+ catalystInstance,
68
+ HotUpdaterRecoveryManager.RecoveryJSExceptionHandler(previousHandler),
69
+ )
70
+ }
71
+ }
@@ -0,0 +1,167 @@
1
+ import Compression
2
+ import Foundation
3
+
4
+ enum BrotliFileDecompressor {
5
+ private static let bufferSize = 64 * 1024
6
+
7
+ static func decompress(
8
+ from sourcePath: String,
9
+ to outputPath: String,
10
+ expectedOutputByteSize: Int64? = nil
11
+ ) throws {
12
+ if let expectedOutputByteSize, expectedOutputByteSize < 0 {
13
+ throw error(6, "Invalid expected Brotli output size")
14
+ }
15
+ let fileManager = FileManager.default
16
+ let parentDirectory = (outputPath as NSString).deletingLastPathComponent
17
+ try fileManager.createDirectory(
18
+ atPath: parentDirectory,
19
+ withIntermediateDirectories: true
20
+ )
21
+ if fileManager.fileExists(atPath: outputPath) {
22
+ try fileManager.removeItem(atPath: outputPath)
23
+ }
24
+ guard fileManager.createFile(atPath: outputPath, contents: nil) else {
25
+ throw error(1, "Failed to create Brotli output file")
26
+ }
27
+
28
+ let input = try FileHandle(forReadingFrom: URL(fileURLWithPath: sourcePath))
29
+ let output = try FileHandle(forWritingTo: URL(fileURLWithPath: outputPath))
30
+ defer {
31
+ try? input.close()
32
+ try? output.close()
33
+ }
34
+
35
+ var stream = compression_stream(
36
+ dst_ptr: UnsafeMutablePointer<UInt8>(bitPattern: 1)!,
37
+ dst_size: 0,
38
+ src_ptr: UnsafePointer<UInt8>(bitPattern: 1)!,
39
+ src_size: 0,
40
+ state: nil
41
+ )
42
+ guard compression_stream_init(
43
+ &stream,
44
+ COMPRESSION_STREAM_DECODE,
45
+ COMPRESSION_BROTLI
46
+ ) == COMPRESSION_STATUS_OK else {
47
+ throw error(2, "Failed to initialize Brotli decompression")
48
+ }
49
+ defer { compression_stream_destroy(&stream) }
50
+
51
+ let outputBuffer = UnsafeMutablePointer<UInt8>.allocate(
52
+ capacity: bufferSize
53
+ )
54
+ defer { outputBuffer.deallocate() }
55
+
56
+ var reachedEnd = false
57
+ var totalOutputByteSize: UInt64 = 0
58
+ let maximumOutputByteSize = expectedOutputByteSize.map(UInt64.init)
59
+ while !reachedEnd {
60
+ let chunk = try FileUtilities.readUpToCount(
61
+ from: input,
62
+ count: bufferSize
63
+ ) ?? Data()
64
+ let status: compression_status
65
+ if chunk.isEmpty {
66
+ stream.src_ptr = UnsafePointer<UInt8>(bitPattern: 1)!
67
+ stream.src_size = 0
68
+ status = try process(
69
+ &stream,
70
+ output: output,
71
+ outputBuffer: outputBuffer,
72
+ finalize: true,
73
+ totalOutputByteSize: &totalOutputByteSize,
74
+ maximumOutputByteSize: maximumOutputByteSize
75
+ )
76
+ } else {
77
+ status = try chunk.withUnsafeBytes { bytes in
78
+ guard let source = bytes.baseAddress?
79
+ .assumingMemoryBound(to: UInt8.self) else {
80
+ return COMPRESSION_STATUS_OK
81
+ }
82
+ stream.src_ptr = source
83
+ stream.src_size = chunk.count
84
+ return try process(
85
+ &stream,
86
+ output: output,
87
+ outputBuffer: outputBuffer,
88
+ finalize: false,
89
+ totalOutputByteSize: &totalOutputByteSize,
90
+ maximumOutputByteSize: maximumOutputByteSize
91
+ )
92
+ }
93
+ }
94
+
95
+ if status == COMPRESSION_STATUS_END {
96
+ guard stream.src_size == 0 else {
97
+ throw error(7, "Brotli stream contains trailing compressed bytes")
98
+ }
99
+ let trailing = try FileUtilities.readUpToCount(from: input, count: 1)
100
+ guard trailing?.isEmpty != false else {
101
+ throw error(7, "Brotli stream contains trailing compressed bytes")
102
+ }
103
+ reachedEnd = true
104
+ } else if chunk.isEmpty {
105
+ throw error(3, "Brotli input ended before the stream completed")
106
+ }
107
+ }
108
+
109
+ if let maximumOutputByteSize,
110
+ totalOutputByteSize != maximumOutputByteSize {
111
+ throw error(
112
+ 8,
113
+ "Brotli output size mismatch: expected \(maximumOutputByteSize), got \(totalOutputByteSize)"
114
+ )
115
+ }
116
+ }
117
+
118
+ private static func process(
119
+ _ stream: inout compression_stream,
120
+ output: FileHandle,
121
+ outputBuffer: UnsafeMutablePointer<UInt8>,
122
+ finalize: Bool,
123
+ totalOutputByteSize: inout UInt64,
124
+ maximumOutputByteSize: UInt64?
125
+ ) throws -> compression_status {
126
+ let flags = finalize ? Int32(COMPRESSION_STREAM_FINALIZE.rawValue) : 0
127
+ var status = COMPRESSION_STATUS_OK
128
+ repeat {
129
+ let previousSourceSize = stream.src_size
130
+ stream.dst_ptr = outputBuffer
131
+ stream.dst_size = bufferSize
132
+ status = compression_stream_process(&stream, flags)
133
+
134
+ guard status == COMPRESSION_STATUS_OK ||
135
+ status == COMPRESSION_STATUS_END else {
136
+ throw error(4, "Brotli decompression failed")
137
+ }
138
+ let produced = bufferSize - stream.dst_size
139
+ if produced > 0 {
140
+ let (nextOutputByteSize, overflowed) = totalOutputByteSize
141
+ .addingReportingOverflow(UInt64(produced))
142
+ guard !overflowed,
143
+ maximumOutputByteSize.map({ nextOutputByteSize <= $0 }) ?? true else {
144
+ throw error(6, "Brotli output exceeds expected size")
145
+ }
146
+ output.write(Data(bytes: outputBuffer, count: produced))
147
+ totalOutputByteSize = nextOutputByteSize
148
+ }
149
+ if finalize,
150
+ status == COMPRESSION_STATUS_OK,
151
+ produced == 0,
152
+ previousSourceSize == stream.src_size {
153
+ throw error(5, "Brotli decompression stalled")
154
+ }
155
+ } while stream.src_size > 0 || stream.dst_size == 0 ||
156
+ (finalize && status == COMPRESSION_STATUS_OK)
157
+ return status
158
+ }
159
+
160
+ private static func error(_ code: Int, _ message: String) -> NSError {
161
+ NSError(
162
+ domain: "BrotliFileDecompressor",
163
+ code: code,
164
+ userInfo: [NSLocalizedDescriptionKey: message]
165
+ )
166
+ }
167
+ }