@hot-updater/react-native 1.0.0-rc.2 → 1.0.0-rc.22

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 (291) hide show
  1. package/android/proguard-rules.pro +17 -2
  2. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  3. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +954 -837
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +15 -11
  5. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  6. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  7. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +43 -31
  8. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
  9. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  10. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  11. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  12. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  13. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  14. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  15. package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
  16. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  17. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  18. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  19. package/android/src/newarch/HotUpdater.kt +7 -9
  20. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  21. package/android/src/oldarch/HotUpdater.kt +7 -9
  22. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  23. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  24. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  25. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  26. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +950 -1112
  27. package/ios/HotUpdater/Internal/BundleMetadata.swift +8 -53
  28. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  29. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  30. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  31. package/ios/HotUpdater/Internal/HotUpdater.mm +13 -45
  32. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +190 -73
  33. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  34. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  35. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  36. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  37. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  38. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  39. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  40. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  41. package/lib/commonjs/appReady.js +111 -0
  42. package/lib/commonjs/appReady.js.map +1 -0
  43. package/lib/commonjs/checkForUpdate.js +125 -70
  44. package/lib/commonjs/checkForUpdate.js.map +1 -1
  45. package/lib/commonjs/clientPlugin.js +13 -0
  46. package/lib/commonjs/clientPlugin.js.map +1 -0
  47. package/lib/commonjs/error.js +4 -13
  48. package/lib/commonjs/error.js.map +1 -1
  49. package/lib/commonjs/fetchJSON.js +5 -2
  50. package/lib/commonjs/fetchJSON.js.map +1 -1
  51. package/lib/commonjs/httpClient.js +55 -92
  52. package/lib/commonjs/httpClient.js.map +1 -1
  53. package/lib/commonjs/index.js +68 -20
  54. package/lib/commonjs/index.js.map +1 -1
  55. package/lib/commonjs/native.js +82 -62
  56. package/lib/commonjs/native.js.map +1 -1
  57. package/lib/commonjs/pluginHost.js +44 -0
  58. package/lib/commonjs/pluginHost.js.map +1 -0
  59. package/lib/commonjs/releaseCatalogCache.js +20 -61
  60. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  61. package/lib/commonjs/sdkVersion.js +1 -1
  62. package/lib/commonjs/sdkVersion.js.map +1 -1
  63. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  64. package/lib/commonjs/store.js +3 -10
  65. package/lib/commonjs/store.js.map +1 -1
  66. package/lib/commonjs/updateError.js +132 -0
  67. package/lib/commonjs/updateError.js.map +1 -0
  68. package/lib/commonjs/wrap.js +44 -27
  69. package/lib/commonjs/wrap.js.map +1 -1
  70. package/lib/module/appReady.js +103 -0
  71. package/lib/module/appReady.js.map +1 -0
  72. package/lib/module/checkForUpdate.js +118 -65
  73. package/lib/module/checkForUpdate.js.map +1 -1
  74. package/lib/module/clientPlugin.js +4 -0
  75. package/lib/module/clientPlugin.js.map +1 -0
  76. package/lib/module/error.js +4 -13
  77. package/lib/module/error.js.map +1 -1
  78. package/lib/module/fetchJSON.js +3 -1
  79. package/lib/module/fetchJSON.js.map +1 -1
  80. package/lib/module/httpClient.js +53 -90
  81. package/lib/module/httpClient.js.map +1 -1
  82. package/lib/module/index.js +55 -22
  83. package/lib/module/index.js.map +1 -1
  84. package/lib/module/native.js +74 -54
  85. package/lib/module/native.js.map +1 -1
  86. package/lib/module/pluginHost.js +35 -0
  87. package/lib/module/pluginHost.js.map +1 -0
  88. package/lib/module/releaseCatalogCache.js +16 -56
  89. package/lib/module/releaseCatalogCache.js.map +1 -1
  90. package/lib/module/sdkVersion.js +1 -1
  91. package/lib/module/sdkVersion.js.map +1 -1
  92. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  93. package/lib/module/store.js +3 -10
  94. package/lib/module/store.js.map +1 -1
  95. package/lib/module/updateError.js +126 -0
  96. package/lib/module/updateError.js.map +1 -0
  97. package/lib/module/wrap.js +45 -28
  98. package/lib/module/wrap.js.map +1 -1
  99. package/lib/typescript/commonjs/appReady.d.ts +23 -0
  100. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
  102. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  104. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/error.d.ts +4 -13
  106. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/fetchJSON.d.ts +3 -0
  108. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/httpClient.d.ts +3 -32
  110. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/index.d.ts +7 -12
  112. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/native.d.ts +33 -23
  114. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
  116. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +6 -14
  118. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  120. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +42 -47
  122. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/store.d.ts +1 -3
  124. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/types.d.ts +1 -1
  126. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  128. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
  131. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  132. package/lib/typescript/module/appReady.d.ts +23 -0
  133. package/lib/typescript/module/appReady.d.ts.map +1 -0
  134. package/lib/typescript/module/checkForUpdate.d.ts +16 -4
  135. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  136. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  137. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  138. package/lib/typescript/module/error.d.ts +4 -13
  139. package/lib/typescript/module/error.d.ts.map +1 -1
  140. package/lib/typescript/module/fetchJSON.d.ts +3 -0
  141. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  142. package/lib/typescript/module/httpClient.d.ts +3 -32
  143. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  144. package/lib/typescript/module/index.d.ts +7 -12
  145. package/lib/typescript/module/index.d.ts.map +1 -1
  146. package/lib/typescript/module/native.d.ts +33 -23
  147. package/lib/typescript/module/native.d.ts.map +1 -1
  148. package/lib/typescript/module/pluginHost.d.ts +4 -0
  149. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  150. package/lib/typescript/module/releaseCatalogCache.d.ts +6 -14
  151. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  152. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  153. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  154. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +42 -47
  155. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  156. package/lib/typescript/module/store.d.ts +1 -3
  157. package/lib/typescript/module/store.d.ts.map +1 -1
  158. package/lib/typescript/module/types.d.ts +1 -1
  159. package/lib/typescript/module/types.d.ts.map +1 -1
  160. package/lib/typescript/module/updateError.d.ts +26 -0
  161. package/lib/typescript/module/updateError.d.ts.map +1 -0
  162. package/lib/typescript/module/wrap.d.ts.map +1 -1
  163. package/lib/typescript/module/wrap.types.d.ts +6 -7
  164. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  165. package/package.json +7 -4
  166. package/src/appReady.ts +149 -0
  167. package/src/checkForUpdate.ts +152 -89
  168. package/src/clientPlugin.ts +17 -0
  169. package/src/error.ts +4 -14
  170. package/src/fetchJSON.ts +5 -1
  171. package/src/httpClient.ts +78 -157
  172. package/src/index.ts +87 -29
  173. package/src/native.ts +109 -104
  174. package/src/pluginHost.ts +45 -0
  175. package/src/releaseCatalogCache.ts +26 -140
  176. package/src/sdkVersion.ts +1 -1
  177. package/src/specs/NativeHotUpdater.ts +41 -48
  178. package/src/store.ts +2 -23
  179. package/src/types.ts +1 -1
  180. package/src/updateError.ts +153 -0
  181. package/src/wrap.tsx +59 -28
  182. package/src/wrap.types.ts +6 -7
  183. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  184. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  185. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  186. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  187. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  188. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  189. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  190. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  191. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  192. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  193. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  194. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  195. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  196. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  197. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  198. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  199. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  200. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  201. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  202. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  203. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  204. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  205. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  206. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -604
  207. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  208. package/lib/commonjs/httpClient.spec.js +0 -102
  209. package/lib/commonjs/httpClient.spec.js.map +0 -1
  210. package/lib/commonjs/index.spec.js +0 -298
  211. package/lib/commonjs/index.spec.js.map +0 -1
  212. package/lib/commonjs/iosInterop.spec.js +0 -18
  213. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  214. package/lib/commonjs/native.spec.js +0 -795
  215. package/lib/commonjs/native.spec.js.map +0 -1
  216. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  217. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  218. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  219. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  220. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  221. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  222. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  223. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  224. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  225. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  226. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  227. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  228. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  229. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  230. package/lib/commonjs/store.notification.spec.js +0 -76
  231. package/lib/commonjs/store.notification.spec.js.map +0 -1
  232. package/lib/commonjs/store.spec.js +0 -316
  233. package/lib/commonjs/store.spec.js.map +0 -1
  234. package/lib/commonjs/wrap.spec.js +0 -296
  235. package/lib/commonjs/wrap.spec.js.map +0 -1
  236. package/lib/module/catalogCacheNative.spec.js +0 -31
  237. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  238. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -603
  239. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  240. package/lib/module/httpClient.spec.js +0 -102
  241. package/lib/module/httpClient.spec.js.map +0 -1
  242. package/lib/module/index.spec.js +0 -297
  243. package/lib/module/index.spec.js.map +0 -1
  244. package/lib/module/iosInterop.spec.js +0 -18
  245. package/lib/module/iosInterop.spec.js.map +0 -1
  246. package/lib/module/native.spec.js +0 -794
  247. package/lib/module/native.spec.js.map +0 -1
  248. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  249. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  250. package/lib/module/notifyAppReadyInsights.js +0 -128
  251. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  252. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  253. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  254. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  255. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  256. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  257. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  258. package/lib/module/releaseCatalogCache.spec.js +0 -195
  259. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  260. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  261. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  262. package/lib/module/store.notification.spec.js +0 -77
  263. package/lib/module/store.notification.spec.js.map +0 -1
  264. package/lib/module/store.spec.js +0 -315
  265. package/lib/module/store.spec.js.map +0 -1
  266. package/lib/module/wrap.spec.js +0 -295
  267. package/lib/module/wrap.spec.js.map +0 -1
  268. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  269. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  270. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  271. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  272. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  273. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  274. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  275. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  276. package/src/catalogCacheNative.spec.ts +0 -63
  277. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -596
  278. package/src/httpClient.spec.ts +0 -132
  279. package/src/index.spec.ts +0 -331
  280. package/src/iosInterop.spec.ts +0 -30
  281. package/src/native.spec.ts +0 -855
  282. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  283. package/src/notifyAppReadyInsights.spec.ts +0 -243
  284. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  285. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  286. package/src/notifyAppReadyInsights.ts +0 -194
  287. package/src/releaseCatalogCache.spec.ts +0 -282
  288. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  289. package/src/store.notification.spec.ts +0 -92
  290. package/src/store.spec.ts +0 -372
  291. package/src/wrap.spec.ts +0 -367
