@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.15

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 (227) 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 +651 -637
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +10 -0
  5. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +0 -13
  6. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +10 -14
  7. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
  8. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  9. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +19 -0
  10. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  11. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  12. package/android/src/newarch/HotUpdater.kt +7 -9
  13. package/android/src/newarch/HotUpdaterModule.kt +40 -20
  14. package/android/src/oldarch/HotUpdater.kt +7 -9
  15. package/android/src/oldarch/HotUpdaterModule.kt +40 -20
  16. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
  17. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
  18. package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
  19. package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
  20. package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
  21. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  22. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  23. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
  24. package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -0
  25. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  26. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  27. package/ios/HotUpdater/Internal/HotUpdater.mm +5 -27
  28. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +149 -51
  29. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  30. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  31. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
  32. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  33. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  34. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  35. package/lib/commonjs/checkForUpdate.js +32 -23
  36. package/lib/commonjs/checkForUpdate.js.map +1 -1
  37. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +97 -28
  38. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  39. package/lib/commonjs/error.js +4 -13
  40. package/lib/commonjs/error.js.map +1 -1
  41. package/lib/commonjs/fetchJSON.js +9 -2
  42. package/lib/commonjs/fetchJSON.js.map +1 -1
  43. package/lib/commonjs/httpClient.js +46 -29
  44. package/lib/commonjs/httpClient.js.map +1 -1
  45. package/lib/commonjs/httpClient.spec.js +57 -5
  46. package/lib/commonjs/httpClient.spec.js.map +1 -1
  47. package/lib/commonjs/index.js +31 -14
  48. package/lib/commonjs/index.js.map +1 -1
  49. package/lib/commonjs/index.spec.js +56 -11
  50. package/lib/commonjs/index.spec.js.map +1 -1
  51. package/lib/commonjs/native.js +15 -14
  52. package/lib/commonjs/native.js.map +1 -1
  53. package/lib/commonjs/native.spec.js +128 -22
  54. package/lib/commonjs/native.spec.js.map +1 -1
  55. package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +28 -21
  56. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
  57. package/lib/commonjs/notifyAppReadyInsights.js +188 -0
  58. package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
  59. package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +161 -29
  60. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
  61. package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
  62. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
  63. package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +14 -7
  64. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
  65. package/lib/commonjs/sdkVersion.js +1 -1
  66. package/lib/commonjs/sdkVersion.js.map +1 -1
  67. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  68. package/lib/commonjs/store.js +3 -10
  69. package/lib/commonjs/store.js.map +1 -1
  70. package/lib/commonjs/store.notification.spec.js +12 -4
  71. package/lib/commonjs/store.notification.spec.js.map +1 -1
  72. package/lib/commonjs/store.spec.js +2 -136
  73. package/lib/commonjs/store.spec.js.map +1 -1
  74. package/lib/commonjs/wrap.js +11 -12
  75. package/lib/commonjs/wrap.js.map +1 -1
  76. package/lib/commonjs/wrap.spec.js +97 -25
  77. package/lib/commonjs/wrap.spec.js.map +1 -1
  78. package/lib/module/checkForUpdate.js +32 -23
  79. package/lib/module/checkForUpdate.js.map +1 -1
  80. package/lib/module/checkForUpdate.releaseCatalog.spec.js +97 -28
  81. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
  82. package/lib/module/error.js +4 -13
  83. package/lib/module/error.js.map +1 -1
  84. package/lib/module/fetchJSON.js +7 -1
  85. package/lib/module/fetchJSON.js.map +1 -1
  86. package/lib/module/httpClient.js +48 -31
  87. package/lib/module/httpClient.js.map +1 -1
  88. package/lib/module/httpClient.spec.js +57 -5
  89. package/lib/module/httpClient.spec.js.map +1 -1
  90. package/lib/module/index.js +32 -15
  91. package/lib/module/index.js.map +1 -1
  92. package/lib/module/index.spec.js +56 -11
  93. package/lib/module/index.spec.js.map +1 -1
  94. package/lib/module/native.js +13 -12
  95. package/lib/module/native.js.map +1 -1
  96. package/lib/module/native.spec.js +128 -22
  97. package/lib/module/native.spec.js.map +1 -1
  98. package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +23 -16
  99. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
  100. package/lib/module/notifyAppReadyInsights.js +181 -0
  101. package/lib/module/notifyAppReadyInsights.js.map +1 -0
  102. package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +153 -21
  103. package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
  104. package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
  105. package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
  106. package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +11 -4
  107. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
  108. package/lib/module/sdkVersion.js +1 -1
  109. package/lib/module/sdkVersion.js.map +1 -1
  110. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  111. package/lib/module/store.js +3 -10
  112. package/lib/module/store.js.map +1 -1
  113. package/lib/module/store.notification.spec.js +12 -4
  114. package/lib/module/store.notification.spec.js.map +1 -1
  115. package/lib/module/store.spec.js +2 -136
  116. package/lib/module/store.spec.js.map +1 -1
  117. package/lib/module/wrap.js +11 -12
  118. package/lib/module/wrap.js.map +1 -1
  119. package/lib/module/wrap.spec.js +99 -25
  120. package/lib/module/wrap.spec.js.map +1 -1
  121. package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -3
  122. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/error.d.ts +4 -13
  124. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/fetchJSON.d.ts +4 -0
  126. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/httpClient.d.ts +6 -6
  128. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/index.d.ts +4 -7
  130. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/native.d.ts +5 -9
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +25 -0
  134. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
  136. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +8 -35
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/wrap.types.d.ts +5 -8
  144. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  145. package/lib/typescript/module/checkForUpdate.d.ts +2 -3
  146. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  147. package/lib/typescript/module/error.d.ts +4 -13
  148. package/lib/typescript/module/error.d.ts.map +1 -1
  149. package/lib/typescript/module/fetchJSON.d.ts +4 -0
  150. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  151. package/lib/typescript/module/httpClient.d.ts +6 -6
  152. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  153. package/lib/typescript/module/index.d.ts +4 -7
  154. package/lib/typescript/module/index.d.ts.map +1 -1
  155. package/lib/typescript/module/native.d.ts +5 -9
  156. package/lib/typescript/module/native.d.ts.map +1 -1
  157. package/lib/typescript/module/notifyAppReadyInsights.d.ts +25 -0
  158. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
  159. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
  160. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
  161. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  162. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  163. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +8 -35
  164. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  165. package/lib/typescript/module/store.d.ts +1 -3
  166. package/lib/typescript/module/store.d.ts.map +1 -1
  167. package/lib/typescript/module/wrap.types.d.ts +5 -8
  168. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  169. package/package.json +3 -3
  170. package/src/checkForUpdate.releaseCatalog.spec.ts +137 -61
  171. package/src/checkForUpdate.ts +35 -29
  172. package/src/error.ts +4 -14
  173. package/src/fetchJSON.ts +12 -1
  174. package/src/httpClient.spec.ts +70 -5
  175. package/src/httpClient.ts +78 -64
  176. package/src/index.spec.ts +59 -11
  177. package/src/index.ts +41 -17
  178. package/src/native.spec.ts +133 -22
  179. package/src/native.ts +21 -32
  180. package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +25 -17
  181. package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +141 -22
  182. package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
  183. package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +13 -5
  184. package/src/notifyAppReadyInsights.ts +293 -0
  185. package/src/sdkVersion.ts +1 -1
  186. package/src/specs/NativeHotUpdater.ts +8 -36
  187. package/src/store.notification.spec.ts +13 -3
  188. package/src/store.spec.ts +0 -157
  189. package/src/store.ts +2 -23
  190. package/src/wrap.spec.ts +109 -31
  191. package/src/wrap.tsx +11 -11
  192. package/src/wrap.types.ts +5 -8
  193. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  194. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  195. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  196. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  197. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  198. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  199. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  200. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  201. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  202. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  203. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  204. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  205. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  206. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  207. package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
  208. package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
  209. package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
  210. package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
  211. package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
  212. package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
  213. package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
  214. package/lib/module/notifyAppReadyAnalytics.js +0 -128
  215. package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
  216. package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
  217. package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
  218. package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
  219. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +0 -13
  220. package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
  221. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
  222. package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
  223. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +0 -13
  224. package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
  225. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
  226. package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
  227. package/src/notifyAppReadyAnalytics.ts +0 -194
