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

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
package/src/httpClient.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import {
2
+ ARTIFACT_PROTOCOL_VERSION,
3
+ canonicalizeAppVersion,
2
4
  encodeChannelKey,
5
+ resolveBaseURL,
3
6
  type ArtifactInfo,
7
+ type HotUpdaterBaseURL,
4
8
  type ReleaseCatalog,
5
- } from "@hot-updater/core";
6
- import { canonicalizeAppVersion } from "@hot-updater/plugin-core";
9
+ } from "@hot-updater/protocol";
7
10
 
8
- import { fetchJSON } from "./fetchJSON";
11
+ import { fetchJSON, FetchJSONResponseError } from "./fetchJSON";
9
12
  import { fetchReleaseCatalogWithCache } from "./releaseCatalogCache";
10
- import { HOT_UPDATER_SDK_VERSION } from "./sdkVersion";
11
- import type { HotUpdaterBaseURL } from "./types";
13
+ import { InvalidUpdateResponseError } from "./updateError";
12
14
 
13
15
  export interface ReleaseCatalogRequest {
14
16
  readonly platform: "ios" | "android";
@@ -27,69 +29,31 @@ export interface ArtifactRequest {
27
29
  readonly requestTimeout?: number;
28
30
  }
29
31
 
30
- interface InsightsEventCommonParams {
31
- readonly installId: string;
32
- readonly userId?: string;
33
- readonly username?: string;
34
- readonly platform: "ios" | "android";
35
- readonly appVersion: string;
36
- readonly channel: string;
37
- readonly cohort: string;
38
- readonly fingerprintHash: string | null;
39
- readonly fromReleaseId?: string | null;
40
- readonly toReleaseId?: string | null;
41
- readonly requestHeaders?: Record<string, string>;
42
- readonly requestTimeout?: number;
43
- }
44
-
45
- type InsightsTransitionEventParams = InsightsEventCommonParams & {
46
- readonly type: "UPDATE_APPLIED" | "RECOVERED" | "RELEASE_ADOPTED";
47
- readonly fromBundleId: string;
48
- readonly toBundleId: string;
49
- readonly updateStrategy: "fingerprint" | "appVersion";
50
- };
51
-
52
- type InsightsUnchangedEventParams = InsightsEventCommonParams & {
53
- readonly type: "UNCHANGED";
54
- readonly fromBundleId: null;
55
- readonly toBundleId: string;
56
- readonly updateStrategy: null;
57
- };
58
-
59
- export type InsightsEventParams =
60
- | InsightsTransitionEventParams
61
- | InsightsUnchangedEventParams;
62
-
63
32
  export interface HotUpdaterHttpSession {
33
+ /** Resolves to null when the server says the scope has no catalog, so no update. */
64
34
  fetchReleaseCatalog: (
65
35
  params: ReleaseCatalogRequest,
66
- ) => Promise<ReleaseCatalog>;
36
+ ) => Promise<ReleaseCatalog | null>;
67
37
  resolveArtifact: (params: ArtifactRequest) => Promise<ArtifactInfo>;
68
- sendInsightsEvent: (params: InsightsEventParams) => Promise<void>;
69
38
  }
70
39
 
71
40
  export interface HotUpdaterHttpClient {
72
41
  createSession: () => Promise<HotUpdaterHttpSession>;
73
42
  }
74
43
 
