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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/android/build.gradle +13 -2
  2. package/android/proguard-rules.pro +8 -2
  3. package/android/src/bridge/BridgeReactInstance.kt +38 -0
  4. package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
  5. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
  6. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  7. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
  8. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
  9. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  11. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
  12. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
  13. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  14. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  15. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  16. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  17. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  18. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  19. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  20. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  21. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  22. package/android/src/newarch/HotUpdater.kt +7 -9
  23. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  24. package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
  25. package/android/src/newarch/ReactIntegrationManager.kt +6 -32
  26. package/android/src/oldarch/HotUpdater.kt +7 -9
  27. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  28. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  29. package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
  30. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  31. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  32. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
  33. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
  34. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  35. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  36. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  37. package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
  38. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
  39. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  40. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  41. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  42. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  43. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  44. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  45. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  46. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  47. package/lib/commonjs/appReady.js +96 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +121 -69
  50. package/lib/commonjs/checkForUpdate.js.map +1 -1
  51. package/lib/commonjs/clientPlugin.js +13 -0
  52. package/lib/commonjs/clientPlugin.js.map +1 -0
  53. package/lib/commonjs/error.js +5 -14
  54. package/lib/commonjs/error.js.map +1 -1
  55. package/lib/commonjs/fetchJSON.js +16 -27
  56. package/lib/commonjs/fetchJSON.js.map +1 -1
  57. package/lib/commonjs/fetchUpdateResponse.js +60 -0
  58. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  59. package/lib/commonjs/httpClient.js +64 -95
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +355 -324
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/init.types.js +6 -0
  64. package/lib/commonjs/init.types.js.map +1 -0
  65. package/lib/commonjs/native.js +77 -63
  66. package/lib/commonjs/native.js.map +1 -1
  67. package/lib/commonjs/pluginHost.js +41 -0
  68. package/lib/commonjs/pluginHost.js.map +1 -0
  69. package/lib/commonjs/releaseCatalogCache.js +36 -87
  70. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  71. package/lib/commonjs/sdkVersion.js +1 -1
  72. package/lib/commonjs/sdkVersion.js.map +1 -1
  73. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  74. package/lib/commonjs/store.js +3 -10
  75. package/lib/commonjs/store.js.map +1 -1
  76. package/lib/commonjs/updateError.js +145 -0
  77. package/lib/commonjs/updateError.js.map +1 -0
  78. package/lib/commonjs/wrap.js +46 -38
  79. package/lib/commonjs/wrap.js.map +1 -1
  80. package/lib/module/appReady.js +90 -0
  81. package/lib/module/appReady.js.map +1 -0
  82. package/lib/module/checkForUpdate.js +114 -64
  83. package/lib/module/checkForUpdate.js.map +1 -1
  84. package/lib/module/clientPlugin.js +4 -0
  85. package/lib/module/clientPlugin.js.map +1 -0
  86. package/lib/module/error.js +5 -14
  87. package/lib/module/error.js.map +1 -1
  88. package/lib/module/fetchJSON.js +14 -26
  89. package/lib/module/fetchJSON.js.map +1 -1
  90. package/lib/module/fetchUpdateResponse.js +55 -0
  91. package/lib/module/fetchUpdateResponse.js.map +1 -0
  92. package/lib/module/httpClient.js +63 -94
  93. package/lib/module/httpClient.js.map +1 -1
  94. package/lib/module/index.js +335 -327
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/init.types.js +4 -0
  97. package/lib/module/init.types.js.map +1 -0
  98. package/lib/module/native.js +70 -56
  99. package/lib/module/native.js.map +1 -1
  100. package/lib/module/pluginHost.js +36 -0
  101. package/lib/module/pluginHost.js.map +1 -0
  102. package/lib/module/releaseCatalogCache.js +32 -82
  103. package/lib/module/releaseCatalogCache.js.map +1 -1
  104. package/lib/module/sdkVersion.js +1 -1
  105. package/lib/module/sdkVersion.js.map +1 -1
  106. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  107. package/lib/module/store.js +3 -10
  108. package/lib/module/store.js.map +1 -1
  109. package/lib/module/updateError.js +139 -0
  110. package/lib/module/updateError.js.map +1 -0
  111. package/lib/module/wrap.js +47 -38
  112. package/lib/module/wrap.js.map +1 -1
  113. package/lib/typescript/commonjs/appReady.d.ts +30 -0
  114. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
  116. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  118. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/error.d.ts +5 -14
  120. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  122. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  124. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/httpClient.d.ts +5 -34
  126. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/index.d.ts +124 -116
  128. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  130. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/native.d.ts +32 -25
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
  134. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  136. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/types.d.ts +1 -1
  144. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  146. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  148. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
  150. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  151. package/lib/typescript/module/appReady.d.ts +30 -0
  152. package/lib/typescript/module/appReady.d.ts.map +1 -0
  153. package/lib/typescript/module/checkForUpdate.d.ts +20 -6
  154. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  155. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  156. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  157. package/lib/typescript/module/error.d.ts +5 -14
  158. package/lib/typescript/module/error.d.ts.map +1 -1
  159. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  160. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  161. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  162. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  163. package/lib/typescript/module/httpClient.d.ts +5 -34
  164. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  165. package/lib/typescript/module/index.d.ts +124 -116
  166. package/lib/typescript/module/index.d.ts.map +1 -1
  167. package/lib/typescript/module/init.types.d.ts +21 -0
  168. package/lib/typescript/module/init.types.d.ts.map +1 -0
  169. package/lib/typescript/module/native.d.ts +32 -25
  170. package/lib/typescript/module/native.d.ts.map +1 -1
  171. package/lib/typescript/module/pluginHost.d.ts +8 -0
  172. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  173. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  174. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  175. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  176. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  177. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  179. package/lib/typescript/module/store.d.ts +1 -3
  180. package/lib/typescript/module/store.d.ts.map +1 -1
  181. package/lib/typescript/module/types.d.ts +1 -1
  182. package/lib/typescript/module/types.d.ts.map +1 -1
  183. package/lib/typescript/module/updateError.d.ts +26 -0
  184. package/lib/typescript/module/updateError.d.ts.map +1 -0
  185. package/lib/typescript/module/wrap.d.ts +2 -3
  186. package/lib/typescript/module/wrap.d.ts.map +1 -1
  187. package/lib/typescript/module/wrap.types.d.ts +13 -41
  188. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  189. package/package.json +8 -4
  190. package/src/appReady.ts +147 -0
  191. package/src/checkForUpdate.ts +156 -89
  192. package/src/clientPlugin.ts +21 -0
  193. package/src/error.ts +5 -15
  194. package/src/fetchJSON.ts +14 -30
  195. package/src/fetchUpdateResponse.ts +60 -0
  196. package/src/httpClient.ts +89 -160
  197. package/src/index.ts +413 -425
  198. package/src/init.types.ts +24 -0
  199. package/src/native.ts +101 -105
  200. package/src/pluginHost.ts +52 -0
  201. package/src/releaseCatalogCache.ts +43 -172
  202. package/src/sdkVersion.ts +1 -1
  203. package/src/specs/NativeHotUpdater.ts +34 -48
  204. package/src/store.ts +2 -23
  205. package/src/types.ts +1 -1
  206. package/src/updateError.ts +167 -0
  207. package/src/wrap.tsx +60 -43
  208. package/src/wrap.types.ts +18 -46
  209. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  210. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  211. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  212. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  213. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  214. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  215. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  216. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  217. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  218. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  219. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  220. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  221. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  222. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  223. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  224. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  225. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  226. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  227. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  228. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  229. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  230. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  231. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  232. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  233. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  234. package/lib/commonjs/httpClient.spec.js +0 -102
  235. package/lib/commonjs/httpClient.spec.js.map +0 -1
  236. package/lib/commonjs/index.spec.js +0 -302
  237. package/lib/commonjs/index.spec.js.map +0 -1
  238. package/lib/commonjs/iosInterop.spec.js +0 -18
  239. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  240. package/lib/commonjs/native.spec.js +0 -883
  241. package/lib/commonjs/native.spec.js.map +0 -1
  242. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  243. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  244. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  245. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  246. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  247. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  248. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  249. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  250. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  251. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  252. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  253. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  254. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  255. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  256. package/lib/commonjs/store.notification.spec.js +0 -76
  257. package/lib/commonjs/store.notification.spec.js.map +0 -1
  258. package/lib/commonjs/store.spec.js +0 -316
  259. package/lib/commonjs/store.spec.js.map +0 -1
  260. package/lib/commonjs/wrap.spec.js +0 -329
  261. package/lib/commonjs/wrap.spec.js.map +0 -1
  262. package/lib/module/catalogCacheNative.spec.js +0 -31
  263. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  264. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  265. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  266. package/lib/module/httpClient.spec.js +0 -102
  267. package/lib/module/httpClient.spec.js.map +0 -1
  268. package/lib/module/index.spec.js +0 -301
  269. package/lib/module/index.spec.js.map +0 -1
  270. package/lib/module/iosInterop.spec.js +0 -18
  271. package/lib/module/iosInterop.spec.js.map +0 -1
  272. package/lib/module/native.spec.js +0 -882
  273. package/lib/module/native.spec.js.map +0 -1
  274. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  275. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  276. package/lib/module/notifyAppReadyInsights.js +0 -128
  277. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  278. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  279. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  280. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  281. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  282. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  283. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  284. package/lib/module/releaseCatalogCache.spec.js +0 -195
  285. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  286. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  287. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  288. package/lib/module/store.notification.spec.js +0 -77
  289. package/lib/module/store.notification.spec.js.map +0 -1
  290. package/lib/module/store.spec.js +0 -315
  291. package/lib/module/store.spec.js.map +0 -1
  292. package/lib/module/wrap.spec.js +0 -330
  293. package/lib/module/wrap.spec.js.map +0 -1
  294. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  295. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  296. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  297. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  298. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  299. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  300. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  301. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  302. package/src/catalogCacheNative.spec.ts +0 -63
  303. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  304. package/src/httpClient.spec.ts +0 -132
  305. package/src/index.spec.ts +0 -337
  306. package/src/iosInterop.spec.ts +0 -30
  307. package/src/native.spec.ts +0 -945
  308. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  309. package/src/notifyAppReadyInsights.spec.ts +0 -243
  310. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  311. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  312. package/src/notifyAppReadyInsights.ts +0 -194
  313. package/src/releaseCatalogCache.spec.ts +0 -282
  314. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  315. package/src/store.notification.spec.ts +0 -92
  316. package/src/store.spec.ts +0 -372
  317. package/src/wrap.spec.ts +0 -406
