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

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 (306) 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 +102 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +120 -68
  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 +4 -13
  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 +63 -94
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +71 -18
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/native.js +74 -60
  64. package/lib/commonjs/native.js.map +1 -1
  65. package/lib/commonjs/pluginHost.js +45 -0
  66. package/lib/commonjs/pluginHost.js.map +1 -0
  67. package/lib/commonjs/releaseCatalogCache.js +36 -87
  68. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  69. package/lib/commonjs/sdkVersion.js +1 -1
  70. package/lib/commonjs/sdkVersion.js.map +1 -1
  71. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  72. package/lib/commonjs/store.js +3 -10
  73. package/lib/commonjs/store.js.map +1 -1
  74. package/lib/commonjs/updateError.js +145 -0
  75. package/lib/commonjs/updateError.js.map +1 -0
  76. package/lib/commonjs/wrap.js +43 -26
  77. package/lib/commonjs/wrap.js.map +1 -1
  78. package/lib/module/appReady.js +95 -0
  79. package/lib/module/appReady.js.map +1 -0
  80. package/lib/module/checkForUpdate.js +113 -63
  81. package/lib/module/checkForUpdate.js.map +1 -1
  82. package/lib/module/clientPlugin.js +4 -0
  83. package/lib/module/clientPlugin.js.map +1 -0
  84. package/lib/module/error.js +4 -13
  85. package/lib/module/error.js.map +1 -1
  86. package/lib/module/fetchJSON.js +14 -26
  87. package/lib/module/fetchJSON.js.map +1 -1
  88. package/lib/module/fetchUpdateResponse.js +55 -0
  89. package/lib/module/fetchUpdateResponse.js.map +1 -0
  90. package/lib/module/httpClient.js +62 -93
  91. package/lib/module/httpClient.js.map +1 -1
  92. package/lib/module/index.js +58 -20
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/native.js +67 -53
  95. package/lib/module/native.js.map +1 -1
  96. package/lib/module/pluginHost.js +36 -0
  97. package/lib/module/pluginHost.js.map +1 -0
  98. package/lib/module/releaseCatalogCache.js +32 -82
  99. package/lib/module/releaseCatalogCache.js.map +1 -1
  100. package/lib/module/sdkVersion.js +1 -1
  101. package/lib/module/sdkVersion.js.map +1 -1
  102. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  103. package/lib/module/store.js +3 -10
  104. package/lib/module/store.js.map +1 -1
  105. package/lib/module/updateError.js +139 -0
  106. package/lib/module/updateError.js.map +1 -0
  107. package/lib/module/wrap.js +43 -26
  108. package/lib/module/wrap.js.map +1 -1
  109. package/lib/typescript/commonjs/appReady.d.ts +21 -0
  110. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
  112. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  114. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/error.d.ts +4 -13
  116. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  118. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  120. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/httpClient.d.ts +4 -33
  122. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/index.d.ts +4 -8
  124. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/native.d.ts +29 -22
  126. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
  128. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  130. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  132. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  134. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/store.d.ts +1 -3
  136. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/types.d.ts +1 -1
  138. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  140. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
  143. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  144. package/lib/typescript/module/appReady.d.ts +21 -0
  145. package/lib/typescript/module/appReady.d.ts.map +1 -0
  146. package/lib/typescript/module/checkForUpdate.d.ts +16 -4
  147. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  148. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  149. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  150. package/lib/typescript/module/error.d.ts +4 -13
  151. package/lib/typescript/module/error.d.ts.map +1 -1
  152. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  153. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  154. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  155. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  156. package/lib/typescript/module/httpClient.d.ts +4 -33
  157. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  158. package/lib/typescript/module/index.d.ts +4 -8
  159. package/lib/typescript/module/index.d.ts.map +1 -1
  160. package/lib/typescript/module/native.d.ts +29 -22
  161. package/lib/typescript/module/native.d.ts.map +1 -1
  162. package/lib/typescript/module/pluginHost.d.ts +4 -0
  163. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  164. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  165. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  166. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  167. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  168. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  169. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  170. package/lib/typescript/module/store.d.ts +1 -3
  171. package/lib/typescript/module/store.d.ts.map +1 -1
  172. package/lib/typescript/module/types.d.ts +1 -1
  173. package/lib/typescript/module/types.d.ts.map +1 -1
  174. package/lib/typescript/module/updateError.d.ts +26 -0
  175. package/lib/typescript/module/updateError.d.ts.map +1 -0
  176. package/lib/typescript/module/wrap.d.ts.map +1 -1
  177. package/lib/typescript/module/wrap.types.d.ts +6 -7
  178. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  179. package/package.json +7 -4
  180. package/src/appReady.ts +141 -0
  181. package/src/checkForUpdate.ts +145 -86
  182. package/src/clientPlugin.ts +18 -0
  183. package/src/error.ts +4 -14
  184. package/src/fetchJSON.ts +14 -30
  185. package/src/fetchUpdateResponse.ts +60 -0
  186. package/src/httpClient.ts +88 -159
  187. package/src/index.ts +90 -24
  188. package/src/native.ts +98 -102
  189. package/src/pluginHost.ts +47 -0
  190. package/src/releaseCatalogCache.ts +43 -172
  191. package/src/sdkVersion.ts +1 -1
  192. package/src/specs/NativeHotUpdater.ts +34 -48
  193. package/src/store.ts +2 -23
  194. package/src/types.ts +1 -1
  195. package/src/updateError.ts +167 -0
  196. package/src/wrap.tsx +57 -26
  197. package/src/wrap.types.ts +6 -7
  198. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  199. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  200. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  201. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  202. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  203. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  204. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  205. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  206. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  207. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  208. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  209. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  210. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  211. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  212. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  213. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  214. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  215. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  216. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  217. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  218. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  219. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  220. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  221. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  222. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  223. package/lib/commonjs/httpClient.spec.js +0 -102
  224. package/lib/commonjs/httpClient.spec.js.map +0 -1
  225. package/lib/commonjs/index.spec.js +0 -302
  226. package/lib/commonjs/index.spec.js.map +0 -1
  227. package/lib/commonjs/iosInterop.spec.js +0 -18
  228. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  229. package/lib/commonjs/native.spec.js +0 -883
  230. package/lib/commonjs/native.spec.js.map +0 -1
  231. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  232. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  233. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  234. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  235. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  236. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  237. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  238. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  239. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  240. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  241. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  242. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  243. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  244. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  245. package/lib/commonjs/store.notification.spec.js +0 -76
  246. package/lib/commonjs/store.notification.spec.js.map +0 -1
  247. package/lib/commonjs/store.spec.js +0 -316
  248. package/lib/commonjs/store.spec.js.map +0 -1
  249. package/lib/commonjs/wrap.spec.js +0 -329
  250. package/lib/commonjs/wrap.spec.js.map +0 -1
  251. package/lib/module/catalogCacheNative.spec.js +0 -31
  252. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  253. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  254. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  255. package/lib/module/httpClient.spec.js +0 -102
  256. package/lib/module/httpClient.spec.js.map +0 -1
  257. package/lib/module/index.spec.js +0 -301
  258. package/lib/module/index.spec.js.map +0 -1
  259. package/lib/module/iosInterop.spec.js +0 -18
  260. package/lib/module/iosInterop.spec.js.map +0 -1
  261. package/lib/module/native.spec.js +0 -882
  262. package/lib/module/native.spec.js.map +0 -1
  263. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  264. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  265. package/lib/module/notifyAppReadyInsights.js +0 -128
  266. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  267. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  268. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  269. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  270. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  271. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  272. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  273. package/lib/module/releaseCatalogCache.spec.js +0 -195
  274. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  275. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  276. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  277. package/lib/module/store.notification.spec.js +0 -77
  278. package/lib/module/store.notification.spec.js.map +0 -1
  279. package/lib/module/store.spec.js +0 -315
  280. package/lib/module/store.spec.js.map +0 -1
  281. package/lib/module/wrap.spec.js +0 -330
  282. package/lib/module/wrap.spec.js.map +0 -1
  283. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  284. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  285. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  286. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  287. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  288. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  289. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  290. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  291. package/src/catalogCacheNative.spec.ts +0 -63
  292. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  293. package/src/httpClient.spec.ts +0 -132
  294. package/src/index.spec.ts +0 -337
  295. package/src/iosInterop.spec.ts +0 -30
  296. package/src/native.spec.ts +0 -945
  297. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  298. package/src/notifyAppReadyInsights.spec.ts +0 -243
  299. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  300. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  301. package/src/notifyAppReadyInsights.ts +0 -194
  302. package/src/releaseCatalogCache.spec.ts +0 -282
  303. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  304. package/src/store.notification.spec.ts +0 -92
  305. package/src/store.spec.ts +0 -372
  306. 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
+ }