@hot-updater/react-native 1.0.0-rc.2 → 1.0.0-rc.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/proguard-rules.pro +17 -2
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +954 -837
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +15 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +43 -31
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
- package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
- package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
- package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
- package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
- package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
- package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
- package/android/src/newarch/HotUpdater.kt +7 -9
- package/android/src/newarch/HotUpdaterModule.kt +109 -84
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +98 -74
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +950 -1112
- package/ios/HotUpdater/Internal/BundleMetadata.swift +8 -53
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
- package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
- package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
- package/ios/HotUpdater/Internal/HotUpdater.mm +13 -45
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +190 -73
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
- package/lib/commonjs/appReady.js +111 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +125 -70
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +13 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/error.js +4 -13
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +5 -2
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +55 -92
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +68 -20
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +82 -62
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +44 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +20 -61
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/store.js +3 -10
- package/lib/commonjs/store.js.map +1 -1
- package/lib/commonjs/updateError.js +132 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +44 -27
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +103 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +118 -65
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/clientPlugin.js +4 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/error.js +4 -13
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +3 -1
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +53 -90
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +55 -22
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +74 -54
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +35 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +16 -56
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/store.js +3 -10
- package/lib/module/store.js.map +1 -1
- package/lib/module/updateError.js +126 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +45 -28
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +23 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/commonjs/error.d.ts +4 -13
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +3 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +3 -32
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +7 -12
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +33 -23
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +6 -14
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +42 -47
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/store.d.ts +1 -3
- package/lib/typescript/commonjs/store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/updateError.d.ts +26 -0
- package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +23 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +16 -4
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +2 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/module/error.d.ts +4 -13
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +3 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +3 -32
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +7 -12
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +33 -23
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +4 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +6 -14
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +42 -47
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/store.d.ts +1 -3
- package/lib/typescript/module/store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/updateError.d.ts +26 -0
- package/lib/typescript/module/updateError.d.ts.map +1 -0
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +6 -7
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +7 -4
- package/src/appReady.ts +149 -0
- package/src/checkForUpdate.ts +152 -89
- package/src/clientPlugin.ts +17 -0
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +5 -1
- package/src/httpClient.ts +78 -157
- package/src/index.ts +87 -29
- package/src/native.ts +109 -104
- package/src/pluginHost.ts +45 -0
- package/src/releaseCatalogCache.ts +26 -140
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +41 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +153 -0
- package/src/wrap.tsx +59 -28
- package/src/wrap.types.ts +6 -7
- package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
- package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
- package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
- package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
- package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
- package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
- package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
- package/lib/commonjs/catalogCacheNative.spec.js +0 -31
- package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -604
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/commonjs/httpClient.spec.js +0 -102
- package/lib/commonjs/httpClient.spec.js.map +0 -1
- package/lib/commonjs/index.spec.js +0 -298
- package/lib/commonjs/index.spec.js.map +0 -1
- package/lib/commonjs/iosInterop.spec.js +0 -18
- package/lib/commonjs/iosInterop.spec.js.map +0 -1
- package/lib/commonjs/native.spec.js +0 -795
- package/lib/commonjs/native.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -133
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
- package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/commonjs/store.notification.spec.js +0 -76
- package/lib/commonjs/store.notification.spec.js.map +0 -1
- package/lib/commonjs/store.spec.js +0 -316
- package/lib/commonjs/store.spec.js.map +0 -1
- package/lib/commonjs/wrap.spec.js +0 -296
- package/lib/commonjs/wrap.spec.js.map +0 -1
- package/lib/module/catalogCacheNative.spec.js +0 -31
- package/lib/module/catalogCacheNative.spec.js.map +0 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -603
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/module/httpClient.spec.js +0 -102
- package/lib/module/httpClient.spec.js.map +0 -1
- package/lib/module/index.spec.js +0 -297
- package/lib/module/index.spec.js.map +0 -1
- package/lib/module/iosInterop.spec.js +0 -18
- package/lib/module/iosInterop.spec.js.map +0 -1
- package/lib/module/native.spec.js +0 -794
- package/lib/module/native.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -128
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -245
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/releaseCatalogCache.spec.js +0 -195
- package/lib/module/releaseCatalogCache.spec.js.map +0 -1
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/module/store.notification.spec.js +0 -77
- package/lib/module/store.notification.spec.js.map +0 -1
- package/lib/module/store.spec.js +0 -315
- package/lib/module/store.spec.js.map +0 -1
- package/lib/module/wrap.spec.js +0 -295
- package/lib/module/wrap.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/src/catalogCacheNative.spec.ts +0 -63
- package/src/checkForUpdate.releaseCatalog.spec.ts +0 -596
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -331
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -855
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
- package/src/notifyAppReadyInsights.spec.ts +0 -243
- package/src/notifyAppReadyInsights.test-utils.ts +0 -31
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
- package/src/notifyAppReadyInsights.ts +0 -194
- package/src/releaseCatalogCache.spec.ts +0 -282
- package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
- package/src/store.notification.spec.ts +0 -92
- package/src/store.spec.ts +0 -372
- package/src/wrap.spec.ts +0 -367
package/src/native.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type
|
|
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/
|
|
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<
|
|
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(
|
|
109
|
+
getInflightUpdate(
|
|
110
|
+
bundleId: string,
|
|
111
|
+
): Promise<BundleDelivery | null> | undefined {
|
|
107
112
|
return this.inflightUpdates.get(bundleId);
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
trackInflightUpdate(
|
|
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 = "
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
498
|
+
const targetAssets = params.assets;
|
|
475
499
|
|
|
476
500
|
const promise = (async () => {
|
|
477
501
|
try {
|
|
478
502
|
const selection = params.selection;
|
|
479
|
-
const
|
|
503
|
+
const staged = await HotUpdaterNative.updateBundle({
|
|
480
504
|
bundleId: updateBundleId,
|
|
481
505
|
channel: targetChannel,
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
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
|
-
|
|
491
|
-
|
|
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
|
-
|
|
603
|
-
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
|
|
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
|
-
|
|
742
|
-
export
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
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
|
|
791
|
-
toReleaseId
|
|
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
|
|
896
|
+
const getLaunchTransition = (
|
|
895
897
|
result: RawNotifyAppReadyResult,
|
|
896
|
-
):
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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/
|
|
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 =
|
|
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
|
|
177
|
+
throw new UpdateHttpError(response.status, response.statusText);
|
|
296
178
|
}
|
|
297
179
|
|
|
298
180
|
const body = await response.text();
|
|
299
|
-
const catalog =
|
|
181
|
+
const catalog = parseReleaseCatalog(body, input.expectedScope);
|
|
300
182
|
if (catalog === null) {
|
|
301
183
|
await removeNativeReleaseCatalogCache(partition);
|
|
302
|
-
throw new
|
|
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.
|
|
1
|
+
export const HOT_UPDATER_SDK_VERSION = "1.0.0-rc.22";
|