@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
@@ -84,6 +84,7 @@ public struct BundleMetadata: Codable {
84
84
  var stableSelection: PersistedSelection?
85
85
  var stagingSelection: PersistedSelection?
86
86
  var verificationPending: Bool
87
+ var launchInProgress: Bool
87
88
  var pendingTransition: PendingBundleTransition?
88
89
  var pendingSelectionTransition: PendingSelectionTransition?
89
90
  var highestSeenCatalogs: [String: CatalogHighWater]
@@ -98,6 +99,7 @@ public struct BundleMetadata: Codable {
98
99
  case stableSelection = "stable_selection"
99
100
  case stagingSelection = "staging_selection"
100
101
  case verificationPending = "verification_pending"
102
+ case launchInProgress = "launch_in_progress"
101
103
  case pendingTransition = "pending_transition"
102
104
  case pendingSelectionTransition = "pending_selection_transition"
103
105
  case highestSeenCatalogs = "highest_seen_catalogs"
@@ -113,6 +115,7 @@ public struct BundleMetadata: Codable {
113
115
  stableSelection: PersistedSelection? = nil,
114
116
  stagingSelection: PersistedSelection? = nil,
115
117
  verificationPending: Bool = false,
118
+ launchInProgress: Bool = false,
116
119
  pendingTransition: PendingBundleTransition? = nil,
117
120
  pendingSelectionTransition: PendingSelectionTransition? = nil,
118
121
  highestSeenCatalogs: [String: CatalogHighWater] = [:],
@@ -126,6 +129,7 @@ public struct BundleMetadata: Codable {
126
129
  self.stableSelection = stableSelection
127
130
  self.stagingSelection = stagingSelection
128
131
  self.verificationPending = verificationPending
132
+ self.launchInProgress = launchInProgress
129
133
  self.pendingTransition = pendingTransition
130
134
  self.pendingSelectionTransition = pendingSelectionTransition
131
135
  self.highestSeenCatalogs = highestSeenCatalogs
@@ -144,6 +148,7 @@ public struct BundleMetadata: Codable {
144
148
  stagingSelection = try values.decodeIfPresent(PersistedSelection.self, forKey: .stagingSelection)
145
149
  ?? stagingBundleId.map(PersistedSelection.legacyBundle)
146
150
  verificationPending = try values.decodeIfPresent(Bool.self, forKey: .verificationPending) ?? false
151
+ launchInProgress = try values.decodeIfPresent(Bool.self, forKey: .launchInProgress) ?? false
147
152
  pendingTransition = try values.decodeIfPresent(PendingBundleTransition.self, forKey: .pendingTransition)
148
153
  pendingSelectionTransition = try values.decodeIfPresent(PendingSelectionTransition.self, forKey: .pendingSelectionTransition)
149
154
  highestSeenCatalogs = try values.decodeIfPresent([String: CatalogHighWater].self, forKey: .highestSeenCatalogs) ?? [:]
@@ -217,6 +222,37 @@ public struct CrashedBundleEntry: Codable {
217
222
  }
218
223
  }
219
224
 
225
+ // MARK: - InterruptedLaunch
226
+
227
+ /// A bundle whose launch ended before first content without a crash, waiting
228
+ /// for its one retry. Such a launch cannot tell a hang from a user leaving
229
+ /// early, so the bundle is offered again instead of entering crash history.
230
+ /// A second unfinished launch of it adds it there.
231
+ ///
232
+ /// `retryReady` turns true once a later launch shows content. The retry starts
233
+ /// with the next process, so the session that recovered never reloads into the
234
+ /// same bundle.
235
+ public struct InterruptedLaunch: Codable {
236
+ let bundleId: String
237
+ var retryReady: Bool
238
+
239
+ enum CodingKeys: String, CodingKey {
240
+ case bundleId
241
+ case retryReady
242
+ }
243
+
244
+ init(bundleId: String, retryReady: Bool = false) {
245
+ self.bundleId = bundleId
246
+ self.retryReady = retryReady
247
+ }
248
+
249
+ public init(from decoder: Decoder) throws {
250
+ let values = try decoder.container(keyedBy: CodingKeys.self)
251
+ bundleId = try values.decode(String.self, forKey: .bundleId)
252
+ retryReady = try values.decodeIfPresent(Bool.self, forKey: .retryReady) ?? false
253
+ }
254
+ }
255
+
220
256
  // MARK: - CrashedHistory
221
257
 
222
258
  /// History of crashed bundles
@@ -226,10 +262,30 @@ public struct CrashedHistory: Codable {
226
262
 
227
263
  var bundles: [CrashedBundleEntry]
228
264
  var maxHistorySize: Int
265
+ var interruptedLaunch: InterruptedLaunch?
266
+
267
+ enum CodingKeys: String, CodingKey {
268
+ case bundles
269
+ case maxHistorySize
270
+ case interruptedLaunch
271
+ }
229
272
 
230
- init(bundles: [CrashedBundleEntry] = [], maxHistorySize: Int = CrashedHistory.defaultMaxHistorySize) {
273
+ init(
274
+ bundles: [CrashedBundleEntry] = [],
275
+ maxHistorySize: Int = CrashedHistory.defaultMaxHistorySize,
276
+ interruptedLaunch: InterruptedLaunch? = nil
277
+ ) {
231
278
  self.bundles = bundles
232
279
  self.maxHistorySize = maxHistorySize
280
+ self.interruptedLaunch = interruptedLaunch
281
+ }
282
+
283
+ public init(from decoder: Decoder) throws {
284
+ let values = try decoder.container(keyedBy: CodingKeys.self)
285
+ bundles = try values.decode([CrashedBundleEntry].self, forKey: .bundles)
286
+ maxHistorySize = try values.decode(Int.self, forKey: .maxHistorySize)
287
+ // A malformed retry record must not drop the crash history.
288
+ interruptedLaunch = try? values.decodeIfPresent(InterruptedLaunch.self, forKey: .interruptedLaunch)
233
289
  }
234
290
 
235
291
  static func load(from file: URL) -> CrashedHistory {
@@ -293,6 +349,7 @@ public struct CrashedHistory: Codable {
293
349
 
294
350
  mutating func clear() {
295
351
  bundles.removeAll()
352
+ interruptedLaunch = nil
296
353
  }
297
354
  }
298
355
 
@@ -378,6 +435,8 @@ public struct LaunchReport: Codable {
378
435
  }
379
436
  }
380
437
 
438
+ /// The random id created once per app installation. It lives in
439
+ /// `NoBackupStorage`, so a device backup never carries it to another device.
381
440
  public struct InstallIdentity: Codable {
382
441
  static let installIdentityFilename = "install-identity.json"
383
442
 
@@ -407,11 +466,7 @@ public struct InstallIdentity: Codable {
407
466
  let encoder = JSONEncoder()
408
467
  encoder.outputFormatting = .prettyPrinted
409
468
  let data = try encoder.encode(self)
410
- let directory = file.deletingLastPathComponent()
411
- if !FileManager.default.fileExists(atPath: directory.path) {
412
- try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
413
- }
414
- try data.write(to: file)
469
+ try NoBackupStorage.write(data, to: file)
415
470
  return true
416
471
  } catch {
417
472
  print("[InstallIdentity] Failed to save install identity: \(error)")
@@ -419,51 +474,3 @@ public struct InstallIdentity: Codable {
419
474
  }
420
475
  }
421
476
  }
422
-
423
- public struct UserIdentity: Codable {
424
- static let userIdentityFilename = "user-identity.json"
425
-
426
- let userId: String?
427
- let username: String?
428
-
429
- init(userId: String?, username: String?) {
430
- self.userId = userId
431
- self.username = username
432
- }
433
-
434
- var isEmpty: Bool {
435
- userId == nil && username == nil
436
- }
437
-
438
- static func load(from file: URL) -> UserIdentity? {
439
- guard FileManager.default.fileExists(atPath: file.path) else {
440
- return nil
441
- }
442
-
443
- do {
444
- let data = try Data(contentsOf: file)
445
- let identity = try JSONDecoder().decode(UserIdentity.self, from: data)
446
- return identity.isEmpty ? nil : identity
447
- } catch {
448
- print("[UserIdentity] Failed to load user identity: \(error)")
449
- return nil
450
- }
451
- }
452
-
453
- func save(to file: URL) -> Bool {
454
- do {
455
- let encoder = JSONEncoder()
456
- encoder.outputFormatting = .prettyPrinted
457
- let data = try encoder.encode(self)
458
- let directory = file.deletingLastPathComponent()
459
- if !FileManager.default.fileExists(atPath: directory.path) {
460
- try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
461
- }
462
- try data.write(to: file)
463
- return true
464
- } catch {
465
- print("[UserIdentity] Failed to save user identity: \(error)")
466
- return false
467
- }
468
- }
469
- }
@@ -17,6 +17,45 @@ protocol FileSystemService {
17
17
  func contentsOfDirectory(atPath path: String) throws -> [String]
18
18
  func attributesOfItem(atPath path: String) throws -> [FileAttributeKey: Any]
19
19
  func documentsPath() -> String
20
+ /// Base directory for data that lives as long as the installation.
21
+ func applicationSupportPath() -> String
22
+ }
23
+
24
+ /// `Application Support/HotUpdater`: data the SDK keeps for the life of an
25
+ /// installation, such as the install id and plugin storage. Device backups
26
+ /// skip it, so restoring a backup onto another device never copies it there.
27
+ enum NoBackupStorage {
28
+ static let directoryName = "HotUpdater"
29
+
30
+ static func directory(in fileSystem: FileSystemService) -> URL {
31
+ URL(fileURLWithPath: fileSystem.applicationSupportPath(), isDirectory: true)
32
+ .appendingPathComponent(directoryName, isDirectory: true)
33
+ }
34
+
35
+ /// Atomically replaces `file` with `data`, creating its directory when
36
+ /// needed, and excludes the directory and the file from device backups.
37
+ static func write(_ data: Data, to file: URL) throws {
38
+ let directory = file.deletingLastPathComponent()
39
+ try FileManager.default.createDirectory(
40
+ at: directory,
41
+ withIntermediateDirectories: true
42
+ )
43
+ excludeFromBackup(directory)
44
+ try data.write(to: file, options: .atomic)
45
+ // An atomic write replaces the file, which drops its previous flag.
46
+ excludeFromBackup(file)
47
+ }
48
+
49
+ static func excludeFromBackup(_ url: URL) {
50
+ var url = url
51
+ var values = URLResourceValues()
52
+ values.isExcludedFromBackup = true
53
+ do {
54
+ try url.setResourceValues(values)
55
+ } catch {
56
+ NSLog("[NoBackupStorage] Failed to exclude \(url.path) from backups: \(error)")
57
+ }
58
+ }
20
59
  }
21
60
 
22
61
  class FileManagerService: FileSystemService {
@@ -89,4 +128,13 @@ class FileManagerService: FileSystemService {
89
128
  return NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
90
129
  #endif
91
130
  }
131
+
132
+ func applicationSupportPath() -> String {
133
+ #if os(tvOS)
134
+ // tvOS apps can only write purgeable files, so use Caches like documentsPath().
135
+ return NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)[0]
136
+ #else
137
+ return NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)[0]
138
+ #endif
139
+ }
92
140
  }
@@ -0,0 +1,64 @@
1
+ import Foundation
2
+
3
+ enum FileUtilities {
4
+ static func readUpToCount(from handle: FileHandle, count: Int) throws -> Data? {
5
+ guard count >= 0 else {
6
+ throw NSError(
7
+ domain: "FileUtilities",
8
+ code: 1,
9
+ userInfo: [NSLocalizedDescriptionKey: "Invalid read size: \(count)"]
10
+ )
11
+ }
12
+
13
+ if count == 0 {
14
+ return Data()
15
+ }
16
+
17
+ if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
18
+ return try handle.read(upToCount: count)
19
+ }
20
+
21
+ return handle.readData(ofLength: count)
22
+ }
23
+
24
+ static func normalizedRelativePath(from rawPath: String) -> String? {
25
+ let candidate = rawPath.trimmingCharacters(in: .whitespacesAndNewlines)
26
+ guard !candidate.isEmpty,
27
+ !candidate.contains("\0"),
28
+ !candidate.contains("\\"),
29
+ !candidate.hasPrefix("/"),
30
+ candidate.range(of: #"^[A-Za-z]:"#, options: .regularExpression) == nil
31
+ else {
32
+ return nil
33
+ }
34
+
35
+ let components = candidate.split(separator: "/").map(String.init)
36
+ guard !components.isEmpty,
37
+ components.count == candidate.split(separator: "/", omittingEmptySubsequences: false).count,
38
+ !components.contains(".."),
39
+ !components.contains(".")
40
+ else {
41
+ return nil
42
+ }
43
+
44
+ return components.joined(separator: "/")
45
+ }
46
+
47
+ static func fileURL(for relativePath: String, destinationRoot: String) throws -> URL {
48
+ let rootURL = URL(fileURLWithPath: destinationRoot, isDirectory: true)
49
+ let targetURL = rootURL.appendingPathComponent(relativePath)
50
+ let standardizedRoot = rootURL.standardizedFileURL.path
51
+ let standardizedTarget = targetURL.standardizedFileURL.path
52
+
53
+ guard standardizedTarget == standardizedRoot ||
54
+ standardizedTarget.hasPrefix(standardizedRoot + "/") else {
55
+ throw NSError(
56
+ domain: "FileUtilities",
57
+ code: 2,
58
+ userInfo: [NSLocalizedDescriptionKey: "Path traversal attempt detected: \(relativePath)"]
59
+ )
60
+ }
61
+
62
+ return targetURL
63
+ }
64
+ }
@@ -7,7 +7,6 @@
7
7
  #import "React/RCTConstants.h"
8
8
  #import "React/RCTRootView.h"
9
9
  #import "React/RCTUtils.h" // Needed for RCTPromiseResolveBlock/RejectBlock in Swift
10
- #import <SSZipArchive/SSZipArchive.h>
11
10
  #import "BsdiffPatchBridge.h"
12
11
 
13
12
  @interface HotUpdaterRecoverySignalBridge : NSObject
@@ -213,31 +213,28 @@ extern "C" NSString *HotUpdaterGetMinBundleId(void)
213
213
  #if DEBUG
214
214
  uuid = @"00000000-0000-0000-0000-000000000000";
215
215
  #else
216
- // Step 1: Try to read HOT_UPDATER_BUILD_TIMESTAMP from Info.plist
217
- NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
218
- NSString *customValue = infoDictionary[@"HOT_UPDATER_BUILD_TIMESTAMP"];
219
-
220
- // Step 2: If custom value exists and is not empty
221
- if (customValue && customValue.length > 0 && ![customValue isEqualToString:@"$(HOT_UPDATER_BUILD_TIMESTAMP)"]) {
222
- // Check if it's a timestamp (pure digits) or UUID
223
- NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
224
- BOOL isTimestamp = ([customValue rangeOfCharacterFromSet:nonDigits].location == NSNotFound);
225
-
226
- if (isTimestamp) {
227
- // Convert timestamp (milliseconds) to UUID v7
228
- uint64_t timestampMs = [customValue longLongValue];
229
- uuid = [HotUpdater generateUUIDv7FromTimestamp:timestampMs];
230
- RCTLogInfo(@"[HotUpdater.mm] Using timestamp %@ as MIN_BUNDLE_ID: %@", customValue, uuid);
231
- } else {
232
- // Use as UUID directly
233
- uuid = customValue;
234
- RCTLogInfo(@"[HotUpdater.mm] Using custom MIN_BUNDLE_ID from Info.plist: %@", uuid);
216
+ // Step 1: Use the HOT_UPDATER_MIN_BUNDLE_ID build setting, which the
217
+ // hot-updater CLI passes to xcodebuild and the Info.plist slot it adds
218
+ // (`$(HOT_UPDATER_MIN_BUNDLE_ID)`) carries into the app, as Android
219
+ // takes -PMIN_BUNDLE_ID.
220
+ NSString *customValue = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"HOT_UPDATER_MIN_BUNDLE_ID"];
221
+ if ([customValue isKindOfClass:[NSString class]]) {
222
+ NSString *trimmed = [customValue stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
223
+ // An unset build setting leaves the slot empty or unexpanded; anything
224
+ // but a UUID falls back to the compile time below.
225
+ NSUUID *parsed = trimmed.length > 0 ? [[NSUUID alloc] initWithUUIDString:trimmed] : nil;
226
+ if (parsed) {
227
+ uuid = parsed.UUIDString.lowercaseString;
228
+ RCTLogInfo(@"[HotUpdater.mm] Using MIN_BUNDLE_ID from the HOT_UPDATER_MIN_BUNDLE_ID build setting: %@", uuid);
229
+ return;
230
+ }
231
+ if (trimmed.length > 0 && ![trimmed isEqualToString:@"$(HOT_UPDATER_MIN_BUNDLE_ID)"]) {
232
+ RCTLogWarn(@"[HotUpdater.mm] Ignoring HOT_UPDATER_MIN_BUNDLE_ID %@: not a UUID", trimmed);
235
233
  }
236
- return;
237
234
  }
238
235
 
239
- // Step 3: Fallback to default logic (26-hour subtraction)
240
- RCTLogInfo(@"[HotUpdater.mm] No custom MIN_BUNDLE_ID found, using default calculation");
236
+ // Step 2: Fall back to the compile time (26-hour subtraction)
237
+ RCTLogInfo(@"[HotUpdater.mm] No HOT_UPDATER_MIN_BUNDLE_ID build setting, using the compile time");
241
238
 
242
239
  NSString *compileDateStr = [NSString stringWithFormat:@"%s %s", __DATE__, __TIME__];
243
240
  NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
@@ -274,8 +271,6 @@ extern "C" void *HotUpdaterCopyMinBundleId(void)
274
271
 
275
272
 
276
273
  // Define Notification names used for observing Swift Core
277
- NSNotificationName const HotUpdaterDownloadProgressUpdateNotification = @"HotUpdaterDownloadProgressUpdate";
278
- NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterDownloadDidFinish";
279
274
  NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUpdaterUpdateProgressDidChange";
280
275
 
281
276
  @interface HotUpdaterRecoverySignalBridge : NSObject
@@ -300,8 +295,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
300
295
 
301
296
  @implementation HotUpdater {
302
297
  bool hasListeners;
303
- // Keep track of tasks ONLY for removing observers when this ObjC instance is invalidated
304
- NSMutableSet<NSURLSessionTask *> *observedTasks; // Changed to NSURLSessionTask for broader compatibility if needed
305
298
  }
306
299
 
307
300
  @synthesize bridge = _bridge;
@@ -314,16 +307,10 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
314
307
  - (instancetype)init {
315
308
  self = [super init];
316
309
  if (self) {
317
- observedTasks = [NSMutableSet set];
318
-
319
310
  [[NSNotificationCenter defaultCenter] addObserver:self
320
311
  selector:@selector(handleDownloadProgress:)
321
312
  name:HotUpdaterUpdateProgressDidChangeNotification
322
313
  object:nil];
323
- [[NSNotificationCenter defaultCenter] addObserver:self
324
- selector:@selector(handleDownloadCompletion:)
325
- name:HotUpdaterDownloadDidFinishNotification
326
- object:nil];
327
314
 
328
315
  _lastUpdateTime = 0;
329
316
  }
@@ -349,7 +336,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
349
336
  if (gHotUpdaterBridge == self.bridge) {
350
337
  gHotUpdaterBridge = nil;
351
338
  }
352
- // Swift side should handle KVO observer removal for its tasks
353
339
  [super invalidate];
354
340
  }
355
341
 
@@ -520,14 +506,6 @@ RCT_EXPORT_MODULE();
520
506
  }
521
507
  }
522
508
 
523
- - (void)handleDownloadCompletion:(NSNotification *)notification {
524
- NSURLSessionTask *task = notification.object; // Task that finished
525
- RCTLogInfo(@"[HotUpdater.mm] Received download completion notification for task: %@", task.originalRequest.URL);
526
- // Swift side handles KVO observer removal internally now when task finishes.
527
- // No specific cleanup needed here based on this notification anymore.
528
- }
529
-
530
-
531
509
  #pragma mark - React Native Events (Keep as is)
532
510
 
533
511
  - (NSArray<NSString *> *)supportedEvents {
@@ -594,20 +572,17 @@ RCT_EXPORT_MODULE();
594
572
  if (params.bundleId()) {
595
573
  paramDict[@"bundleId"] = params.bundleId();
596
574
  }
597
- if (params.fileUrl()) {
598
- paramDict[@"fileUrl"] = params.fileUrl();
599
- }
600
- if (params.fileHash()) {
601
- paramDict[@"fileHash"] = params.fileHash();
602
- }
603
575
  if (params.manifestUrl()) {
604
576
  paramDict[@"manifestUrl"] = params.manifestUrl();
605
577
  }
606
578
  if (params.manifestFileHash()) {
607
579
  paramDict[@"manifestFileHash"] = params.manifestFileHash();
608
580
  }
609
- if (params.changedAssets()) {
610
- paramDict[@"changedAssets"] = params.changedAssets();
581
+ if (params.archiveUrl()) {
582
+ paramDict[@"archiveUrl"] = params.archiveUrl();
583
+ }
584
+ if (params.assets()) {
585
+ paramDict[@"assets"] = params.assets();
611
586
  }
612
587
  if (params.channel()) {
613
588
  paramDict[@"channel"] = params.channel();
@@ -682,19 +657,14 @@ RCT_EXPORT_MODULE();
682
657
  return [impl getInstallId];
683
658
  }
684
659
 
685
- - (NSString * _Nullable)getUserId {
660
+ - (NSString * _Nullable)getStorageItem:(NSString *)key {
686
661
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
687
- return [impl getUserId];
662
+ return [impl getStorageItem:key];
688
663
  }
689
664
 
690
- - (NSString * _Nullable)getUsername {
665
+ - (void)setStorageItem:(NSString *)key value:(NSString * _Nullable)value {
691
666
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
692
- return [impl getUsername];
693
- }
694
-
695
- - (void)setUser:(NSString * _Nullable)userId username:(NSString * _Nullable)username {
696
- HotUpdaterImpl *impl = [HotUpdater sharedImpl];
697
- [impl setUser:userId username:username];
667
+ [impl setStorageItem:key value:value];
698
668
  }
699
669
 
700
670
  - (NSString * _Nullable)getBaseURL {
@@ -852,19 +822,14 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getInstallId) {
852
822
  return [impl getInstallId];
853
823
  }
854
824
 
855
- RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getUserId) {
856
- HotUpdaterImpl *impl = [HotUpdater sharedImpl];
857
- return [impl getUserId];
858
- }
859
-
860
- RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getUsername) {
825
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getStorageItem:(NSString *)key) {
861
826
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
862
- return [impl getUsername];
827
+ return [impl getStorageItem:key];
863
828
  }
864
829
 
865
- RCT_EXPORT_METHOD(setUser:(NSString * _Nullable)userId username:(NSString * _Nullable)username) {
830
+ RCT_EXPORT_METHOD(setStorageItem:(NSString *)key value:(NSString * _Nullable)value) {
866
831
  HotUpdaterImpl *impl = [HotUpdater sharedImpl];
867
- [impl setUser:userId username:username];
832
+ [impl setStorageItem:key value:value];
868
833
  }
869
834
 
870
835
  RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBaseURL) {