@@ -1,17 +1,24 @@
1
1
  import {
2
2
  authorizeReleaseTransition,
3
+ canonicalizeAppVersion,
3
4
  createReleaseSelectionContextHash,
4
5
  encodeChannelKey,
6
+ type ExpectedReleaseCatalogScope,
7
+ hasExpectedReleaseCatalogScope,
5
8
  selectDesiredRelease,
6
- type ArtifactInfo,
7
9
  type PersistedSelectionReceipt,
8
10
  type ReleaseCatalog,
9
- } from "@hot-updater/core";
10
- import { canonicalizeAppVersion } from "@hot-updater/plugin-core";
11
+ } from "@hot-updater/protocol";
11
12
  import { Platform } from "react-native";
12
13
 
14
+ import { getRunningReleaseId, type LaunchReporter } from "./appReady";
15
+ import type {
16
+ ReleaseTransitionKind,
17
+ UpdateErrorResource,
18
+ UpdateErrorStage,
19
+ } from "./clientPlugin";
13
20
  import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
14
- import type { HotUpdaterHttpClient, HotUpdaterHttpSession } from "./httpClient";
21
+ import type { HotUpdaterHttpClient } from "./httpClient";
15
22
  import {
16
23
  acceptReleaseCatalog,
17
24
  commitReleaseSelection,
@@ -22,26 +29,22 @@ import {
22
29
  getCohort,
23
30
  getDefaultChannel,
24
31
  getFingerprintHash,
25
- getInstallId,
26
32
  getMinBundleId,
27
33
  getCrashHistory,
28
- getPersistedUserIdentity,
29
34
  isReleaseSelectionCurrent,
30
35
  isChannelSwitched,
31
- updateBundle,
36
+ stageBundle,
32
37
  } from "./native";
33
- import {
34
- hasExpectedReleaseCatalogScope,
35
- type ExpectedReleaseCatalogScope,
36
- } from "./releaseCatalogCache";
37
38
  import { hotUpdaterStore } from "./store";
39
+ import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
40
+
41
+ export type { ReleaseTransitionKind };
38
42
 
39
43
  export interface CheckForUpdateOptions {
40
44
  /**
41
45
  * Update strategy
42
46
  * - "fingerprint": Use fingerprint hash to check for updates
43
47
  * - "appVersion": Use app version to check for updates
44
- * - Can override the strategy set in HotUpdater.wrap()
45
48
  */
46
49
  updateStrategy: "appVersion" | "fingerprint";
47
50
 
@@ -60,7 +63,7 @@ export interface CheckForUpdateOptions {
60
63
  requestTimeout?: number;
61
64
  }
62
65
 
63
- export type CheckForUpdateResult = ArtifactInfo & {
66
+ export type CheckForUpdateResult = {
64
67
  readonly id: string;
65
68
  readonly message: string | null;
66
69
  readonly rolloutCohortCount: number;
@@ -71,20 +74,15 @@ export type CheckForUpdateResult = ArtifactInfo & {
71
74
  readonly transitionKind?: ReleaseTransitionKind;
72
75
  /**
73
76
  * Updates the bundle.
74
- * This method is equivalent to `HotUpdater.updateBundle()` but with all required arguments pre-filled.
77
+ * This method is equivalent to `hotUpdater.updateBundle()` but with all required arguments pre-filled.
75
78
  */
76
79
  updateBundle: () => Promise<boolean>;
77
80
  };
78
81
 
79
- export type ReleaseTransitionKind =
80
- | "INSTALL"
81
- | "ADOPT_RELEASE"
82
- | "USE_EMBEDDED"
83
- | "USE_BUILTIN";
84
-
85
82
  export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
86
- insights?: boolean;
87
83
  client: HotUpdaterHttpClient;
84
+ /** Calls the checking instance's plugin hooks. */
85
+ emit: LaunchReporter["emit"];
88
86
  }
89
87
 
90
88
  const sameReceipt = (
@@ -113,7 +111,7 @@ const validateCatalog = (
113
111
  !/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
114
112
  !hasExpectedReleaseCatalogScope(catalog, expectedScope)
115
113
  ) {
116
- throw new HotUpdaterError("Received an invalid Release catalog");
114
+ throw new InvalidUpdateResponseError("Received an invalid Release catalog");
117
115
  }
118
116
  };
119
117
 
@@ -121,47 +119,49 @@ const resetProgress = () => {
121
119
  hotUpdaterStore.setState({
122
120
  artifactType: null,
123
121
  details: null,
124
- downloadedBytes: undefined,
125
122
  isUpdateDownloaded: false,
126
123
  progress: 0,
127
- totalBytes: undefined,
128
124
  });
129
125
  };
130
126
 
131
- const notifyUnchangedSelection = async (input: {
132
- readonly active: PersistedSelectionReceipt | null;
133
- readonly desired: PersistedSelectionReceipt;
134
- readonly appVersion: string;
135
- readonly cohort: string;
136
- readonly fingerprintHash: string | null;
137
- readonly platform: "ios" | "android";
138
- readonly session: HotUpdaterHttpSession;
139
- readonly requestHeaders?: Record<string, string>;
140
- readonly requestTimeout?: number;
141
- }): Promise<void> => {
142
- const { userId, username } = getPersistedUserIdentity();
143
- try {
144
- await input.session.sendInsightsEvent({
145
- appVersion: input.appVersion,
146
- channel: input.desired.channel,
147
- cohort: input.cohort,
148
- fingerprintHash: input.fingerprintHash,
149
- fromBundleId: null,
150
- fromReleaseId: input.active?.releaseId ?? null,
151
- installId: getInstallId(),
152
- platform: input.platform,
153
- requestHeaders: input.requestHeaders,
154
- requestTimeout: input.requestTimeout,
155
- toBundleId: input.desired.bundleId,
156
- toReleaseId: input.desired.releaseId,
157
- type: "UNCHANGED",
158
- updateStrategy: null,
159
- ...(userId === undefined ? {} : { userId }),
160
- ...(username === undefined ? {} : { username }),
161
- });
162
- } catch (error) {
163
- console.warn("[HotUpdater] Unchanged selection insights failed:", error);
164
- }
127
+ /** The running bundle, the channel checked, and the update's target. */
128
+ type UpdateErrorScope = {
129
+ readonly channel: string;
130
+ readonly bundleId: string;
131
+ readonly updateStrategy: "fingerprint" | "appVersion";
132
+ readonly targetBundleId?: string;
133
+ readonly targetReleaseId?: string | null;
134
+ };
135
+
136
+ /**
137
+ * Tells plugins why an update failed, when it failed as an update. An error
138
+ * the SDK's JavaScript raised happened in `stage`, fetching `resource`.
139
+ */
140
+ export const reportUpdateError = (
141
+ emit: LaunchReporter["emit"],
142
+ error: unknown,
143
+ stage: UpdateErrorStage,
144
+ resource: UpdateErrorResource | undefined,
145
+ scope: UpdateErrorScope,
146
+ ): void => {
147
+ emit("onUpdateError", () => {
148
+ const classification = classifyUpdateError(error, stage, resource);
149
+ if (classification === null) return null;
150
+ return {
151
+ ...classification,
152
+ channel: scope.channel,
153
+ bundleId: scope.bundleId,
154
+ releaseId: getRunningReleaseId(scope.bundleId, getChannel()),
155
+ updateStrategy: scope.updateStrategy,
156
+ ...(scope.targetBundleId === undefined
157
+ ? {}
158
+ : { targetBundleId: scope.targetBundleId }),
159
+ ...(scope.targetReleaseId == null
160
+ ? {}
161
+ : { targetReleaseId: scope.targetReleaseId }),
162
+ cause: error,
163
+ };
164
+ });
165
165
  };
166
166
 
167
167
  async function checkForReleaseCatalogUpdate(input: {
@@ -212,12 +212,15 @@ async function checkForReleaseCatalogUpdate(input: {
212
212
  requestTimeout: options.requestTimeout,
213
213
  updateStrategy: options.updateStrategy,
214
214
  });
215
+ // The server says this scope has no catalog yet: no update, not a failure.
216
+ if (catalog === null) return null;
215
217
  validateCatalog(catalog, expectedScope);
216
218
  const catalogId = catalog.catalogId;
217
219
  const scopeKey = catalog.scopeKey;
218
220
 
219
221
  const crashedBundleIds = getCrashHistory();
220
- const active = getActiveUpdateState().activeSelection;
222
+ const activeState = getActiveUpdateState();
223
+ const active = activeState.activeSelection;
221
224
  const explicitScopeSwitch =
222
225
  input.explicitChannel !== undefined &&
223
226
  input.explicitChannel !== input.currentChannel;
@@ -264,7 +267,7 @@ async function checkForReleaseCatalogUpdate(input: {
264
267
  scopeKey,
265
268
  })
266
269
  ) {
267
- throw new HotUpdaterError(
270
+ throw new InvalidUpdateResponseError(
268
271
  "Rejected a stale or inconsistent Release catalog",
269
272
  );
270
273
  }
@@ -328,7 +331,20 @@ async function checkForReleaseCatalogUpdate(input: {
328
331
  scopeKey,
329
332
  selectionContextHash,
330
333
  };
331
- const updateBundleForSelection = async (): Promise<boolean> => {
334
+ const fromReleaseId =
335
+ active?.bundleId === input.currentBundleId
336
+ ? active.releaseId
337
+ : activeState.stableSelection?.bundleId === input.currentBundleId
338
+ ? activeState.stableSelection.releaseId
339
+ : null;
340
+ const errorScope = {
341
+ bundleId: input.currentBundleId,
342
+ channel: input.targetChannel,
343
+ targetBundleId: desired.bundleId,
344
+ targetReleaseId: desired.releaseId,
345
+ updateStrategy: options.updateStrategy,
346
+ };
347
+ const installSelection = async (): Promise<boolean> => {
332
348
  resetProgress();
333
349
  if (!isReleaseSelectionCurrent(guard)) {
334
350
  throw new StaleReleaseCatalogError();
@@ -339,18 +355,14 @@ async function checkForReleaseCatalogUpdate(input: {
339
355
  guard,
340
356
  selection: receipt,
341
357
  });
342
- if (committed && transitionKind === "ADOPT_RELEASE" && options.insights) {
343
- await notifyUnchangedSelection({
344
- active,
345
- appVersion: input.currentAppVersion,
346
- cohort: input.cohort,
347
- desired: receipt,
348
- fingerprintHash: input.fingerprintHash,
349
- platform: input.platform,
350
- requestHeaders: options.requestHeaders,
351
- requestTimeout: options.requestTimeout,
352
- session,
353
- });
358
+ if (committed && transitionKind === "ADOPT_RELEASE") {
359
+ options.emit("onUpdateCheck", () => ({
360
+ status: "UNCHANGED",
361
+ channel: receipt.channel,
362
+ bundleId: receipt.bundleId,
363
+ releaseId: receipt.releaseId,
364
+ previousReleaseId: active?.releaseId ?? null,
365
+ }));
354
366
  }
355
367
  return committed;
356
368
  }
@@ -364,33 +376,70 @@ async function checkForReleaseCatalogUpdate(input: {
364
376
  if (!isReleaseSelectionCurrent(guard)) {
365
377
  throw new StaleReleaseCatalogError();
366
378
  }
367
- return updateBundle({
379
+ const delivery = await stageBundle({
380
+ assets: artifact.assets,
368
381
  bundleId: desired.bundleId,
369
- changedAssets: artifact.changedAssets ?? null,
370
382
  channel: input.targetChannel,
371
- fileHash: artifact.fileHash,
372
- fileUrl: artifact.fileUrl,
373
- manifestFileHash: artifact.manifestFileHash ?? null,
374
- manifestUrl: artifact.manifestUrl ?? null,
383
+ manifestFileHash: artifact.manifestFileHash,
384
+ manifestUrl: artifact.manifestUrl,
385
+ ...(artifact.archiveUrl ? { archiveUrl: artifact.archiveUrl } : {}),
375
386
  selection: receipt,
376
387
  shouldSkipCurrentBundleIdCheck: true,
377
388
  status: desired.status,
378
389
  });
390
+ if (delivery !== null && desired.bundleId !== input.currentBundleId) {
391
+ options.emit("onBundleDownloaded", () => ({
392
+ channel: input.targetChannel,
393
+ fromBundleId: input.currentBundleId,
394
+ fromReleaseId,
395
+ toBundleId: desired.bundleId,
396
+ toReleaseId: desired.releaseId,
397
+ updateStrategy: options.updateStrategy,
398
+ ...delivery,
399
+ }));
400
+ }
401
+ return true;
402
+ };
403
+ const updateBundleForSelection = async (): Promise<boolean> => {
404
+ try {
405
+ return await installSelection();
406
+ } catch (error) {
407
+ reportUpdateError(
408
+ options.emit,
409
+ error,
410
+ "download",
411
+ "artifact",
412
+ errorScope,
413
+ );
414
+ throw error;
415
+ }
379
416
  };
380
417
 
418
+ const shouldForceUpdate =
419
+ transitionKind === "ADOPT_RELEASE"
420
+ ? false
421
+ : desired.status === "ROLLBACK"
422
+ ? true
423
+ : (release?.shouldForceUpdate ?? false);
424
+ options.emit("onUpdateCheck", () => ({
425
+ status: "UPDATE_AVAILABLE",
426
+ channel: input.targetChannel,
427
+ fromBundleId: input.currentBundleId,
428
+ fromReleaseId,
429
+ toBundleId: desired.bundleId,
430
+ toReleaseId: desired.releaseId,
431
+ transitionKind,
432
+ updateStatus: desired.status,
433
+ shouldForceUpdate,
434
+ updateStrategy: options.updateStrategy,
435
+ }));
436
+
381
437
  return {
382
- fileHash: null,
383
- fileUrl: null,
384
438
  id: desired.releaseId ?? desired.bundleId,
385
439
  message: release?.message ?? null,
386
440
  releaseId: desired.releaseId,
387
441
  rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
388
- shouldForceUpdate:
389
- transitionKind === "ADOPT_RELEASE"
390
- ? false
391
- : desired.status === "ROLLBACK"
392
- ? true
393
- : (release?.shouldForceUpdate ?? false),
442
+ shouldForceUpdate,
394
443
  status: desired.status,
395
444
  targetCohorts: release?.targetCohorts ? [...release.targetCohorts] : [],
396
445
  transitionKind,
@@ -430,7 +479,7 @@ export async function checkForUpdate(
430
479
 
431
480
  if (isSwitched && explicitChannel && explicitChannel !== currentChannel) {
432
481
  const error = new HotUpdaterError(
433
- `Runtime channel is already switched to "${currentChannel}". Call HotUpdater.resetChannel() before checking "${explicitChannel}".`,
482
+ `Runtime channel is already switched to "${currentChannel}". Call hotUpdater.resetChannel() before checking "${explicitChannel}".`,
434
483
  );
435
484
  options.onError?.(error);
436
485
  throw error;
@@ -439,7 +488,7 @@ export async function checkForUpdate(
439
488
  const fingerprintHash = getFingerprintHash();
440
489
 
441
490
  try {
442
- return await checkForReleaseCatalogUpdate({
491
+ const result = await checkForReleaseCatalogUpdate({
443
492
  cohort,
444
493
  currentAppVersion,
445
494
  currentBundleId,
@@ -453,7 +502,25 @@ export async function checkForUpdate(
453
502
  platform,
454
503
  targetChannel,
455
504
  });
505
+ if (result === null) {
506
+ options.emit("onUpdateCheck", () => {
507
+ const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
508
+ return {
509
+ status: "UNCHANGED",
510
+ channel: currentChannel,
511
+ bundleId: currentBundleId,
512
+ releaseId,
513
+ previousReleaseId: releaseId,
514
+ };
515
+ });
516
+ }
517
+ return result;
456
518
  } catch (error) {
519
+ reportUpdateError(options.emit, error, "check", "catalog", {
520
+ bundleId: currentBundleId,
521
+ channel: targetChannel,
522
+ updateStrategy: options.updateStrategy,
523
+ });
457
524
  options.onError?.(error as Error);
458
525
  return null;
459
526
  }
@@ -0,0 +1,21 @@
1
+ export {
2
+ defineClientPlugin,
3
+ type AppReadyResult,
4
+ type BundleDownloadedInfo,
5
+ type ClientPluginApi,
6
+ type ClientPluginApis,
7
+ type HotUpdaterClientContext,
8
+ type HotUpdaterClientHooks,
9
+ type HotUpdaterClientPlugin,
10
+ type HotUpdaterClientSetup,
11
+ type HotUpdaterClientStorage,
12
+ type ReleaseTransitionKind,
13
+ type UpdateCheckResult,
14
+ type UpdateError,
15
+ type UpdateErrorReason,
16
+ type UpdateErrorResource,
17
+ type UpdateErrorStage,
18
+ type UpdateErrorTransport,
19
+ type UpdateHttpResponse,
20
+ type UpdateStrategy,
21
+ } from "@hot-updater/protocol";
package/src/error.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Error Classification:
9
9
  * - Parameter Validation: Invalid or missing function parameters
10
- * - Bundle Storage: Errors during download, extraction, and storage
10
+ * - Bundle Storage: Errors during download, verification, and storage
11
11
  * - Signature Verification: Cryptographic verification failures (collapsed to a single public code)
12
12
  * - Internal: Platform-specific or unexpected errors
13
13
  *
@@ -27,8 +27,7 @@ export enum HotUpdaterErrorCode {
27
27
  MISSING_BUNDLE_ID = "MISSING_BUNDLE_ID",
28
28
 
29
29
  /**
30
- * File URL is invalid or malformed.
31
- * Thrown when fileUrl parameter cannot be parsed as a valid URL.
30
+ * Manifest or asset URL is invalid or malformed.
32
31
  * @retryable false
33
32
  */
34
33
  INVALID_FILE_URL = "INVALID_FILE_URL",
@@ -58,25 +57,16 @@ export enum HotUpdaterErrorCode {
58
57
  */
59
58
  INCOMPLETE_DOWNLOAD = "INCOMPLETE_DOWNLOAD",
60
59
 
61
- /**
62
- * Bundle archive format is invalid or corrupted.
63
- * Thrown when ZIP file has wrong magic bytes, invalid structure, or unsupported format.
64
- * Also thrown for path traversal attempts during extraction.
65
- * @retryable false - Indicates corrupted or malicious bundle
66
- */
67
- EXTRACTION_FORMAT_ERROR = "EXTRACTION_FORMAT_ERROR",
68
-
69
60
  /**
70
61
  * Bundle missing required platform files.
71
- * Thrown when extracted bundle doesn't contain index.android.bundle (Android)
62
+ * Thrown when the staged bundle doesn't contain index.android.bundle (Android)
72
63
  * or main.jsbundle (iOS).
73
64
  * @retryable false - Indicates incorrectly built bundle
74
65
  */
75
66
  INVALID_BUNDLE = "INVALID_BUNDLE",
76
67
 
77
68
  /**
78
- * Insufficient disk space for bundle download and extraction.
79
- * Thrown when available disk space is less than required (file size * 2).
69
+ * Insufficient disk space for bundle staging.
80
70
  * Error message includes required and available bytes.
81
71
  * @retryable false - User must free up disk space
82
72
  */
@@ -102,7 +92,7 @@ export enum HotUpdaterErrorCode {
102
92
  /**
103
93
  * Bundle is in crashed history and cannot be applied.
104
94
  * Thrown when attempting to install a bundle that previously caused a crash.
105
- * Use HotUpdater.clearCrashHistory() to allow retrying this bundle.
95
+ * Use hotUpdater.clearCrashHistory() to allow retrying this bundle.
106
96
  * @retryable false - Bundle was marked as crashed for safety
107
97
  */
108
98
  BUNDLE_IN_CRASHED_HISTORY = "BUNDLE_IN_CRASHED_HISTORY",
package/src/fetchJSON.ts CHANGED
@@ -1,33 +1,17 @@
1
- export const fetchJSON = async <T>({
2
- url,
3
- requestHeaders,
4
- requestTimeout = 5000,
5
- }: {
6
- url: string;
7
- requestHeaders?: Record<string, string>;
8
- requestTimeout?: number;
9
- }): Promise<T> => {
10
- const controller = new AbortController();
11
- const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
1
+ import { fetchUpdateResponse, type UpdateRequest } from "./fetchUpdateResponse";
2
+ import { UpdateHttpError } from "./updateError";
12
3
 
13
- try {
14
- const response = await fetch(url, {
15
- headers: {
16
- Accept: "application/json",
17
- ...requestHeaders,
18
- },
19
- signal: controller.signal,
20
- });
21
- if (response.status !== 200) {
22
- throw new Error(response.statusText);
23
- }
24
- return (await response.json()) as T;
25
- } catch (error: unknown) {
26
- if (error instanceof Error && error.name === "AbortError") {
27
- throw new Error("Request timed out");
28
- }
29
- throw error;
30
- } finally {
31
- clearTimeout(timeoutId);
4
+ export class FetchJSONResponseError extends UpdateHttpError {}
5
+
6
+ export const fetchJSON = async <T>(
7
+ input: Omit<UpdateRequest, "resource">,
8
+ ): Promise<T> => {
9
+ const { response, body } = await fetchUpdateResponse({
10
+ ...input,
11
+ resource: "artifact",
12
+ });
13
+ if (response.status !== 200) {
14
+ throw new FetchJSONResponseError(response.status, response.statusText);
32
15
  }
16
+ return JSON.parse(body!) as T;
33
17
  };
@@ -0,0 +1,60 @@
1
+ import type { UpdateHttpResponse } from "@hot-updater/protocol";
2
+
3
+ export interface UpdateRequest {
4
+ readonly url: string;
5
+ readonly resource: UpdateHttpResponse["resource"];
6
+ readonly requestHeaders?: Record<string, string>;
7
+ readonly requestTimeout?: number;
8
+ readonly onResponse?: (response: UpdateHttpResponse) => void;
9
+ }
10
+
11
+ /** Read once for parsing and diagnostics, under the same request timeout. */
12
+ export const fetchUpdateResponse = async ({
13
+ url,
14
+ resource,
15
+ requestHeaders,
16
+ requestTimeout = 5000,
17
+ onResponse,
18
+ }: UpdateRequest): Promise<{ response: Response; body: string | null }> => {
19
+ const controller = new AbortController();
20
+ let timedOut = false;
21
+ const timeoutId = setTimeout(() => {
22
+ timedOut = true;
23
+ controller.abort();
24
+ }, requestTimeout);
25
+ try {
26
+ const headers = new Headers(requestHeaders);
27
+ if (!headers.has("Accept")) headers.set("Accept", "application/json");
28
+ const response = await fetch(url, {
29
+ headers,
30
+ signal: controller.signal,
31
+ });
32
+ let body: string | null = null;
33
+ try {
34
+ body = await response.text();
35
+ } catch (error) {
36
+ // A broken error body must not hide an HTTP error already received.
37
+ if (response.status === 200) throw error;
38
+ } finally {
39
+ onResponse?.({
40
+ resource,
41
+ // React Native URL implementations do not all expose pathname.
42
+ path:
43
+ url.replace(/^https?:\/\/[^/?#]*/i, "").split(/[?#]/, 1)[0] || "/",
44
+ status: response.status,
45
+ body,
46
+ bodyTruncated: false,
47
+ });
48
+ }
49
+ return { response, body };
50
+ } catch (error: unknown) {
51
+ // Whatever the fetch rejects with once its timeout aborts it: an
52
+ // AbortError, or Expo's "fetch failed: FetchRequestCanceledException".
53
+ if (timedOut || (error instanceof Error && error.name === "AbortError")) {
54
+ throw new Error("Request timed out");
55
+ }
56
+ throw error;
57
+ } finally {
58
+ clearTimeout(timeoutId);
59
+ }
60
+ };