@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/native.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
- type ChangedAsset,
2
+ type ArtifactAsset,
3
3
  type CatalogHighWater,
4
4
  INVALID_COHORT_ERROR_MESSAGE,
5
5
  isValidCohort,
6
6
  normalizeCohortValue,
7
7
  type UpdateStatus,
8
8
  type PersistedSelectionReceipt,
9
- } from "@hot-updater/core";
9
+ } from "@hot-updater/protocol";
10
10
  import { NativeEventEmitter, Platform } from "react-native";
11
11
 
12
12
  import { HotUpdaterErrorCode, isHotUpdaterError } from "./error";
@@ -74,7 +74,10 @@ class HotUpdaterSessionState {
74
74
  private readonly defaultChannel: string;
75
75
  private currentChannel: string;
76
76
  private cachedCohort: string | undefined;
77
- private readonly inflightUpdates = new Map<string, Promise<boolean>>();
77
+ private readonly inflightUpdates = new Map<
78
+ string,
79
+ Promise<BundleDelivery | null>
80
+ >();
78
81
  private lastInstalledBundleId: string | null = null;
79
82
  private readonly activeBundleSnapshotCache = new Map<
80
83
  ActiveBundleSnapshotCacheKey,
@@ -103,11 +106,16 @@ class HotUpdaterSessionState {
103
106
  return this.lastInstalledBundleId === bundleId;
104
107
  }
105
108
 
106
- getInflightUpdate(bundleId: string): Promise<boolean> | undefined {
109
+ getInflightUpdate(
110
+ bundleId: string,
111
+ ): Promise<BundleDelivery | null> | undefined {
107
112
  return this.inflightUpdates.get(bundleId);
108
113
  }
109
114
 
110
- trackInflightUpdate(bundleId: string, promise: Promise<boolean>) {
115
+ trackInflightUpdate(
116
+ bundleId: string,
117
+ promise: Promise<BundleDelivery | null>,
118
+ ) {
111
119
  this.inflightUpdates.set(bundleId, promise);
112
120
  }
113
121
 
@@ -234,7 +242,7 @@ const getReloadProcess = (): (() => Promise<void>) | null => {
234
242
  : null;
235
243
  };
236
244
 
237
- export type HotUpdaterProgressArtifactType = "archive" | "diff";
245
+ export type HotUpdaterProgressArtifactType = "diff";
238
246
 
239
247
  export type HotUpdaterDiffFileStatus =
240
248
  | "pending"
@@ -286,18 +294,11 @@ export interface HotUpdaterDiffProgressDetails {
286
294
  files: HotUpdaterDiffFileSnapshot[];
287
295
  }
288
296
 
289
- export type HotUpdaterProgressEvent =
290
- | {
291
- progress: number;
292
- artifactType: "archive";
293
- downloadedBytes?: number;
294
- totalBytes?: number;
295
- }
296
- | {
297
- progress: number;
298
- artifactType: "diff";
299
- details: HotUpdaterDiffProgressDetails;
300
- };
297
+ export type HotUpdaterProgressEvent = {
298
+ progress: number;
299
+ artifactType: "diff";
300
+ details: HotUpdaterDiffProgressDetails;
301
+ };
301
302
 
302
303
  export type HotUpdaterEvent = {
303
304
  onProgress: HotUpdaterProgressEvent;
@@ -424,18 +425,42 @@ export const commitReleaseSelection = async (input: {
424
425
  return ok;
425
426
  };
426
427
 
428
+ /** How native delivered a staged bundle. */
429
+ export type BundleDelivery = {
430
+ /**
431
+ * "patch" when a bsdiff patch produced a file, "manifest" when only the
432
+ * changed files were downloaded, "archive" for the full archive.
433
+ */
434
+ readonly delivery: "patch" | "manifest" | "archive";
435
+ /** A patch was tried, but the file or the archive was downloaded instead. */
436
+ readonly patchFallback: boolean;
437
+ };
438
+
439
+ const readBundleDelivery = (value: unknown): BundleDelivery | null => {
440
+ if (typeof value !== "object" || value === null) return null;
441
+ const { delivery, patchFallback } = value as Record<string, unknown>;
442
+ if (
443
+ delivery !== "patch" &&
444
+ delivery !== "manifest" &&
445
+ delivery !== "archive"
446
+ ) {
447
+ return null;
448
+ }
449
+ return { delivery, patchFallback: patchFallback === true };
450
+ };
451
+
427
452
  /**
428
- * Downloads files and applies them to the app.
453
+ * Stages a bundle for the next launch. Resolves with how native delivered
454
+ * it, or with null when this runtime had already staged it and nothing was
455
+ * downloaded; rejects when the download or install fails.
429
456
  *
430
- * @param {UpdateParams} params - Parameters object required for bundle update
431
- * @returns {Promise<boolean>} Resolves with true if download was successful
432
457
  * @throws {Error} Rejects with error.code from HotUpdaterErrorCode enum and error.message
433
458
  */
434
- export async function updateBundle(params: UpdateParams): Promise<boolean> {
459
+ export async function stageBundle(
460
+ params: UpdateParams,
461
+ ): Promise<BundleDelivery | null> {
435
462
  const updateBundleId = params.bundleId;
436
463
  const status = params.status;
437
- const targetFileUrl = params.fileUrl;
438
-
439
464
  const currentBundleId = status === "UPDATE" ? getFreshBundleId() : undefined;
440
465
 
441
466
  // If native is still on the same bundle we installed in this session,
@@ -446,7 +471,7 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
446
471
  sessionState.hasInstalledBundle(updateBundleId) &&
447
472
  currentBundleId === updateBundleId
448
473
  ) {
449
- return true;
474
+ return null;
450
475
  }
451
476
 
452
477
  const shouldSkipCurrentBundleIdCheck =
@@ -467,30 +492,25 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
467
492
  const existing = sessionState.getInflightUpdate(updateBundleId);
468
493
  if (existing) return existing;
469
494
 
470
- const targetFileHash = params.fileHash;
471
495
  const targetChannel = params.channel;
472
496
  const targetManifestUrl = params.manifestUrl;
473
497
  const targetManifestFileHash = params.manifestFileHash;
474
- const targetChangedAssets = params.changedAssets;
498
+ const targetAssets = params.assets;
475
499
 
476
500
  const promise = (async () => {
477
501
  try {
478
502
  const selection = params.selection;
479
- const ok = await HotUpdaterNative.updateBundle({
503
+ const staged = await HotUpdaterNative.updateBundle({
480
504
  bundleId: updateBundleId,
481
505
  channel: targetChannel,
482
- changedAssets:
483
- (targetChangedAssets as Record<string, ChangedAsset> | null) ?? null,
484
- fileUrl: targetFileUrl,
485
- fileHash: targetFileHash ?? null,
486
- manifestFileHash: targetManifestFileHash ?? null,
487
- manifestUrl: targetManifestUrl ?? null,
506
+ assets: targetAssets as Record<string, ArtifactAsset>,
507
+ manifestFileHash: targetManifestFileHash,
508
+ manifestUrl: targetManifestUrl,
509
+ ...(params.archiveUrl ? { archiveUrl: params.archiveUrl } : {}),
488
510
  ...(selection === undefined ? {} : { selection }),
489
511
  });
490
- if (ok) {
491
- sessionState.markBundleInstalled(updateBundleId, targetChannel);
492
- }
493
- return ok;
512
+ sessionState.markBundleInstalled(updateBundleId, targetChannel);
513
+ return readBundleDelivery(staged);
494
514
  } finally {
495
515
  sessionState.clearInflightUpdate(updateBundleId);
496
516
  }
@@ -500,6 +520,18 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
500
520
  return promise;
501
521
  }
502
522
 
523
+ /**
524
+ * Downloads files and applies them to the app.
525
+ *
526
+ * @param {UpdateParams} params - Parameters object required for bundle update
527
+ * @returns {Promise<boolean>} Resolves with true if download was successful
528
+ * @throws {Error} Rejects with error.code from HotUpdaterErrorCode enum and error.message
529
+ */
530
+ export async function updateBundle(params: UpdateParams): Promise<boolean> {
531
+ await stageBundle(params);
532
+ return true;
533
+ }
534
+
503
535
  /**
504
536
  * Fetches the current app version.
505
537
  */
@@ -599,8 +631,9 @@ export const getMinBundleId = (): string => {
599
631
  return constants.MIN_BUNDLE_ID;
600
632
  };
601
633
 
602
- export const getReleaseId = async (): Promise<string | null> =>
603
- getActiveUpdateState().activeSelection?.releaseId ?? null;
634
+ /** Returns the selected update ID shown in the console, including staged updates. */
635
+ export const getUpdateId = (): string =>
636
+ getActiveUpdateState().activeSelection?.releaseId ?? getBundleId();
604
637
 
605
638
  /**
606
639
  * Fetches the current bundle version id.
@@ -704,64 +737,31 @@ export const getInstallId = (): string => {
704
737
  return nativeModule.getInstallId();
705
738
  };
706
739
 
707
- export type PersistedUserIdentity = {
708
- userId?: string;
709
- username?: string;
710
- };
711
-
712
- export const getPersistedUserIdentity = (): PersistedUserIdentity => {
713
- const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
714
- getUserId?: () => string | null;
715
- getUsername?: () => string | null;
716
- };
717
-
718
- if (
719
- typeof nativeModule.getUserId !== "function" ||
720
- typeof nativeModule.getUsername !== "function"
721
- ) {
740
+ const requireStorageNativeMethod = <T extends (...args: any[]) => any>(
741
+ name: "getStorageItem" | "setStorageItem",
742
+ ): T => {
743
+ const method = (HotUpdaterNative as unknown as Record<string, unknown>)[name];
744
+ if (typeof method !== "function") {
722
745
  throw new Error(
723
- "[HotUpdater] Native module is missing 'getUserId()' or 'getUsername()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.",
746
+ `[HotUpdater] Native module is missing '${name}()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.`,
724
747
  );
725
748
  }
726
-
727
- const userId = nativeModule.getUserId();
728
- const username = nativeModule.getUsername();
729
-
730
- return {
731
- ...(userId != null ? { userId } : {}),
732
- ...(username != null ? { username } : {}),
733
- };
749
+ return method.bind(HotUpdaterNative) as T;
734
750
  };
735
751
 
736
- export type SetUserParams = {
737
- userId?: string | number | null;
738
- username?: string | null;
752
+ /** Reads a value from the SDK's native key-value store. */
753
+ export const getStorageItem = (key: string): string | null => {
754
+ const value =
755
+ requireStorageNativeMethod<(key: string) => unknown>("getStorageItem")(key);
756
+ return typeof value === "string" ? value : null;
739
757
  };
740
758
 
741
- export function setUser(params: SetUserParams): void;
742
- export function setUser(params: null): void;
743
- export function setUser(params: SetUserParams | null): void {
744
- const nativeModule = HotUpdaterNative as typeof HotUpdaterNative & {
745
- setUser?: (userId: string | null, username: string | null) => void;
746
- };
747
-
748
- if (typeof nativeModule.setUser !== "function") {
749
- throw new Error(
750
- "[HotUpdater] Native module is missing 'setUser()'. This JS bundle requires a newer native @hot-updater/react-native SDK. Rebuild and release a new app version before delivering this OTA update.",
751
- );
752
- }
753
-
754
- if (params === null) {
755
- nativeModule.setUser(null, null);
756
- return;
757
- }
758
-
759
- const normalizedUserId =
760
- params.userId === null || params.userId === undefined
761
- ? null
762
- : String(params.userId);
763
- nativeModule.setUser(normalizedUserId, params.username ?? null);
764
- }
759
+ /** Writes a value to the SDK's native key-value store; null removes it. */
760
+ export const setStorageItem = (key: string, value: string | null): void => {
761
+ requireStorageNativeMethod<(key: string, value: string | null) => void>(
762
+ "setStorageItem",
763
+ )(key, value);
764
+ };
765
765
 
766
766
  /**
767
767
  * Result returned by notifyAppReady()
@@ -783,18 +783,20 @@ export type NotifyAppReadyResult =
783
783
  toReleaseId?: string;
784
784
  };
785
785
 
786
- export type NotifyAppReadyInsightsEvent = {
786
+ /** An update applied or recovered at this launch, as native persisted it. */
787
+ export type LaunchTransition = {
787
788
  type: "UPDATE_APPLIED" | "RECOVERED";
788
789
  fromBundleId: string;
789
790
  toBundleId: string;
790
- fromReleaseId?: string | null;
791
- toReleaseId?: string | null;
791
+ fromReleaseId: string | null;
792
+ toReleaseId: string | null;
792
793
  updateStrategy: PersistedUpdateStrategy;
793
794
  };
794
795
 
795
796
  type RawNotifyAppReadyResult = {
796
797
  status?: string;
797
798
  crashedBundleId?: string;
799
+ previousProcessExit?: string;
798
800
  fromReleaseId?: string;
799
801
  fromBundleId?: string;
800
802
  toReleaseId?: string;
@@ -891,9 +893,9 @@ const getNotifyAppReadyTransition = (
891
893
  return null;
892
894
  };
893
895
 
894
- const getNotifyAppReadyInsightsEvent = (
896
+ const getLaunchTransition = (
895
897
  result: RawNotifyAppReadyResult,
896
- ): NotifyAppReadyInsightsEvent | null => {
898
+ ): LaunchTransition | null => {
897
899
  const transition = getNotifyAppReadyTransition(result);
898
900
 
899
901
  if (!transition || !isPersistedUpdateStrategy(result.updateStrategy)) {
@@ -904,12 +906,8 @@ const getNotifyAppReadyInsightsEvent = (
904
906
  type: transition.status,
905
907
  fromBundleId: transition.fromBundleId,
906
908
  toBundleId: transition.toBundleId,
907
- ...(transition.fromReleaseId === null
908
- ? {}
909
- : { fromReleaseId: transition.fromReleaseId }),
910
- ...(transition.toReleaseId === null
911
- ? {}
912
- : { toReleaseId: transition.toReleaseId }),
909
+ fromReleaseId: transition.fromReleaseId,
910
+ toReleaseId: transition.toReleaseId,
913
911
  updateStrategy: result.updateStrategy,
914
912
  };
915
913
  };
@@ -938,14 +936,21 @@ const normalizeNotifyAppReadyResult = (
938
936
 
939
937
  export const readNotifyAppReady = (): {
940
938
  result: NotifyAppReadyResult;
941
- insightsEvent: NotifyAppReadyInsightsEvent | null;
939
+ transition: LaunchTransition | null;
940
+ /** Android 11+: why the previous main process exited, when native knows. */
941
+ previousProcessExit: string | null;
942
942
  pending: boolean;
943
943
  } => {
944
944
  const rawResult = readRawNotifyAppReadyResult();
945
+ const { previousProcessExit } = rawResult;
945
946
 
946
947
  return {
947
948
  result: normalizeNotifyAppReadyResult(rawResult),
948
- insightsEvent: getNotifyAppReadyInsightsEvent(rawResult),
949
+ transition: getLaunchTransition(rawResult),
950
+ previousProcessExit:
951
+ typeof previousProcessExit === "string" && previousProcessExit !== ""
952
+ ? previousProcessExit
953
+ : null,
949
954
  pending: rawResult.status === "PENDING",
950
955
  };
951
956
  };
@@ -0,0 +1,45 @@
1
+ import { createPluginHost } from "@hot-updater/protocol";
2
+ import { Platform } from "react-native";
3
+
4
+ import {
5
+ getAppVersion,
6
+ getBundleId,
7
+ getChannel,
8
+ getCohort,
9
+ getFingerprintHash,
10
+ getInstallId,
11
+ getStorageItem,
12
+ setStorageItem,
13
+ } from "./native";
14
+ import { HOT_UPDATER_SDK_VERSION } from "./sdkVersion";
15
+
16
+ export type { PluginHostConfig } from "@hot-updater/protocol";
17
+
18
+ /** The app's plugin host: plugins read the native module and fetch the configured server. */
19
+ export const {
20
+ configurePlugins,
21
+ hasPluginHook,
22
+ buildPluginEvent,
23
+ dispatchPluginHook,
24
+ emitPluginHook,
25
+ } = createPluginHost({
26
+ fetch: (url, init) => fetch(url, init),
27
+ get platform() {
28
+ return Platform.OS === "android" ? "android" : "ios";
29
+ },
30
+ get isDebugBuild() {
31
+ return typeof __DEV__ !== "undefined" && __DEV__;
32
+ },
33
+ get sdkVersion() {
34
+ return HOT_UPDATER_SDK_VERSION;
35
+ },
36
+ getInstallId: () => getInstallId(),
37
+ getAppVersion: () => getAppVersion(),
38
+ getBundleId: () => getBundleId(),
39
+ getChannel: () => getChannel(),
40
+ getCohort: () => getCohort(),
41
+ getFingerprintHash: () => getFingerprintHash(),
42
+ getStorageItem: (key) => getStorageItem(key),
43
+ setStorageItem: (key, value) => setStorageItem(key, value),
44
+ now: () => Date.now(),
45
+ });
@@ -1,17 +1,15 @@
1
1
  import {
2
- MAX_TARGET_COHORTS_PER_RELEASE,
3
- MAX_COMPILED_CATALOG_BYTES,
4
- MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE,
5
- NUMERIC_COHORT_SIZE,
6
- isUUIDv7,
7
- parseReleaseCatalogScopeKey,
2
+ type ExpectedReleaseCatalogScope,
3
+ getUtf8ByteLength,
4
+ MAX_RELEASE_CATALOG_WIRE_BYTES,
5
+ parseReleaseCatalog,
8
6
  type ReleaseCatalog,
9
- } from "@hot-updater/core";
7
+ } from "@hot-updater/protocol";
8
+
9
+ import { InvalidUpdateResponseError, UpdateHttpError } from "./updateError";
10
10
 
11
11
  const CACHE_FORMAT_VERSION = "1";
12
12
  const MAX_ETAG_BYTES = 1024;
13
- export const MAX_RELEASE_CATALOG_WIRE_BYTES =
14
- MAX_COMPILED_CATALOG_BYTES * 2 + 4 * 1024;
15
13
  export const MAX_RELEASE_CATALOG_CACHE_ENTRY_BYTES =
16
14
  MAX_RELEASE_CATALOG_WIRE_BYTES + MAX_ETAG_BYTES + 3;
17
15
 
@@ -46,20 +44,6 @@ let lastParsedCache:
46
44
  })
47
45
  | null = null;
48
46
 
49
- export type ExpectedReleaseCatalogScope = {
50
- readonly channelKey: string;
51
- readonly platform: "ios" | "android";
52
- } & (
53
- | {
54
- readonly strategy: "APP_VERSION";
55
- readonly fingerprintHash?: never;
56
- }
57
- | {
58
- readonly strategy: "FINGERPRINT";
59
- readonly fingerprintHash: string;
60
- }
61
- );
62
-
63
47
  type FetchReleaseCatalogInput = {
64
48
  readonly baseURL: string;
65
49
  readonly expectedScope: ExpectedReleaseCatalogScope;
@@ -68,117 +52,6 @@ type FetchReleaseCatalogInput = {
68
52
  readonly url: string;
69
53
  };
70
54
 
71
- const getUtf8ByteLength = (value: string): number => {
72
- let bytes = 0;
73
- for (let index = 0; index < value.length; index += 1) {
74
- const code = value.charCodeAt(index);
75
- if (code <= 0x7f) {
76
- bytes += 1;
77
- } else if (code <= 0x7ff) {
78
- bytes += 2;
79
- } else if (
80
- code >= 0xd800 &&
81
- code <= 0xdbff &&
82
- index + 1 < value.length &&
83
- value.charCodeAt(index + 1) >= 0xdc00 &&
84
- value.charCodeAt(index + 1) <= 0xdfff
85
- ) {
86
- bytes += 4;
87
- index += 1;
88
- } else {
89
- bytes += 3;
90
- }
91
- }
92
- return bytes;
93
- };
94
-
95
- const isStringArray = (value: unknown): value is readonly string[] =>
96
- Array.isArray(value) && value.every((entry) => typeof entry === "string");
97
-
98
- const isValidReleaseDescriptor = (value: unknown): boolean => {
99
- if (value === null || typeof value !== "object") return false;
100
- const descriptor = value as Record<string, unknown>;
101
-
102
- return (
103
- isUUIDv7(descriptor.releaseId) &&
104
- (descriptor.kind === "BUNDLE" || descriptor.kind === "EMBEDDED") &&
105
- ((descriptor.kind === "BUNDLE" &&
106
- typeof descriptor.bundleId === "string") ||
107
- (descriptor.kind === "EMBEDDED" && descriptor.bundleId === null)) &&
108
- Number.isSafeInteger(descriptor.rolloutCohortCount) &&
109
- (descriptor.rolloutCohortCount as number) >= 0 &&
110
- (descriptor.rolloutCohortCount as number) <= NUMERIC_COHORT_SIZE &&
111
- isStringArray(descriptor.targetCohorts) &&
112
- descriptor.targetCohorts.length <= MAX_TARGET_COHORTS_PER_RELEASE &&
113
- typeof descriptor.shouldForceUpdate === "boolean" &&
114
- (descriptor.message === null || typeof descriptor.message === "string")
115
- );
116
- };
117
-
118
- export const hasExpectedReleaseCatalogScope = (
119
- catalog: ReleaseCatalog,
120
- expected: ExpectedReleaseCatalogScope,
121
- ): boolean => {
122
- try {
123
- const parsed = parseReleaseCatalogScopeKey(catalog.scopeKey);
124
- return (
125
- parsed.channelKey === expected.channelKey &&
126
- parsed.platform === expected.platform &&
127
- parsed.strategy === expected.strategy &&
128
- (parsed.strategy === "APP_VERSION" ||
129
- (expected.strategy === "FINGERPRINT" &&
130
- parsed.fingerprintHash === expected.fingerprintHash))
131
- );
132
- } catch {
133
- return false;
134
- }
135
- };
136
-
137
- const parseValidatedCatalog = (
138
- value: string,
139
- expectedScope: ExpectedReleaseCatalogScope,
140
- ): ReleaseCatalog | null => {
141
- if (getUtf8ByteLength(value) > MAX_RELEASE_CATALOG_WIRE_BYTES) return null;
142
-
143
- try {
144
- const catalog = JSON.parse(value) as Partial<ReleaseCatalog>;
145
- if (
146
- catalog.schemaVersion !== 1 ||
147
- typeof catalog.catalogId !== "string" ||
148
- catalog.catalogId.length === 0 ||
149
- typeof catalog.scopeKey !== "string" ||
150
- !Number.isSafeInteger(catalog.generation) ||
151
- (catalog.generation ?? 0) < 1 ||
152
- typeof catalog.catalogHash !== "string" ||
153
- !/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
154
- catalog.fallbackPolicy !== "BUILTIN_IF_ACTIVE_INELIGIBLE" ||
155
- !Array.isArray(catalog.releases) ||
156
- !catalog.releases.every(isValidReleaseDescriptor) ||
157
- (catalog.rollbackReleases !== undefined &&
158
- (!Array.isArray(catalog.rollbackReleases) ||
159
- !catalog.rollbackReleases.every(isValidReleaseDescriptor)))
160
- ) {
161
- return null;
162
- }
163
- if (
164
- !hasExpectedReleaseCatalogScope(catalog as ReleaseCatalog, expectedScope)
165
- ) {
166
- return null;
167
- }
168
- const distinctTargetCohorts = new Set(
169
- [...catalog.releases, ...(catalog.rollbackReleases ?? [])].flatMap(
170
- (release) => release.targetCohorts,
171
- ),
172
- );
173
- if (distinctTargetCohorts.size > MAX_DISTINCT_TARGET_COHORTS_PER_SCOPE) {
174
- return null;
175
- }
176
- return catalog as ReleaseCatalog;
177
- } catch {
178
- return null;
179
- }
180
- };
181
-
182
55
  const isValidETag = (value: string | null): value is string =>
183
56
  value !== null &&
184
57
  value.length > 0 &&
@@ -214,7 +87,7 @@ const parseCache = (
214
87
  const etag = value.slice(versionEnd + 1, etagEnd);
215
88
  const body = value.slice(etagEnd + 1);
216
89
  if (!isValidETag(etag)) return null;
217
- const catalog = parseValidatedCatalog(body, expectedScope);
90
+ const catalog = parseReleaseCatalog(body, expectedScope);
218
91
  if (catalog === null) return null;
219
92
  const parsed = { catalog, etag, partition, serialized: value };
220
93
  lastParsedCache = parsed;
@@ -290,16 +163,25 @@ const consumeSuccessfulResponse = async (
290
163
  response: Response,
291
164
  input: FetchReleaseCatalogInput,
292
165
  partition: string,
293
- ): Promise<ReleaseCatalog> => {
166
+ ): Promise<ReleaseCatalog | null> => {
167
+ // The server marks the 404 of a scope that has no catalog yet: no update,
168
+ // not a failure. Any other 404 is a wrong baseURL or route.
169
+ if (
170
+ response.status === 404 &&
171
+ response.headers.get("x-hot-updater-catalog")?.trim().toLowerCase() ===
172
+ "none"
173
+ ) {
174
+ return null;
175
+ }
294
176
  if (response.status !== 200) {
295
- throw new Error(response.statusText);
177
+ throw new UpdateHttpError(response.status, response.statusText);
296
178
  }
297
179
 
298
180
  const body = await response.text();
299
- const catalog = parseValidatedCatalog(body, input.expectedScope);
181
+ const catalog = parseReleaseCatalog(body, input.expectedScope);
300
182
  if (catalog === null) {
301
183
  await removeNativeReleaseCatalogCache(partition);
302
- throw new Error("Received an invalid Release catalog");
184
+ throw new InvalidUpdateResponseError("Received an invalid Release catalog");
303
185
  }
304
186
 
305
187
  const etag = response.headers.get("etag");
@@ -323,9 +205,13 @@ const consumeSuccessfulResponse = async (
323
205
  return catalog;
324
206
  };
325
207
 
208
+ /**
209
+ * Fetches a scope's catalog, or null when the server answers that the scope
210
+ * has none (a 404 with `x-hot-updater-catalog: none`).
211
+ */
326
212
  export const fetchReleaseCatalogWithCache = async (
327
213
  input: FetchReleaseCatalogInput,
328
- ): Promise<ReleaseCatalog> => {
214
+ ): Promise<ReleaseCatalog | null> => {
329
215
  const partition = createReleaseCatalogCachePartition(input);
330
216
  const cached = await readValidatedCache(partition, input.expectedScope);
331
217
  const response = await fetchCatalogResponse(input, cached?.etag);
package/src/sdkVersion.ts CHANGED
@@ -1 +1 @@
1
- export const HOT_UPDATER_SDK_VERSION = "1.0.0-rc.2";
1
+ export const HOT_UPDATER_SDK_VERSION = "1.0.0-rc.21";