75
- const resolveBaseURL = async (baseURL: HotUpdaterBaseURL): Promise<string> => {
76
- const resolvedBaseURL =
77
- typeof baseURL === "function" ? await baseURL() : baseURL;
78
-
79
- if (!resolvedBaseURL) {
80
- throw new Error("baseURL function must return a non-empty string");
81
- }
82
-
83
- return resolvedBaseURL.replace(/\/+$/, "");
84
- };
85
-
86
44
  const resolveArtifactUrl = (baseURL: string, value: string): string => {
87
45
  if (/^https?:\/\//i.test(value)) {
88
- new URL(value);
46
+ try {
47
+ new URL(value);
48
+ } catch (error) {
49
+ throw new InvalidUpdateResponseError(`Invalid artifact URL: ${value}`, {
50
+ cause: error,
51
+ });
52
+ }
89
53
  return value;
90
54
  }
91
55
  if (!value.startsWith("/storage/")) {
92
- throw new Error(
56
+ throw new InvalidUpdateResponseError(
93
57
  "Artifact URLs must be absolute HTTP(S) URLs or client-relative storage paths.",
94
58
  );
95
59
  }
@@ -101,109 +65,56 @@ const resolveArtifactUrls = (
101
65
  info: ArtifactInfo,
102
66
  ): ArtifactInfo => ({
103
67
  ...info,
104
- fileUrl:
105
- info.fileUrl === null ? null : resolveArtifactUrl(baseURL, info.fileUrl),
106
- ...(info.manifestUrl === undefined
107
- ? {}
108
- : {
109
- manifestUrl:
110
- info.manifestUrl === null
111
- ? null
112
- : resolveArtifactUrl(baseURL, info.manifestUrl),
113
- }),
114
- ...(info.changedAssets === undefined
115
- ? {}
116
- : {
117
- changedAssets:
118
- info.changedAssets === null
119
- ? null
120
- : Object.fromEntries(
121
- Object.entries(info.changedAssets).map(([path, asset]) => [
122
- path,
123
- {
124
- ...asset,
125
- ...(asset.file
126
- ? {
127
- file: {
128
- ...asset.file,
129
- url: resolveArtifactUrl(baseURL, asset.file.url),
130
- },
131
- }
132
- : {}),
133
- ...(asset.patch
134
- ? {
135
- patch: {
136
- ...asset.patch,
137
- patchUrl: resolveArtifactUrl(
138
- baseURL,
139
- asset.patch.patchUrl,
140
- ),
141
- },
142
- }
143
- : {}),
144
- },
145
- ]),
146
- ),
147
- }),
148
- });
149
-
150
- const sendInsightsEvent = async (
151
- baseURL: string,
152
- params: InsightsEventParams,
153
- ): Promise<void> => {
154
- const controller = new AbortController();
155
- const timeoutId = setTimeout(() => {
156
- controller.abort();
157
- }, params.requestTimeout ?? 5000);
158
-
159
- try {
160
- const response = await fetch(`${baseURL}/events`, {
161
- body: JSON.stringify({
162
- appVersion: params.appVersion,
163
- channel: params.channel,
164
- cohort: params.cohort,
165
- fingerprintHash: params.fingerprintHash,
166
- fromBundleId: params.fromBundleId,
167
- ...(params.fromReleaseId === undefined
168
- ? {}
169
- : { fromReleaseId: params.fromReleaseId }),
170
- installId: params.installId,
171
- platform: params.platform,
172
- sdkVersion: HOT_UPDATER_SDK_VERSION,
173
- toBundleId: params.toBundleId,
174
- ...(params.toReleaseId === undefined
175
- ? {}
176
- : { toReleaseId: params.toReleaseId }),
177
- type: params.type,
178
- updateStrategy: params.updateStrategy,
179
- ...(params.userId != null ? { userId: params.userId } : {}),
180
- ...(params.username != null ? { username: params.username } : {}),
181
- }),
182
- headers: {
183
- "Content-Type": "application/json",
184
- ...params.requestHeaders,
68
+ manifestUrl: resolveArtifactUrl(baseURL, info.manifestUrl),
69
+ ...(info.archiveUrl
70
+ ? { archiveUrl: resolveArtifactUrl(baseURL, info.archiveUrl) }
71
+ : {}),
72
+ assets: Object.fromEntries(
73
+ Object.entries(info.assets).map(([path, asset]) => [
74
+ path,
75
+ {
76
+ ...asset,
77
+ file: {
78
+ ...asset.file,
79
+ url: resolveArtifactUrl(baseURL, asset.file.url),
80
+ },
81
+ ...(asset.patch
82
+ ? {
83
+ patch: {
84
+ ...asset.patch,
85
+ patchUrl: resolveArtifactUrl(baseURL, asset.patch.patchUrl),
86
+ },
87
+ }
88
+ : {}),
185
89
  },
186
- method: "POST",
187
- signal: controller.signal,
188
- });
90
+ ]),
91
+ ),
92
+ });
189
93
 
190
- if (response.status !== 204) {
191
- throw new Error(
192
- `Expected HTTP 204 from /events, received ${response.status}`,
94
+ const requireArtifactProtocolV1 = (info: ArtifactInfo): ArtifactInfo => {
95
+ if (
96
+ info.artifactProtocolVersion !== ARTIFACT_PROTOCOL_VERSION ||
97
+ !info.assets ||
98
+ !info.manifestUrl ||
99
+ !info.manifestFileHash ||
100
+ (info.archiveUrl !== undefined && typeof info.archiveUrl !== "string")
101
+ ) {
102
+ throw new InvalidUpdateResponseError(
103
+ `Server does not support artifact protocol ${ARTIFACT_PROTOCOL_VERSION}.`,
104
+ );
105
+ }
106
+ for (const asset of Object.values(info.assets)) {
107
+ if (!asset.file?.url || !asset.fileHash) {
108
+ throw new InvalidUpdateResponseError(
109
+ "Artifact protocol 1 requires an original file for every asset.",
193
110
  );
194
111
  }
195
- } catch (error: unknown) {
196
- if (error instanceof Error && error.name === "AbortError") {
197
- throw new Error("Request timed out");
198
- }
199
- throw error;
200
- } finally {
201
- clearTimeout(timeoutId);
202
112
  }
113
+ return info;
203
114
  };
204
115
 
205
116
  const createSession = (baseURL: string): HotUpdaterHttpSession => ({
206
- fetchReleaseCatalog: async (params): Promise<ReleaseCatalog> => {
117
+ fetchReleaseCatalog: async (params): Promise<ReleaseCatalog | null> => {
207
118
  const channelKey = encodeChannelKey(params.channel);
208
119
  let strategyValue: string;
209
120
  if (params.updateStrategy === "fingerprint") {
@@ -241,16 +152,26 @@ const createSession = (baseURL: string): HotUpdaterHttpSession => ({
241
152
  });
242
153
  },
243
154
  resolveArtifact: async (params): Promise<ArtifactInfo> => {
244
- const info = await fetchJSON<ArtifactInfo>({
245
- requestHeaders: params.requestHeaders,
246
- requestTimeout: params.requestTimeout,
247
- url: `${baseURL}/artifacts/${encodeURIComponent(
248
- params.targetBundleId,
249
- )}/from/${encodeURIComponent(params.currentBundleId)}`,
250
- });
251
- return resolveArtifactUrls(baseURL, info);
155
+ let info: ArtifactInfo;
156
+ try {
157
+ info = await fetchJSON<ArtifactInfo>({
158
+ requestHeaders: params.requestHeaders,
159
+ requestTimeout: params.requestTimeout,
160
+ url: `${baseURL}/artifacts/v1/${encodeURIComponent(
161
+ params.targetBundleId,
162
+ )}/from/${encodeURIComponent(params.currentBundleId)}`,
163
+ });
164
+ } catch (error) {
165
+ if (error instanceof FetchJSONResponseError && error.status === 404) {
166
+ throw new InvalidUpdateResponseError(
167
+ `Server does not support artifact protocol ${ARTIFACT_PROTOCOL_VERSION}.`,
168
+ { cause: error },
169
+ );
170
+ }
171
+ throw error;
172
+ }
173
+ return resolveArtifactUrls(baseURL, requireArtifactProtocolV1(info));
252
174
  },
253
- sendInsightsEvent: (params) => sendInsightsEvent(baseURL, params),
254
175
  });
255
176
 
256
177
  /** Creates the private HTTP client used by HotUpdater.init and HotUpdater.wrap. */
package/src/index.ts CHANGED
@@ -1,16 +1,20 @@
1
+ import { emitAfterAppReady } from "./appReady";
1
2
  import {
2
3
  type CheckForUpdateOptions,
3
4
  checkForUpdate,
4
5
  type InternalCheckForUpdateOptions,
6
+ reportUpdateError,
5
7
  } from "./checkForUpdate";
6
8
  import { createHttpClient, type HotUpdaterHttpClient } from "./httpClient";
7
9
  import {
8
10
  addListener,
9
11
  getPublicActiveUpdateState,
12
+ getActiveUpdateState,
13
+ getBundleId,
10
14
  clearCrashHistory,
11
15
  getAppVersion,
12
16
  getBaseURL,
13
- getBundleId,
17
+ getUpdateId,
14
18
  getChannel,
15
19
  getCohort,
16
20
  getCrashHistory,
@@ -19,17 +23,16 @@ import {
19
23
  getInstallId,
20
24
  getManifest,
21
25
  getMinBundleId,
22
- getReleaseId,
23
26
  isChannelSwitched,
24
27
  notifyAppReady,
25
28
  reload,
26
29
  resetChannel,
27
30
  setCohort,
28
31
  setReloadBehavior,
29
- setUser,
32
+ stageBundle,
30
33
  type UpdateParams,
31
- updateBundle,
32
34
  } from "./native";
35
+ import { configurePlugins } from "./pluginHost";
33
36
  import { hotUpdaterStore } from "./store";
34
37
  import {
35
38
  type AutoUpdateOptions,
@@ -41,6 +44,28 @@ import {
41
44
  wrap,
42
45
  } from "./wrap";
43
46
 
47
+ export {
48
+ defineClientPlugin,
49
+ type AppReadyResult,
50
+ type BundleDownloadedInfo,
51
+ type HotUpdaterClientContext,
52
+ type HotUpdaterClientHooks,
53
+ type HotUpdaterClientPlugin,
54
+ type HotUpdaterClientStorage,
55
+ type ReleaseTransitionKind,
56
+ type UpdateCheckResult,
57
+ type UpdateError,
58
+ type UpdateErrorReason,
59
+ type UpdateErrorStage,
60
+ type UpdateStrategy,
61
+ } from "./clientPlugin";
62
+ // The built-in Insights client plugin, for HotUpdater.init({ plugins }).
63
+ export {
64
+ insights,
65
+ type InsightsOptions,
66
+ type InsightsPlugin,
67
+ type InsightsUser,
68
+ } from "@hot-updater/plugin-insights/client";
44
69
  export type {
45
70
  CustomReloadHandler,
46
71
  HotUpdaterEvent,
@@ -52,7 +77,6 @@ export type {
52
77
  ActiveUpdateState,
53
78
  ReloadBehavior,
54
79
  ReloadBehaviorSetting,
55
- SetUserParams,
56
80
  } from "./native";
57
81
  export * from "./store";
58
82
  export {
@@ -101,13 +125,11 @@ function createHotUpdaterClient() {
101
125
 
102
126
  // Global configuration stored from wrap
103
127
  const globalConfig: {
104
- insights: boolean;
105
128
  client: HotUpdaterHttpClient | null;
106
129
  requestHeaders?: Record<string, string>;
107
130
  requestTimeout?: number;
108
131
  onError?: (error: unknown) => void;
109
132
  } = {
110
- insights: true,
111
133
  client: null,
112
134
  };
113
135
 
@@ -156,10 +178,9 @@ function createHotUpdaterClient() {
156
178
  const autoOptions = incoming as AutoUpdateOptions;
157
179
 
158
180
  if (autoOptions.baseURL) {
159
- const { baseURL, ...rest } = autoOptions;
181
+ const { baseURL, plugins: _plugins, ...rest } = autoOptions;
160
182
  return {
161
183
  ...rest,
162
- insights: rest.insights ?? true,
163
184
  client: createHttpClient(baseURL),
164
185
  };
165
186
  }
@@ -176,10 +197,9 @@ function createHotUpdaterClient() {
176
197
  };
177
198
 
178
199
  if (rest.baseURL) {
179
- const { baseURL, ...baseURLRest } = rest;
200
+ const { baseURL, plugins: _plugins, ...baseURLRest } = rest;
180
201
  return {
181
202
  ...baseURLRest,
182
- insights: baseURLRest.insights ?? true,
183
203
  client: createHttpClient(baseURL),
184
204
  };
185
205
  }
@@ -191,7 +211,13 @@ function createHotUpdaterClient() {
191
211
  normalizedOptions: InternalInitOptions | InternalWrapOptions,
192
212
  options: HotUpdaterOptions | HotUpdaterInitOptions,
193
213
  ) => {
194
- globalConfig.insights = normalizedOptions.insights ?? true;
214
+ // Plugins are set up before init or wrap reads the launch they observe.
215
+ configurePlugins(options.plugins, {
216
+ baseURL: options.baseURL,
217
+ requestHeaders: options.requestHeaders,
218
+ requestTimeout: options.requestTimeout,
219
+ onError: options.onError,
220
+ });
195
221
  globalConfig.client = normalizedOptions.client;
196
222
  globalConfig.requestHeaders = options.requestHeaders;
197
223
  globalConfig.requestTimeout = options.requestTimeout;
@@ -336,16 +362,14 @@ function createHotUpdaterClient() {
336
362
  getActiveUpdateState: async () => getPublicActiveUpdateState(),
337
363
 
338
364
  /**
339
- * Fetches the current bundle ID of the app.
365
+ * Returns the selected update ID, matching the ID shown in the console.
366
+ * A staged update can change this ID before the app reloads.
340
367
  */
341
- getBundleId,
368
+ getBundleId: getUpdateId,
342
369
 
343
370
  /** Returns the minimum bundle ID based on the native app build time. */
344
371
  getMinBundleId,
345
372
 
346
- /** Returns the active Release identity, or null for BUILTIN/legacy state. */
347
- getReleaseId,
348
-
349
373
  /**
350
374
  * Fetches the current manifest for the active bundle.
351
375
  */
@@ -451,7 +475,6 @@ function createHotUpdaterClient() {
451
475
 
452
476
  const mergedConfig: InternalCheckForUpdateOptions = {
453
477
  ...config,
454
- insights: globalConfig.insights,
455
478
  client,
456
479
  requestHeaders: {
457
480
  ...globalConfig.requestHeaders,
@@ -469,8 +492,6 @@ function createHotUpdaterClient() {
469
492
  *
470
493
  * @param {UpdateBundleParams} params - Parameters object required for bundle update
471
494
  * @param {string} params.bundleId - The bundle ID of the app
472
- * @param {string|null} params.fileUrl - The URL of the zip file
473
- *
474
495
  * @returns {Promise<boolean>} Whether the update was successful
475
496
  *
476
497
  * @example
@@ -494,9 +515,53 @@ function createHotUpdaterClient() {
494
515
  * }
495
516
  * ```
496
517
  */
497
- updateBundle: (params: UpdateParams) => {
518
+ updateBundle: async (params: UpdateParams) => {
498
519
  ensureGlobalClient("updateBundle");
499
- return updateBundle(params);
520
+ const fromBundleId = getBundleId();
521
+ const state = getActiveUpdateState();
522
+ const active = state.activeSelection;
523
+ const fromReleaseId =
524
+ active?.bundleId === fromBundleId
525
+ ? active.releaseId
526
+ : state.stableSelection?.bundleId === fromBundleId
527
+ ? state.stableSelection.releaseId
528
+ : null;
529
+ const channel = params.channel ?? getChannel();
530
+ const strategyOf = (scopeKey: string | null | undefined) =>
531
+ scopeKey?.startsWith("v1:fingerprint:") ? "fingerprint" : "appVersion";
532
+ let delivery: Awaited<ReturnType<typeof stageBundle>>;
533
+ try {
534
+ delivery = await stageBundle(params);
535
+ } catch (error) {
536
+ reportUpdateError(error, "download", undefined, {
537
+ bundleId: fromBundleId,
538
+ channel,
539
+ targetBundleId: params.bundleId,
540
+ targetReleaseId:
541
+ (params.selection as { releaseId?: string | null } | undefined)
542
+ ?.releaseId ?? null,
543
+ updateStrategy: strategyOf(
544
+ (params.selection as { scopeKey?: string | null } | undefined)
545
+ ?.scopeKey,
546
+ ),
547
+ });
548
+ throw error;
549
+ }
550
+ if (delivery !== null && params.bundleId !== fromBundleId) {
551
+ emitAfterAppReady("onBundleDownloaded", () => {
552
+ const selection = getActiveUpdateState().activeSelection;
553
+ return {
554
+ channel,
555
+ fromBundleId,
556
+ fromReleaseId,
557
+ toBundleId: params.bundleId,
558
+ toReleaseId: selection?.releaseId ?? null,
559
+ updateStrategy: strategyOf(selection?.scopeKey),
560
+ ...delivery,
561
+ };
562
+ });
563
+ }
564
+ return true;
500
565
  },
501
566
 
502
567
  /**
@@ -510,10 +575,8 @@ function createHotUpdaterClient() {
510
575
  hotUpdaterStore.setState({
511
576
  artifactType: null,
512
577
  details: null,
513
- downloadedBytes: undefined,
514
578
  isUpdateDownloaded: false,
515
579
  progress: 0,
516
- totalBytes: undefined,
517
580
  });
518
581
  }
519
582
  return ok;
@@ -537,11 +600,6 @@ function createHotUpdaterClient() {
537
600
  */
538
601
  getInstallId,
539
602
 
540
- /**
541
- * Persists nullable user identity fields associated with this installation.
542
- */
543
- setUser,
544
-
545
603
  /**
546
604
  * Reads the native launch report for the current process.
547
605
  */