@@ -1,17 +1,28 @@
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 {
15
+ emitAfterAppReady,
16
+ getPreviousProcessExit,
17
+ getRunningReleaseId,
18
+ } from "./appReady";
19
+ import type {
20
+ ReleaseTransitionKind,
21
+ UpdateErrorResource,
22
+ UpdateErrorStage,
23
+ } from "./clientPlugin";
13
24
  import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
14
- import type { HotUpdaterHttpClient, HotUpdaterHttpSession } from "./httpClient";
25
+ import type { HotUpdaterHttpClient } from "./httpClient";
15
26
  import {
16
27
  acceptReleaseCatalog,
17
28
  commitReleaseSelection,
@@ -22,19 +33,16 @@ import {
22
33
  getCohort,
23
34
  getDefaultChannel,
24
35
  getFingerprintHash,
25
- getInstallId,
26
36
  getMinBundleId,
27
37
  getCrashHistory,
28
- getPersistedUserIdentity,
29
38
  isReleaseSelectionCurrent,
30
39
  isChannelSwitched,
31
- updateBundle,
40
+ stageBundle,
32
41
  } from "./native";
33
- import {
34
- hasExpectedReleaseCatalogScope,
35
- type ExpectedReleaseCatalogScope,
36
- } from "./releaseCatalogCache";
37
42
  import { hotUpdaterStore } from "./store";
43
+ import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
44
+
45
+ export type { ReleaseTransitionKind };
38
46
 
39
47
  export interface CheckForUpdateOptions {
40
48
  /**
@@ -60,7 +68,7 @@ export interface CheckForUpdateOptions {
60
68
  requestTimeout?: number;
61
69
  }
62
70
 
63
- export type CheckForUpdateResult = ArtifactInfo & {
71
+ export type CheckForUpdateResult = {
64
72
  readonly id: string;
65
73
  readonly message: string | null;
66
74
  readonly rolloutCohortCount: number;
@@ -76,14 +84,7 @@ export type CheckForUpdateResult = ArtifactInfo & {
76
84
  updateBundle: () => Promise<boolean>;
77
85
  };
78
86
 
79
- export type ReleaseTransitionKind =
80
- | "INSTALL"
81
- | "ADOPT_RELEASE"
82
- | "USE_EMBEDDED"
83
- | "USE_BUILTIN";
84
-
85
87
  export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
86
- insights?: boolean;
87
88
  client: HotUpdaterHttpClient;
88
89
  }
89
90
 
@@ -113,7 +114,7 @@ const validateCatalog = (
113
114
  !/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
114
115
  !hasExpectedReleaseCatalogScope(catalog, expectedScope)
115
116
  ) {
116
- throw new HotUpdaterError("Received an invalid Release catalog");
117
+ throw new InvalidUpdateResponseError("Received an invalid Release catalog");
117
118
  }
118
119
  };
119
120
 
@@ -121,48 +122,50 @@ const resetProgress = () => {
121
122
  hotUpdaterStore.setState({
122
123
  artifactType: null,
123
124
  details: null,
124
- downloadedBytes: undefined,
125
125
  isUpdateDownloaded: false,
126
126
  progress: 0,
127
- totalBytes: undefined,
128
127
  });
129
128
  };
130
129
 
131
- const notifyReleaseAdoption = 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
- readonly updateStrategy: "appVersion" | "fingerprint";
142
- }): Promise<void> => {
143
- const { userId, username } = getPersistedUserIdentity();
144
- try {
145
- await input.session.sendInsightsEvent({
146
- appVersion: input.appVersion,
147
- channel: input.desired.channel,
148
- cohort: input.cohort,
149
- fingerprintHash: input.fingerprintHash,
150
- fromBundleId: input.desired.bundleId,
151
- fromReleaseId: input.active?.releaseId ?? null,
152
- installId: getInstallId(),
153
- platform: input.platform,
154
- requestHeaders: input.requestHeaders,
155
- requestTimeout: input.requestTimeout,
156
- toBundleId: input.desired.bundleId,
157
- toReleaseId: input.desired.releaseId,
158
- type: "RELEASE_ADOPTED",
159
- updateStrategy: input.updateStrategy,
160
- ...(userId === undefined ? {} : { userId }),
161
- ...(username === undefined ? {} : { username }),
162
- });
163
- } catch (error) {
164
- console.warn("[HotUpdater] Release adoption insights failed:", error);
165
- }
130
+ /** The running bundle, the channel checked, and the update's target. */
131
+ type UpdateErrorScope = {
132
+ readonly channel: string;
133
+ readonly bundleId: string;
134
+ readonly updateStrategy: "fingerprint" | "appVersion";
135
+ readonly targetBundleId?: string;
136
+ readonly targetReleaseId?: string | null;
137
+ };
138
+
139
+ /**
140
+ * Tells plugins why an update failed, when it failed as an update. An error
141
+ * the SDK's JavaScript raised happened in `stage`, fetching `resource`.
142
+ */
143
+ export const reportUpdateError = (
144
+ error: unknown,
145
+ stage: UpdateErrorStage,
146
+ resource: UpdateErrorResource | undefined,
147
+ scope: UpdateErrorScope,
148
+ ): void => {
149
+ emitAfterAppReady("onUpdateError", () => {
150
+ const classification = classifyUpdateError(error, stage, resource);
151
+ if (classification === null) return null;
152
+ const previousProcessExit = getPreviousProcessExit();
153
+ return {
154
+ ...classification,
155
+ ...(previousProcessExit === null ? {} : { previousProcessExit }),
156
+ channel: scope.channel,
157
+ bundleId: scope.bundleId,
158
+ releaseId: getRunningReleaseId(scope.bundleId, getChannel()),
159
+ updateStrategy: scope.updateStrategy,
160
+ ...(scope.targetBundleId === undefined
161
+ ? {}
162
+ : { targetBundleId: scope.targetBundleId }),
163
+ ...(scope.targetReleaseId == null
164
+ ? {}
165
+ : { targetReleaseId: scope.targetReleaseId }),
166
+ cause: error,
167
+ };
168
+ });
166
169
  };
167
170
 
168
171
  async function checkForReleaseCatalogUpdate(input: {
@@ -213,12 +216,15 @@ async function checkForReleaseCatalogUpdate(input: {
213
216
  requestTimeout: options.requestTimeout,
214
217
  updateStrategy: options.updateStrategy,
215
218
  });
219
+ // The server says this scope has no catalog yet: no update, not a failure.
220
+ if (catalog === null) return null;
216
221
  validateCatalog(catalog, expectedScope);
217
222
  const catalogId = catalog.catalogId;
218
223
  const scopeKey = catalog.scopeKey;
219
224
 
220
225
  const crashedBundleIds = getCrashHistory();
221
- const active = getActiveUpdateState().activeSelection;
226
+ const activeState = getActiveUpdateState();
227
+ const active = activeState.activeSelection;
222
228
  const explicitScopeSwitch =
223
229
  input.explicitChannel !== undefined &&
224
230
  input.explicitChannel !== input.currentChannel;
@@ -265,7 +271,7 @@ async function checkForReleaseCatalogUpdate(input: {
265
271
  scopeKey,
266
272
  })
267
273
  ) {
268
- throw new HotUpdaterError(
274
+ throw new InvalidUpdateResponseError(
269
275
  "Rejected a stale or inconsistent Release catalog",
270
276
  );
271
277
  }
@@ -329,7 +335,20 @@ async function checkForReleaseCatalogUpdate(input: {
329
335
  scopeKey,
330
336
  selectionContextHash,
331
337
  };
332
- const updateBundleForSelection = async (): Promise<boolean> => {
338
+ const fromReleaseId =
339
+ active?.bundleId === input.currentBundleId
340
+ ? active.releaseId
341
+ : activeState.stableSelection?.bundleId === input.currentBundleId
342
+ ? activeState.stableSelection.releaseId
343
+ : null;
344
+ const errorScope = {
345
+ bundleId: input.currentBundleId,
346
+ channel: input.targetChannel,
347
+ targetBundleId: desired.bundleId,
348
+ targetReleaseId: desired.releaseId,
349
+ updateStrategy: options.updateStrategy,
350
+ };
351
+ const installSelection = async (): Promise<boolean> => {
333
352
  resetProgress();
334
353
  if (!isReleaseSelectionCurrent(guard)) {
335
354
  throw new StaleReleaseCatalogError();
@@ -340,19 +359,14 @@ async function checkForReleaseCatalogUpdate(input: {
340
359
  guard,
341
360
  selection: receipt,
342
361
  });
343
- if (committed && transitionKind === "ADOPT_RELEASE" && options.insights) {
344
- await notifyReleaseAdoption({
345
- active,
346
- appVersion: input.currentAppVersion,
347
- cohort: input.cohort,
348
- desired: receipt,
349
- fingerprintHash: input.fingerprintHash,
350
- platform: input.platform,
351
- requestHeaders: options.requestHeaders,
352
- requestTimeout: options.requestTimeout,
353
- session,
354
- updateStrategy: options.updateStrategy,
355
- });
362
+ if (committed && transitionKind === "ADOPT_RELEASE") {
363
+ emitAfterAppReady("onUpdateCheck", () => ({
364
+ status: "UNCHANGED",
365
+ channel: receipt.channel,
366
+ bundleId: receipt.bundleId,
367
+ releaseId: receipt.releaseId,
368
+ previousReleaseId: active?.releaseId ?? null,
369
+ }));
356
370
  }
357
371
  return committed;
358
372
  }
@@ -366,33 +380,64 @@ async function checkForReleaseCatalogUpdate(input: {
366
380
  if (!isReleaseSelectionCurrent(guard)) {
367
381
  throw new StaleReleaseCatalogError();
368
382
  }
369
- return updateBundle({
383
+ const delivery = await stageBundle({
384
+ assets: artifact.assets,
370
385
  bundleId: desired.bundleId,
371
- changedAssets: artifact.changedAssets ?? null,
372
386
  channel: input.targetChannel,
373
- fileHash: artifact.fileHash,
374
- fileUrl: artifact.fileUrl,
375
- manifestFileHash: artifact.manifestFileHash ?? null,
376
- manifestUrl: artifact.manifestUrl ?? null,
387
+ manifestFileHash: artifact.manifestFileHash,
388
+ manifestUrl: artifact.manifestUrl,
389
+ ...(artifact.archiveUrl ? { archiveUrl: artifact.archiveUrl } : {}),
377
390
  selection: receipt,
378
391
  shouldSkipCurrentBundleIdCheck: true,
379
392
  status: desired.status,
380
393
  });
394
+ if (delivery !== null && desired.bundleId !== input.currentBundleId) {
395
+ emitAfterAppReady("onBundleDownloaded", () => ({
396
+ channel: input.targetChannel,
397
+ fromBundleId: input.currentBundleId,
398
+ fromReleaseId,
399
+ toBundleId: desired.bundleId,
400
+ toReleaseId: desired.releaseId,
401
+ updateStrategy: options.updateStrategy,
402
+ ...delivery,
403
+ }));
404
+ }
405
+ return true;
406
+ };
407
+ const updateBundleForSelection = async (): Promise<boolean> => {
408
+ try {
409
+ return await installSelection();
410
+ } catch (error) {
411
+ reportUpdateError(error, "download", "artifact", errorScope);
412
+ throw error;
413
+ }
381
414
  };
382
415
 
416
+ const shouldForceUpdate =
417
+ transitionKind === "ADOPT_RELEASE"
418
+ ? false
419
+ : desired.status === "ROLLBACK"
420
+ ? true
421
+ : (release?.shouldForceUpdate ?? false);
422
+ emitAfterAppReady("onUpdateCheck", () => ({
423
+ status: "UPDATE_AVAILABLE",
424
+ channel: input.targetChannel,
425
+ fromBundleId: input.currentBundleId,
426
+ fromReleaseId,
427
+ toBundleId: desired.bundleId,
428
+ toReleaseId: desired.releaseId,
429
+ transitionKind,
430
+ updateStatus: desired.status,
431
+ shouldForceUpdate,
432
+ updateStrategy: options.updateStrategy,
433
+ }));
434
+
383
435
  return {
384
- fileHash: null,
385
- fileUrl: null,
386
- id: desired.bundleId,
436
+ id: desired.releaseId ?? desired.bundleId,
387
437
  message: release?.message ?? null,
388
438
  releaseId: desired.releaseId,
389
439
  rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
390
- shouldForceUpdate:
391
- transitionKind === "ADOPT_RELEASE"
392
- ? false
393
- : desired.status === "ROLLBACK"
394
- ? true
395
- : (release?.shouldForceUpdate ?? false),
440
+ shouldForceUpdate,
396
441
  status: desired.status,
397
442
  targetCohorts: release?.targetCohorts ? [...release.targetCohorts] : [],
398
443
  transitionKind,
@@ -441,7 +486,7 @@ export async function checkForUpdate(
441
486
  const fingerprintHash = getFingerprintHash();
442
487
 
443
488
  try {
444
- return await checkForReleaseCatalogUpdate({
489
+ const result = await checkForReleaseCatalogUpdate({
445
490
  cohort,
446
491
  currentAppVersion,
447
492
  currentBundleId,
@@ -455,7 +500,25 @@ export async function checkForUpdate(
455
500
  platform,
456
501
  targetChannel,
457
502
  });
503
+ if (result === null) {
504
+ emitAfterAppReady("onUpdateCheck", () => {
505
+ const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
506
+ return {
507
+ status: "UNCHANGED",
508
+ channel: currentChannel,
509
+ bundleId: currentBundleId,
510
+ releaseId,
511
+ previousReleaseId: releaseId,
512
+ };
513
+ });
514
+ }
515
+ return result;
458
516
  } catch (error) {
517
+ reportUpdateError(error, "check", "catalog", {
518
+ bundleId: currentBundleId,
519
+ channel: targetChannel,
520
+ updateStrategy: options.updateStrategy,
521
+ });
459
522
  options.onError?.(error as Error);
460
523
  return null;
461
524
  }
@@ -0,0 +1,17 @@
1
+ export {
2
+ defineClientPlugin,
3
+ type AppReadyResult,
4
+ type BundleDownloadedInfo,
5
+ type HotUpdaterClientContext,
6
+ type HotUpdaterClientHooks,
7
+ type HotUpdaterClientPlugin,
8
+ type HotUpdaterClientStorage,
9
+ type ReleaseTransitionKind,
10
+ type UpdateCheckResult,
11
+ type UpdateError,
12
+ type UpdateErrorReason,
13
+ type UpdateErrorResource,
14
+ type UpdateErrorStage,
15
+ type UpdateErrorTransport,
16
+ type UpdateStrategy,
17
+ } 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
  */
package/src/fetchJSON.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import { UpdateHttpError } from "./updateError";
2
+
3
+ export class FetchJSONResponseError extends UpdateHttpError {}
4
+
1
5
  export const fetchJSON = async <T>({
2
6
  url,
3
7
  requestHeaders,
@@ -19,7 +23,7 @@ export const fetchJSON = async <T>({
19
23
  signal: controller.signal,
20
24
  });
21
25
  if (response.status !== 200) {
22
- throw new Error(response.statusText);
26
+ throw new FetchJSONResponseError(response.status, response.statusText);
23
27
  }
24
28
  return (await response.json()) as T;
25
29
  } catch (error: unknown) {