@@ -52,6 +52,13 @@ vi.mock("./specs/NativeHotUpdater", () => ({
52
52
  describe("notifyAppReady", () => {
53
53
  beforeEach(() => {
54
54
  vi.resetModules();
55
+ nativeModuleMock.getActiveUpdateState.mockReset();
56
+ nativeModuleMock.getActiveUpdateState.mockReturnValue({
57
+ activeSelection: null,
58
+ stableSelection: null,
59
+ verificationPending: false,
60
+ highestSeenCatalogs: {},
61
+ });
55
62
  nativeModuleMock.notifyAppReady.mockReset();
56
63
  nativeModuleMock.getBaseURL.mockReset();
57
64
  nativeModuleMock.getBundleId.mockReset();
@@ -122,6 +129,89 @@ describe("notifyAppReady", () => {
122
129
  });
123
130
  });
124
131
 
132
+ it("tracks same-file promotions without changing artifact or crash identities", async () => {
133
+ const state = {
134
+ activeSelection: {
135
+ kind: "BUNDLE",
136
+ bundleId: "bundle-123",
137
+ releaseId: "release-1",
138
+ },
139
+ };
140
+ nativeModuleMock.getActiveUpdateState.mockImplementation(() => state);
141
+ nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
142
+ nativeModuleMock.getManifest.mockReturnValue({ assets: {} });
143
+ nativeModuleMock.getCrashHistory.mockReturnValue(["bundle-failed"]);
144
+ const { getUpdateId, getBundleId, getManifest, getCrashHistory } =
145
+ await import("./native");
146
+
147
+ expect(getUpdateId()).toBe("release-1");
148
+ state.activeSelection.releaseId = "release-2";
149
+ expect(getUpdateId()).toBe("release-2");
150
+ expect(getBundleId()).toBe("bundle-123");
151
+ expect(getManifest().bundleId).toBe("bundle-123");
152
+ expect(getCrashHistory()).toEqual(["bundle-failed"]);
153
+ });
154
+
155
+ it.each([
156
+ [null, "min-bundle-id"],
157
+ ["legacy-bundle", "legacy-bundle"],
158
+ ])(
159
+ "preserves the fallback ID for native bundle %s",
160
+ async (bundleId, expected) => {
161
+ nativeModuleMock.getBundleId.mockReturnValue(bundleId);
162
+ const { getUpdateId } = await import("./native");
163
+
164
+ expect(getUpdateId()).toBe(expected);
165
+ },
166
+ );
167
+
168
+ it("reports a staged update while the manifest still identifies the running file", async () => {
169
+ const runningSelection = {
170
+ kind: "BUNDLE",
171
+ bundleId: "running-file",
172
+ releaseId: "running-update",
173
+ };
174
+ const state = {
175
+ activeSelection: runningSelection,
176
+ stableSelection: runningSelection,
177
+ };
178
+ nativeModuleMock.getActiveUpdateState.mockImplementation(() => state);
179
+ nativeModuleMock.getBundleId.mockReturnValue("running-file");
180
+ nativeModuleMock.getManifest.mockReturnValue({
181
+ assets: {},
182
+ bundleId: "running-file",
183
+ });
184
+ const { getUpdateId, getBundleId, getManifest } = await import("./native");
185
+
186
+ expect(getUpdateId()).toBe("running-update");
187
+ state.activeSelection = {
188
+ kind: "BUNDLE",
189
+ bundleId: "staged-file",
190
+ releaseId: "staged-update",
191
+ };
192
+
193
+ expect(getUpdateId()).toBe("staged-update");
194
+ expect(getBundleId()).toBe("running-file");
195
+ expect(getManifest().bundleId).toBe("running-file");
196
+ });
197
+
198
+ it("reports an explicit built-in rollback by its update ID", async () => {
199
+ nativeModuleMock.getActiveUpdateState.mockReturnValue(
200
+ JSON.stringify({
201
+ activeSelection: {
202
+ kind: "EMBEDDED",
203
+ releaseId: "rollback-1",
204
+ bundleId: "min-bundle-id",
205
+ },
206
+ }),
207
+ );
208
+ nativeModuleMock.getBundleId.mockReturnValue(null);
209
+ const { getUpdateId, getBundleId } = await import("./native");
210
+
211
+ expect(getUpdateId()).toBe("rollback-1");
212
+ expect(getBundleId()).toBe("min-bundle-id");
213
+ });
214
+
125
215
  it("normalizes legacy PROMOTED launch reports to UPDATE_APPLIED", async () => {
126
216
  nativeModuleMock.notifyAppReady.mockReturnValue(
127
217
  JSON.stringify({
@@ -157,7 +247,7 @@ describe("notifyAppReady", () => {
157
247
  toBundleId: "bundle-122",
158
248
  });
159
249
  expect(readNotifyAppReady()).toEqual({
160
- analyticsEvent: {
250
+ insightsEvent: {
161
251
  fromBundleId: "bundle-123",
162
252
  toBundleId: "bundle-122",
163
253
  type: "RECOVERED",
@@ -184,13 +274,13 @@ describe("notifyAppReady", () => {
184
274
 
185
275
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
186
276
  expect(readNotifyAppReady()).toEqual({
187
- analyticsEvent: null,
277
+ insightsEvent: null,
188
278
  pending: false,
189
279
  result: { status: "UNCHANGED" },
190
280
  });
191
281
  });
192
282
 
193
- it("returns UNCHANGED when automatic analytics metadata is incomplete", async () => {
283
+ it("returns UNCHANGED when automatic insights metadata is incomplete", async () => {
194
284
  nativeModuleMock.notifyAppReady.mockReturnValue({
195
285
  status: "UPDATE_APPLIED",
196
286
  });
@@ -199,7 +289,7 @@ describe("notifyAppReady", () => {
199
289
 
200
290
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
201
291
  expect(readNotifyAppReady()).toEqual({
202
- analyticsEvent: null,
292
+ insightsEvent: null,
203
293
  pending: false,
204
294
  result: { status: "UNCHANGED" },
205
295
  });
@@ -212,7 +302,7 @@ describe("notifyAppReady", () => {
212
302
 
213
303
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
214
304
  expect(readNotifyAppReady()).toEqual({
215
- analyticsEvent: null,
305
+ insightsEvent: null,
216
306
  pending: false,
217
307
  result: { status: "UNCHANGED" },
218
308
  });
@@ -225,7 +315,7 @@ describe("notifyAppReady", () => {
225
315
 
226
316
  expect(notifyAppReady()).toEqual({ status: "UNCHANGED" });
227
317
  expect(readNotifyAppReady()).toEqual({
228
- analyticsEvent: null,
318
+ insightsEvent: null,
229
319
  pending: true,
230
320
  result: { status: "UNCHANGED" },
231
321
  });
@@ -448,13 +538,20 @@ describe("notifyAppReady", () => {
448
538
  expect(getBaseURL()).toBe("file:///bundle-123");
449
539
 
450
540
  await updateBundle({
541
+ assets: {},
451
542
  bundleId: "bundle-456",
452
- fileHash: null,
453
- fileUrl: "https://example.com/bundle.zip",
543
+ archiveUrl: "https://example.com/bundle.tar.br",
544
+ manifestFileHash: "manifest-hash",
545
+ manifestUrl: "https://example.com/manifest.json",
454
546
  status: "UPDATE",
455
547
  });
456
548
 
457
549
  expect(getBundleId()).toBe("bundle-123");
550
+ expect(nativeModuleMock.updateBundle).toHaveBeenCalledWith(
551
+ expect.objectContaining({
552
+ archiveUrl: "https://example.com/bundle.tar.br",
553
+ }),
554
+ );
458
555
  expect(getManifest()).toEqual({
459
556
  assets: {},
460
557
  bundleId: "bundle-123",
@@ -481,9 +578,10 @@ describe("notifyAppReady", () => {
481
578
  const { getBundleId, updateBundle } = await import("./native");
482
579
 
483
580
  await updateBundle({
581
+ assets: {},
484
582
  bundleId: "00000000-0000-0000-0000-000000000003",
485
- fileHash: null,
486
- fileUrl: "https://example.com/bundle.zip",
583
+ manifestFileHash: "manifest-hash",
584
+ manifestUrl: "https://example.com/manifest.json",
487
585
  status: "UPDATE",
488
586
  });
489
587
 
@@ -496,9 +594,10 @@ describe("notifyAppReady", () => {
496
594
 
497
595
  await expect(
498
596
  updateBundle({
597
+ assets: {},
499
598
  bundleId: "00000000-0000-0000-0000-000000000003",
500
- fileHash: null,
501
- fileUrl: "https://example.com/bundle.zip",
599
+ manifestFileHash: "manifest-hash",
600
+ manifestUrl: "https://example.com/manifest.json",
502
601
  status: "UPDATE",
503
602
  }),
504
603
  ).resolves.toBe(true);
@@ -522,9 +621,10 @@ describe("notifyAppReady", () => {
522
621
  const { updateBundle } = await import("./native");
523
622
 
524
623
  await updateBundle({
624
+ assets: {},
525
625
  bundleId: "00000000-0000-0000-0000-000000000003",
526
- fileHash: null,
527
- fileUrl: "https://example.com/bundle.zip",
626
+ manifestFileHash: "manifest-hash",
627
+ manifestUrl: "https://example.com/manifest.json",
528
628
  status: "UPDATE",
529
629
  });
530
630
 
@@ -534,9 +634,10 @@ describe("notifyAppReady", () => {
534
634
 
535
635
  await expect(
536
636
  updateBundle({
637
+ assets: {},
537
638
  bundleId: "00000000-0000-0000-0000-000000000003",
538
- fileHash: null,
539
- fileUrl: "https://example.com/bundle.zip",
639
+ manifestFileHash: "manifest-hash",
640
+ manifestUrl: "https://example.com/manifest.json",
540
641
  status: "UPDATE",
541
642
  }),
542
643
  ).resolves.toBe(true);
@@ -552,7 +653,7 @@ describe("notifyAppReady", () => {
552
653
 
553
654
  await updateBundle({
554
655
  bundleId: "bundle-789",
555
- changedAssets: {
656
+ assets: {
556
657
  "index.ios.bundle": {
557
658
  file: {
558
659
  compression: "br",
@@ -568,8 +669,6 @@ describe("notifyAppReady", () => {
568
669
  },
569
670
  },
570
671
  },
571
- fileHash: "sig:archive",
572
- fileUrl: "https://example.com/bundle.zip",
573
672
  manifestFileHash: "sig:manifest",
574
673
  manifestUrl: "https://example.com/manifest.json",
575
674
  status: "UPDATE",
@@ -577,7 +676,7 @@ describe("notifyAppReady", () => {
577
676
 
578
677
  expect(nativeModuleMock.updateBundle).toHaveBeenCalledWith({
579
678
  bundleId: "bundle-789",
580
- changedAssets: {
679
+ assets: {
581
680
  "index.ios.bundle": {
582
681
  file: {
583
682
  compression: "br",
@@ -594,8 +693,6 @@ describe("notifyAppReady", () => {
594
693
  },
595
694
  },
596
695
  channel: undefined,
597
- fileHash: "sig:archive",
598
- fileUrl: "https://example.com/bundle.zip",
599
696
  manifestFileHash: "sig:manifest",
600
697
  manifestUrl: "https://example.com/manifest.json",
601
698
  });
@@ -728,6 +825,20 @@ describe("notifyAppReady", () => {
728
825
  expect(getInstallId()).toBe("install-123");
729
826
  });
730
827
 
828
+ it("normalizes an omitted native fingerprint constant to null", async () => {
829
+ nativeModuleMock.getConstants.mockReturnValue({
830
+ APP_VERSION: null,
831
+ CHANNEL: "production",
832
+ DEFAULT_CHANNEL: "production",
833
+ FINGERPRINT_HASH: undefined as unknown as null,
834
+ MIN_BUNDLE_ID: "min-bundle-id",
835
+ });
836
+
837
+ const { getFingerprintHash } = await import("./native");
838
+
839
+ expect(getFingerprintHash()).toBeNull();
840
+ });
841
+
731
842
  it("throws when native SDK does not expose getInstallId", async () => {
732
843
  const nativeModule = nativeModuleMock as typeof nativeModuleMock & {
733
844
  getInstallId?: typeof nativeModuleMock.getInstallId;
package/src/native.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {
2
- type ChangedAsset,
2
+ type ArtifactAsset,
3
3
  type CatalogHighWater,
4
4
  INVALID_COHORT_ERROR_MESSAGE,
5
5
  isValidCohort,
@@ -234,7 +234,7 @@ const getReloadProcess = (): (() => Promise<void>) | null => {
234
234
  : null;
235
235
  };
236
236
 
237
- export type HotUpdaterProgressArtifactType = "archive" | "diff";
237
+ export type HotUpdaterProgressArtifactType = "diff";
238
238
 
239
239
  export type HotUpdaterDiffFileStatus =
240
240
  | "pending"
@@ -286,18 +286,11 @@ export interface HotUpdaterDiffProgressDetails {
286
286
  files: HotUpdaterDiffFileSnapshot[];
287
287
  }
288
288
 
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
- };
289
+ export type HotUpdaterProgressEvent = {
290
+ progress: number;
291
+ artifactType: "diff";
292
+ details: HotUpdaterDiffProgressDetails;
293
+ };
301
294
 
302
295
  export type HotUpdaterEvent = {
303
296
  onProgress: HotUpdaterProgressEvent;
@@ -434,8 +427,6 @@ export const commitReleaseSelection = async (input: {
434
427
  export async function updateBundle(params: UpdateParams): Promise<boolean> {
435
428
  const updateBundleId = params.bundleId;
436
429
  const status = params.status;
437
- const targetFileUrl = params.fileUrl;
438
-
439
430
  const currentBundleId = status === "UPDATE" ? getFreshBundleId() : undefined;
440
431
 
441
432
  // If native is still on the same bundle we installed in this session,
@@ -467,11 +458,10 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
467
458
  const existing = sessionState.getInflightUpdate(updateBundleId);
468
459
  if (existing) return existing;
469
460
 
470
- const targetFileHash = params.fileHash;
471
461
  const targetChannel = params.channel;
472
462
  const targetManifestUrl = params.manifestUrl;
473
463
  const targetManifestFileHash = params.manifestFileHash;
474
- const targetChangedAssets = params.changedAssets;
464
+ const targetAssets = params.assets;
475
465
 
476
466
  const promise = (async () => {
477
467
  try {
@@ -479,12 +469,10 @@ export async function updateBundle(params: UpdateParams): Promise<boolean> {
479
469
  const ok = await HotUpdaterNative.updateBundle({
480
470
  bundleId: updateBundleId,
481
471
  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,
472
+ assets: targetAssets as Record<string, ArtifactAsset>,
473
+ manifestFileHash: targetManifestFileHash,
474
+ manifestUrl: targetManifestUrl,
475
+ ...(params.archiveUrl ? { archiveUrl: params.archiveUrl } : {}),
488
476
  ...(selection === undefined ? {} : { selection }),
489
477
  });
490
478
  if (ok) {
@@ -599,8 +587,9 @@ export const getMinBundleId = (): string => {
599
587
  return constants.MIN_BUNDLE_ID;
600
588
  };
601
589
 
602
- export const getReleaseId = async (): Promise<string | null> =>
603
- getActiveUpdateState().activeSelection?.releaseId ?? null;
590
+ /** Returns the selected update ID shown in the console, including staged updates. */
591
+ export const getUpdateId = (): string =>
592
+ getActiveUpdateState().activeSelection?.releaseId ?? getBundleId();
604
593
 
605
594
  /**
606
595
  * Fetches the current bundle version id.
@@ -687,7 +676,7 @@ export const isChannelSwitched = (): boolean => {
687
676
  */
688
677
  export const getFingerprintHash = (): string | null => {
689
678
  const constants = HotUpdaterNative.getConstants();
690
- return constants.FINGERPRINT_HASH;
679
+ return constants.FINGERPRINT_HASH ?? null;
691
680
  };
692
681
 
693
682
  export const getInstallId = (): string => {
@@ -783,7 +772,7 @@ export type NotifyAppReadyResult =
783
772
  toReleaseId?: string;
784
773
  };
785
774
 
786
- export type NotifyAppReadyAnalyticsEvent = {
775
+ export type NotifyAppReadyInsightsEvent = {
787
776
  type: "UPDATE_APPLIED" | "RECOVERED";
788
777
  fromBundleId: string;
789
778
  toBundleId: string;
@@ -891,9 +880,9 @@ const getNotifyAppReadyTransition = (
891
880
  return null;
892
881
  };
893
882
 
894
- const getNotifyAppReadyAnalyticsEvent = (
883
+ const getNotifyAppReadyInsightsEvent = (
895
884
  result: RawNotifyAppReadyResult,
896
- ): NotifyAppReadyAnalyticsEvent | null => {
885
+ ): NotifyAppReadyInsightsEvent | null => {
897
886
  const transition = getNotifyAppReadyTransition(result);
898
887
 
899
888
  if (!transition || !isPersistedUpdateStrategy(result.updateStrategy)) {
@@ -938,14 +927,14 @@ const normalizeNotifyAppReadyResult = (
938
927
 
939
928
  export const readNotifyAppReady = (): {
940
929
  result: NotifyAppReadyResult;
941
- analyticsEvent: NotifyAppReadyAnalyticsEvent | null;
930
+ insightsEvent: NotifyAppReadyInsightsEvent | null;
942
931
  pending: boolean;
943
932
  } => {
944
933
  const rawResult = readRawNotifyAppReadyResult();
945
934
 
946
935
  return {
947
936
  result: normalizeNotifyAppReadyResult(rawResult),
948
- analyticsEvent: getNotifyAppReadyAnalyticsEvent(rawResult),
937
+ insightsEvent: getNotifyAppReadyInsightsEvent(rawResult),
949
938
  pending: rawResult.status === "PENDING",
950
939
  };
951
940
  };
@@ -1,13 +1,14 @@
1
1
  import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
 
3
3
  import type {
4
- NotifyAppReadyAnalyticsEvent,
4
+ ActiveUpdateState,
5
+ NotifyAppReadyInsightsEvent,
5
6
  NotifyAppReadyResult,
6
7
  } from "./native";
7
8
  import {
8
9
  createNotifyReadResult,
9
10
  stubNotifyFrame,
10
- } from "./notifyAppReadyAnalytics.test-utils";
11
+ } from "./notifyAppReadyInsights.test-utils";
11
12
 
12
13
  const platformState = vi.hoisted((): { OS: "ios" | "android" } => ({
13
14
  OS: "ios",
@@ -42,13 +43,14 @@ const mocks = vi.hoisted(() => ({
42
43
  getAppVersion: vi.fn<() => string | undefined>(() => "1.0.0"),
43
44
  getBundleId: vi.fn<() => string | undefined>(() => "bundle-id"),
44
45
  getChannel: vi.fn(() => "production"),
46
+ getActiveUpdateState: vi.fn<() => ActiveUpdateState>(),
45
47
  getCohort: vi.fn(() => "123"),
46
48
  getFingerprintHash: vi.fn(() => "fingerprint-hash"),
47
49
  getInstallId: vi.fn<() => string | undefined>(() => "install-id"),
48
50
  getPersistedUserIdentity: vi.fn(() => ({})),
49
51
  readNotifyAppReady: vi.fn<
50
52
  () => {
51
- analyticsEvent: NotifyAppReadyAnalyticsEvent | null;
53
+ insightsEvent: NotifyAppReadyInsightsEvent | null;
52
54
  pending: boolean;
53
55
  result: NotifyAppReadyResult;
54
56
  }
@@ -65,6 +67,7 @@ vi.mock("./native", () => ({
65
67
  getAppVersion: mocks.getAppVersion,
66
68
  getBundleId: mocks.getBundleId,
67
69
  getChannel: mocks.getChannel,
70
+ getActiveUpdateState: mocks.getActiveUpdateState,
68
71
  getCohort: mocks.getCohort,
69
72
  getFingerprintHash: mocks.getFingerprintHash,
70
73
  getInstallId: mocks.getInstallId,
@@ -78,20 +81,20 @@ vi.mock("./specs/NativeHotUpdater", () => ({
78
81
  }));
79
82
 
80
83
  const createClient = () => {
81
- const sendAnalyticsEvent = vi.fn().mockResolvedValue(undefined);
84
+ const sendInsightsEvent = vi.fn().mockResolvedValue(undefined);
82
85
  return {
83
86
  client: {
84
87
  createSession: vi.fn(async () => ({
85
88
  fetchReleaseCatalog: vi.fn(),
86
89
  resolveArtifact: vi.fn(),
87
- sendAnalyticsEvent,
90
+ sendInsightsEvent,
88
91
  })),
89
92
  },
90
- sendAnalyticsEvent,
93
+ sendInsightsEvent,
91
94
  };
92
95
  };
93
96
 
94
- describe("automatic notifyAppReady analytics boundaries", () => {
97
+ describe("automatic notifyAppReady insights boundaries", () => {
95
98
  beforeEach(() => {
96
99
  vi.resetModules();
97
100
  vi.unstubAllGlobals();
@@ -106,6 +109,11 @@ describe("automatic notifyAppReady analytics boundaries", () => {
106
109
  mocks.getAppVersion.mockReturnValue("1.0.0");
107
110
  mocks.getBundleId.mockReturnValue("bundle-id");
108
111
  mocks.getChannel.mockReturnValue("production");
112
+ mocks.getActiveUpdateState.mockReturnValue({
113
+ activeSelection: null,
114
+ stableSelection: null,
115
+ verificationPending: false,
116
+ });
109
117
  mocks.getCohort.mockReturnValue("123");
110
118
  mocks.getFingerprintHash.mockReturnValue("fingerprint-hash");
111
119
  mocks.getInstallId.mockReturnValue("install-id");
@@ -124,7 +132,7 @@ describe("automatic notifyAppReady analytics boundaries", () => {
124
132
  const { init } = await import("./wrap");
125
133
 
126
134
  init({
127
- analytics: true,
135
+ insights: true,
128
136
  client: {
129
137
  createSession: vi.fn(async () => {
130
138
  throw new Error("baseURL unavailable");
@@ -138,7 +146,7 @@ describe("automatic notifyAppReady analytics boundaries", () => {
138
146
  expect(onError).not.toHaveBeenCalled();
139
147
  expect(onNotifyAppReady).toHaveBeenCalledWith({ status: "UNCHANGED" });
140
148
  expect(warn).toHaveBeenCalledWith(
141
- "[HotUpdater] Automatic notifyAppReady analytics failed:",
149
+ "[HotUpdater] Automatic notifyAppReady insights failed:",
142
150
  expect.objectContaining({
143
151
  message: "baseURL unavailable",
144
152
  }),
@@ -163,17 +171,17 @@ describe("automatic notifyAppReady analytics boundaries", () => {
163
171
  } else {
164
172
  mocks.getInstallId.mockReturnValue(value);
165
173
  }
166
- const { client, sendAnalyticsEvent } = createClient();
174
+ const { client, sendInsightsEvent } = createClient();
167
175
  const { init } = await import("./wrap");
168
176
 
169
- init({ analytics: true, client, onError, onNotifyAppReady });
177
+ init({ insights: true, client, onError, onNotifyAppReady });
170
178
  await vi.runOnlyPendingTimersAsync();
171
179
 
172
- expect(sendAnalyticsEvent).not.toHaveBeenCalled();
180
+ expect(sendInsightsEvent).not.toHaveBeenCalled();
173
181
  expect(onError).not.toHaveBeenCalled();
174
182
  expect(onNotifyAppReady).toHaveBeenCalledWith({ status: "UNCHANGED" });
175
183
  expect(warn).toHaveBeenCalledWith(
176
- "[HotUpdater] Automatic notifyAppReady analytics failed:",
184
+ "[HotUpdater] Automatic notifyAppReady insights failed:",
177
185
  expect.any(Error),
178
186
  );
179
187
  warn.mockRestore();
@@ -197,17 +205,17 @@ describe("automatic notifyAppReady analytics boundaries", () => {
197
205
  } else {
198
206
  mocks.getBundleId.mockReturnValue(value);
199
207
  }
200
- const { client, sendAnalyticsEvent } = createClient();
208
+ const { client, sendInsightsEvent } = createClient();
201
209
  const { init } = await import("./wrap");
202
210
 
203
- init({ analytics: true, client, onError, onNotifyAppReady });
211
+ init({ insights: true, client, onError, onNotifyAppReady });
204
212
  await vi.runOnlyPendingTimersAsync();
205
213
 
206
- expect(sendAnalyticsEvent).not.toHaveBeenCalled();
214
+ expect(sendInsightsEvent).not.toHaveBeenCalled();
207
215
  expect(onError).not.toHaveBeenCalled();
208
216
  expect(onNotifyAppReady).toHaveBeenCalledWith({ status: "UNCHANGED" });
209
217
  expect(warn).toHaveBeenCalledWith(
210
- "[HotUpdater] Automatic notifyAppReady analytics failed:",
218
+ "[HotUpdater] Automatic notifyAppReady insights failed:",
211
219
  expect.any(Error),
212
220
  );
213
221
  warn.mockRestore();