@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41
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/build.gradle +13 -2
- package/android/proguard-rules.pro +8 -2
- package/android/src/bridge/BridgeReactInstance.kt +38 -0
- package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
- package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -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 +46 -30
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
- 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/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/newarch/JavaScriptExceptionHooks.kt +48 -0
- package/android/src/newarch/ReactIntegrationManager.kt +6 -32
- 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/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
- package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
- 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 +32 -67
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -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 +96 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +121 -69
- 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 +5 -14
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +16 -27
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/fetchUpdateResponse.js +60 -0
- package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
- package/lib/commonjs/httpClient.js +64 -95
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +355 -324
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/init.types.js +6 -0
- package/lib/commonjs/init.types.js.map +1 -0
- package/lib/commonjs/native.js +77 -63
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +41 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +36 -87
- 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 +145 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +46 -38
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +90 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +114 -64
- 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 +5 -14
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +14 -26
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/fetchUpdateResponse.js +55 -0
- package/lib/module/fetchUpdateResponse.js.map +1 -0
- package/lib/module/httpClient.js +63 -94
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +335 -327
- package/lib/module/index.js.map +1 -1
- package/lib/module/init.types.js +4 -0
- package/lib/module/init.types.js.map +1 -0
- package/lib/module/native.js +70 -56
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +36 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +32 -82
- 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 +139 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +47 -38
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +30 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
- 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 +5 -14
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +5 -34
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +124 -116
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/init.types.d.ts +21 -0
- package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
- package/lib/typescript/commonjs/native.d.ts +32 -25
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -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 +35 -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 +2 -3
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +30 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +20 -6
- 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 +5 -14
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +5 -5
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +5 -34
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +124 -116
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/init.types.d.ts +21 -0
- package/lib/typescript/module/init.types.d.ts.map +1 -0
- package/lib/typescript/module/native.d.ts +32 -25
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +8 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +8 -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 +35 -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 +2 -3
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +13 -41
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/appReady.ts +147 -0
- package/src/checkForUpdate.ts +156 -89
- package/src/clientPlugin.ts +21 -0
- package/src/error.ts +5 -15
- package/src/fetchJSON.ts +14 -30
- package/src/fetchUpdateResponse.ts +60 -0
- package/src/httpClient.ts +89 -160
- package/src/index.ts +413 -425
- package/src/init.types.ts +24 -0
- package/src/native.ts +101 -105
- package/src/pluginHost.ts +52 -0
- package/src/releaseCatalogCache.ts +43 -172
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +167 -0
- package/src/wrap.tsx +60 -43
- package/src/wrap.types.ts +18 -46
- 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 -616
- 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 -302
- 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 -883
- 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 -329
- 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 -615
- 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 -301
- 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 -882
- 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 -330
- 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 -612
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -337
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -945
- 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 -406
package/src/checkForUpdate.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
authorizeReleaseTransition,
|
|
3
|
+
canonicalizeAppVersion,
|
|
3
4
|
createReleaseSelectionContextHash,
|
|
4
5
|
encodeChannelKey,
|
|
6
|
+
type ExpectedReleaseCatalogScope,
|
|
7
|
+
hasExpectedReleaseCatalogScope,
|
|
5
8
|
selectDesiredRelease,
|
|
6
|
-
type ArtifactInfo,
|
|
7
9
|
type PersistedSelectionReceipt,
|
|
8
10
|
type ReleaseCatalog,
|
|
9
|
-
} from "@hot-updater/
|
|
10
|
-
import { canonicalizeAppVersion } from "@hot-updater/plugin-core";
|
|
11
|
+
} from "@hot-updater/protocol";
|
|
11
12
|
import { Platform } from "react-native";
|
|
12
13
|
|
|
14
|
+
import { getRunningReleaseId, type LaunchReporter } from "./appReady";
|
|
15
|
+
import type {
|
|
16
|
+
ReleaseTransitionKind,
|
|
17
|
+
UpdateErrorResource,
|
|
18
|
+
UpdateErrorStage,
|
|
19
|
+
} from "./clientPlugin";
|
|
13
20
|
import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
|
|
14
|
-
import type { HotUpdaterHttpClient
|
|
21
|
+
import type { HotUpdaterHttpClient } from "./httpClient";
|
|
15
22
|
import {
|
|
16
23
|
acceptReleaseCatalog,
|
|
17
24
|
commitReleaseSelection,
|
|
@@ -22,26 +29,22 @@ import {
|
|
|
22
29
|
getCohort,
|
|
23
30
|
getDefaultChannel,
|
|
24
31
|
getFingerprintHash,
|
|
25
|
-
getInstallId,
|
|
26
32
|
getMinBundleId,
|
|
27
33
|
getCrashHistory,
|
|
28
|
-
getPersistedUserIdentity,
|
|
29
34
|
isReleaseSelectionCurrent,
|
|
30
35
|
isChannelSwitched,
|
|
31
|
-
|
|
36
|
+
stageBundle,
|
|
32
37
|
} from "./native";
|
|
33
|
-
import {
|
|
34
|
-
hasExpectedReleaseCatalogScope,
|
|
35
|
-
type ExpectedReleaseCatalogScope,
|
|
36
|
-
} from "./releaseCatalogCache";
|
|
37
38
|
import { hotUpdaterStore } from "./store";
|
|
39
|
+
import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
|
|
40
|
+
|
|
41
|
+
export type { ReleaseTransitionKind };
|
|
38
42
|
|
|
39
43
|
export interface CheckForUpdateOptions {
|
|
40
44
|
/**
|
|
41
45
|
* Update strategy
|
|
42
46
|
* - "fingerprint": Use fingerprint hash to check for updates
|
|
43
47
|
* - "appVersion": Use app version to check for updates
|
|
44
|
-
* - Can override the strategy set in HotUpdater.wrap()
|
|
45
48
|
*/
|
|
46
49
|
updateStrategy: "appVersion" | "fingerprint";
|
|
47
50
|
|
|
@@ -60,7 +63,7 @@ export interface CheckForUpdateOptions {
|
|
|
60
63
|
requestTimeout?: number;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
export type CheckForUpdateResult =
|
|
66
|
+
export type CheckForUpdateResult = {
|
|
64
67
|
readonly id: string;
|
|
65
68
|
readonly message: string | null;
|
|
66
69
|
readonly rolloutCohortCount: number;
|
|
@@ -71,20 +74,15 @@ export type CheckForUpdateResult = ArtifactInfo & {
|
|
|
71
74
|
readonly transitionKind?: ReleaseTransitionKind;
|
|
72
75
|
/**
|
|
73
76
|
* Updates the bundle.
|
|
74
|
-
* This method is equivalent to `
|
|
77
|
+
* This method is equivalent to `hotUpdater.updateBundle()` but with all required arguments pre-filled.
|
|
75
78
|
*/
|
|
76
79
|
updateBundle: () => Promise<boolean>;
|
|
77
80
|
};
|
|
78
81
|
|
|
79
|
-
export type ReleaseTransitionKind =
|
|
80
|
-
| "INSTALL"
|
|
81
|
-
| "ADOPT_RELEASE"
|
|
82
|
-
| "USE_EMBEDDED"
|
|
83
|
-
| "USE_BUILTIN";
|
|
84
|
-
|
|
85
82
|
export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
|
|
86
|
-
insights?: boolean;
|
|
87
83
|
client: HotUpdaterHttpClient;
|
|
84
|
+
/** Calls the checking instance's plugin hooks. */
|
|
85
|
+
emit: LaunchReporter["emit"];
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
const sameReceipt = (
|
|
@@ -113,7 +111,7 @@ const validateCatalog = (
|
|
|
113
111
|
!/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
|
|
114
112
|
!hasExpectedReleaseCatalogScope(catalog, expectedScope)
|
|
115
113
|
) {
|
|
116
|
-
throw new
|
|
114
|
+
throw new InvalidUpdateResponseError("Received an invalid Release catalog");
|
|
117
115
|
}
|
|
118
116
|
};
|
|
119
117
|
|
|
@@ -121,47 +119,49 @@ const resetProgress = () => {
|
|
|
121
119
|
hotUpdaterStore.setState({
|
|
122
120
|
artifactType: null,
|
|
123
121
|
details: null,
|
|
124
|
-
downloadedBytes: undefined,
|
|
125
122
|
isUpdateDownloaded: false,
|
|
126
123
|
progress: 0,
|
|
127
|
-
totalBytes: undefined,
|
|
128
124
|
});
|
|
129
125
|
};
|
|
130
126
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
readonly
|
|
134
|
-
readonly
|
|
135
|
-
readonly
|
|
136
|
-
readonly
|
|
137
|
-
readonly
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
...(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
127
|
+
/** The running bundle, the channel checked, and the update's target. */
|
|
128
|
+
type UpdateErrorScope = {
|
|
129
|
+
readonly channel: string;
|
|
130
|
+
readonly bundleId: string;
|
|
131
|
+
readonly updateStrategy: "fingerprint" | "appVersion";
|
|
132
|
+
readonly targetBundleId?: string;
|
|
133
|
+
readonly targetReleaseId?: string | null;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Tells plugins why an update failed, when it failed as an update. An error
|
|
138
|
+
* the SDK's JavaScript raised happened in `stage`, fetching `resource`.
|
|
139
|
+
*/
|
|
140
|
+
export const reportUpdateError = (
|
|
141
|
+
emit: LaunchReporter["emit"],
|
|
142
|
+
error: unknown,
|
|
143
|
+
stage: UpdateErrorStage,
|
|
144
|
+
resource: UpdateErrorResource | undefined,
|
|
145
|
+
scope: UpdateErrorScope,
|
|
146
|
+
): void => {
|
|
147
|
+
emit("onUpdateError", () => {
|
|
148
|
+
const classification = classifyUpdateError(error, stage, resource);
|
|
149
|
+
if (classification === null) return null;
|
|
150
|
+
return {
|
|
151
|
+
...classification,
|
|
152
|
+
channel: scope.channel,
|
|
153
|
+
bundleId: scope.bundleId,
|
|
154
|
+
releaseId: getRunningReleaseId(scope.bundleId, getChannel()),
|
|
155
|
+
updateStrategy: scope.updateStrategy,
|
|
156
|
+
...(scope.targetBundleId === undefined
|
|
157
|
+
? {}
|
|
158
|
+
: { targetBundleId: scope.targetBundleId }),
|
|
159
|
+
...(scope.targetReleaseId == null
|
|
160
|
+
? {}
|
|
161
|
+
: { targetReleaseId: scope.targetReleaseId }),
|
|
162
|
+
cause: error,
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
async function checkForReleaseCatalogUpdate(input: {
|
|
@@ -212,12 +212,15 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
212
212
|
requestTimeout: options.requestTimeout,
|
|
213
213
|
updateStrategy: options.updateStrategy,
|
|
214
214
|
});
|
|
215
|
+
// The server says this scope has no catalog yet: no update, not a failure.
|
|
216
|
+
if (catalog === null) return null;
|
|
215
217
|
validateCatalog(catalog, expectedScope);
|
|
216
218
|
const catalogId = catalog.catalogId;
|
|
217
219
|
const scopeKey = catalog.scopeKey;
|
|
218
220
|
|
|
219
221
|
const crashedBundleIds = getCrashHistory();
|
|
220
|
-
const
|
|
222
|
+
const activeState = getActiveUpdateState();
|
|
223
|
+
const active = activeState.activeSelection;
|
|
221
224
|
const explicitScopeSwitch =
|
|
222
225
|
input.explicitChannel !== undefined &&
|
|
223
226
|
input.explicitChannel !== input.currentChannel;
|
|
@@ -264,7 +267,7 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
264
267
|
scopeKey,
|
|
265
268
|
})
|
|
266
269
|
) {
|
|
267
|
-
throw new
|
|
270
|
+
throw new InvalidUpdateResponseError(
|
|
268
271
|
"Rejected a stale or inconsistent Release catalog",
|
|
269
272
|
);
|
|
270
273
|
}
|
|
@@ -328,7 +331,20 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
328
331
|
scopeKey,
|
|
329
332
|
selectionContextHash,
|
|
330
333
|
};
|
|
331
|
-
const
|
|
334
|
+
const fromReleaseId =
|
|
335
|
+
active?.bundleId === input.currentBundleId
|
|
336
|
+
? active.releaseId
|
|
337
|
+
: activeState.stableSelection?.bundleId === input.currentBundleId
|
|
338
|
+
? activeState.stableSelection.releaseId
|
|
339
|
+
: null;
|
|
340
|
+
const errorScope = {
|
|
341
|
+
bundleId: input.currentBundleId,
|
|
342
|
+
channel: input.targetChannel,
|
|
343
|
+
targetBundleId: desired.bundleId,
|
|
344
|
+
targetReleaseId: desired.releaseId,
|
|
345
|
+
updateStrategy: options.updateStrategy,
|
|
346
|
+
};
|
|
347
|
+
const installSelection = async (): Promise<boolean> => {
|
|
332
348
|
resetProgress();
|
|
333
349
|
if (!isReleaseSelectionCurrent(guard)) {
|
|
334
350
|
throw new StaleReleaseCatalogError();
|
|
@@ -339,18 +355,14 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
339
355
|
guard,
|
|
340
356
|
selection: receipt,
|
|
341
357
|
});
|
|
342
|
-
if (committed && transitionKind === "ADOPT_RELEASE"
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
requestHeaders: options.requestHeaders,
|
|
351
|
-
requestTimeout: options.requestTimeout,
|
|
352
|
-
session,
|
|
353
|
-
});
|
|
358
|
+
if (committed && transitionKind === "ADOPT_RELEASE") {
|
|
359
|
+
options.emit("onUpdateCheck", () => ({
|
|
360
|
+
status: "UNCHANGED",
|
|
361
|
+
channel: receipt.channel,
|
|
362
|
+
bundleId: receipt.bundleId,
|
|
363
|
+
releaseId: receipt.releaseId,
|
|
364
|
+
previousReleaseId: active?.releaseId ?? null,
|
|
365
|
+
}));
|
|
354
366
|
}
|
|
355
367
|
return committed;
|
|
356
368
|
}
|
|
@@ -364,33 +376,70 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
364
376
|
if (!isReleaseSelectionCurrent(guard)) {
|
|
365
377
|
throw new StaleReleaseCatalogError();
|
|
366
378
|
}
|
|
367
|
-
|
|
379
|
+
const delivery = await stageBundle({
|
|
380
|
+
assets: artifact.assets,
|
|
368
381
|
bundleId: desired.bundleId,
|
|
369
|
-
changedAssets: artifact.changedAssets ?? null,
|
|
370
382
|
channel: input.targetChannel,
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
manifestUrl: artifact.manifestUrl ?? null,
|
|
383
|
+
manifestFileHash: artifact.manifestFileHash,
|
|
384
|
+
manifestUrl: artifact.manifestUrl,
|
|
385
|
+
...(artifact.archiveUrl ? { archiveUrl: artifact.archiveUrl } : {}),
|
|
375
386
|
selection: receipt,
|
|
376
387
|
shouldSkipCurrentBundleIdCheck: true,
|
|
377
388
|
status: desired.status,
|
|
378
389
|
});
|
|
390
|
+
if (delivery !== null && desired.bundleId !== input.currentBundleId) {
|
|
391
|
+
options.emit("onBundleDownloaded", () => ({
|
|
392
|
+
channel: input.targetChannel,
|
|
393
|
+
fromBundleId: input.currentBundleId,
|
|
394
|
+
fromReleaseId,
|
|
395
|
+
toBundleId: desired.bundleId,
|
|
396
|
+
toReleaseId: desired.releaseId,
|
|
397
|
+
updateStrategy: options.updateStrategy,
|
|
398
|
+
...delivery,
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
return true;
|
|
402
|
+
};
|
|
403
|
+
const updateBundleForSelection = async (): Promise<boolean> => {
|
|
404
|
+
try {
|
|
405
|
+
return await installSelection();
|
|
406
|
+
} catch (error) {
|
|
407
|
+
reportUpdateError(
|
|
408
|
+
options.emit,
|
|
409
|
+
error,
|
|
410
|
+
"download",
|
|
411
|
+
"artifact",
|
|
412
|
+
errorScope,
|
|
413
|
+
);
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
379
416
|
};
|
|
380
417
|
|
|
418
|
+
const shouldForceUpdate =
|
|
419
|
+
transitionKind === "ADOPT_RELEASE"
|
|
420
|
+
? false
|
|
421
|
+
: desired.status === "ROLLBACK"
|
|
422
|
+
? true
|
|
423
|
+
: (release?.shouldForceUpdate ?? false);
|
|
424
|
+
options.emit("onUpdateCheck", () => ({
|
|
425
|
+
status: "UPDATE_AVAILABLE",
|
|
426
|
+
channel: input.targetChannel,
|
|
427
|
+
fromBundleId: input.currentBundleId,
|
|
428
|
+
fromReleaseId,
|
|
429
|
+
toBundleId: desired.bundleId,
|
|
430
|
+
toReleaseId: desired.releaseId,
|
|
431
|
+
transitionKind,
|
|
432
|
+
updateStatus: desired.status,
|
|
433
|
+
shouldForceUpdate,
|
|
434
|
+
updateStrategy: options.updateStrategy,
|
|
435
|
+
}));
|
|
436
|
+
|
|
381
437
|
return {
|
|
382
|
-
fileHash: null,
|
|
383
|
-
fileUrl: null,
|
|
384
438
|
id: desired.releaseId ?? desired.bundleId,
|
|
385
439
|
message: release?.message ?? null,
|
|
386
440
|
releaseId: desired.releaseId,
|
|
387
441
|
rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
|
|
388
|
-
shouldForceUpdate
|
|
389
|
-
transitionKind === "ADOPT_RELEASE"
|
|
390
|
-
? false
|
|
391
|
-
: desired.status === "ROLLBACK"
|
|
392
|
-
? true
|
|
393
|
-
: (release?.shouldForceUpdate ?? false),
|
|
442
|
+
shouldForceUpdate,
|
|
394
443
|
status: desired.status,
|
|
395
444
|
targetCohorts: release?.targetCohorts ? [...release.targetCohorts] : [],
|
|
396
445
|
transitionKind,
|
|
@@ -430,7 +479,7 @@ export async function checkForUpdate(
|
|
|
430
479
|
|
|
431
480
|
if (isSwitched && explicitChannel && explicitChannel !== currentChannel) {
|
|
432
481
|
const error = new HotUpdaterError(
|
|
433
|
-
`Runtime channel is already switched to "${currentChannel}". Call
|
|
482
|
+
`Runtime channel is already switched to "${currentChannel}". Call hotUpdater.resetChannel() before checking "${explicitChannel}".`,
|
|
434
483
|
);
|
|
435
484
|
options.onError?.(error);
|
|
436
485
|
throw error;
|
|
@@ -439,7 +488,7 @@ export async function checkForUpdate(
|
|
|
439
488
|
const fingerprintHash = getFingerprintHash();
|
|
440
489
|
|
|
441
490
|
try {
|
|
442
|
-
|
|
491
|
+
const result = await checkForReleaseCatalogUpdate({
|
|
443
492
|
cohort,
|
|
444
493
|
currentAppVersion,
|
|
445
494
|
currentBundleId,
|
|
@@ -453,7 +502,25 @@ export async function checkForUpdate(
|
|
|
453
502
|
platform,
|
|
454
503
|
targetChannel,
|
|
455
504
|
});
|
|
505
|
+
if (result === null) {
|
|
506
|
+
options.emit("onUpdateCheck", () => {
|
|
507
|
+
const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
|
|
508
|
+
return {
|
|
509
|
+
status: "UNCHANGED",
|
|
510
|
+
channel: currentChannel,
|
|
511
|
+
bundleId: currentBundleId,
|
|
512
|
+
releaseId,
|
|
513
|
+
previousReleaseId: releaseId,
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return result;
|
|
456
518
|
} catch (error) {
|
|
519
|
+
reportUpdateError(options.emit, error, "check", "catalog", {
|
|
520
|
+
bundleId: currentBundleId,
|
|
521
|
+
channel: targetChannel,
|
|
522
|
+
updateStrategy: options.updateStrategy,
|
|
523
|
+
});
|
|
457
524
|
options.onError?.(error as Error);
|
|
458
525
|
return null;
|
|
459
526
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
defineClientPlugin,
|
|
3
|
+
type AppReadyResult,
|
|
4
|
+
type BundleDownloadedInfo,
|
|
5
|
+
type ClientPluginApi,
|
|
6
|
+
type ClientPluginApis,
|
|
7
|
+
type HotUpdaterClientContext,
|
|
8
|
+
type HotUpdaterClientHooks,
|
|
9
|
+
type HotUpdaterClientPlugin,
|
|
10
|
+
type HotUpdaterClientSetup,
|
|
11
|
+
type HotUpdaterClientStorage,
|
|
12
|
+
type ReleaseTransitionKind,
|
|
13
|
+
type UpdateCheckResult,
|
|
14
|
+
type UpdateError,
|
|
15
|
+
type UpdateErrorReason,
|
|
16
|
+
type UpdateErrorResource,
|
|
17
|
+
type UpdateErrorStage,
|
|
18
|
+
type UpdateErrorTransport,
|
|
19
|
+
type UpdateHttpResponse,
|
|
20
|
+
type UpdateStrategy,
|
|
21
|
+
} from "@hot-updater/protocol";
|
package/src/error.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Error Classification:
|
|
9
9
|
* - Parameter Validation: Invalid or missing function parameters
|
|
10
|
-
* - Bundle Storage: Errors during download,
|
|
10
|
+
* - Bundle Storage: Errors during download, verification, and storage
|
|
11
11
|
* - Signature Verification: Cryptographic verification failures (collapsed to a single public code)
|
|
12
12
|
* - Internal: Platform-specific or unexpected errors
|
|
13
13
|
*
|
|
@@ -27,8 +27,7 @@ export enum HotUpdaterErrorCode {
|
|
|
27
27
|
MISSING_BUNDLE_ID = "MISSING_BUNDLE_ID",
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
* Thrown when fileUrl parameter cannot be parsed as a valid URL.
|
|
30
|
+
* Manifest or asset URL is invalid or malformed.
|
|
32
31
|
* @retryable false
|
|
33
32
|
*/
|
|
34
33
|
INVALID_FILE_URL = "INVALID_FILE_URL",
|
|
@@ -58,25 +57,16 @@ export enum HotUpdaterErrorCode {
|
|
|
58
57
|
*/
|
|
59
58
|
INCOMPLETE_DOWNLOAD = "INCOMPLETE_DOWNLOAD",
|
|
60
59
|
|
|
61
|
-
/**
|
|
62
|
-
* Bundle archive format is invalid or corrupted.
|
|
63
|
-
* Thrown when ZIP file has wrong magic bytes, invalid structure, or unsupported format.
|
|
64
|
-
* Also thrown for path traversal attempts during extraction.
|
|
65
|
-
* @retryable false - Indicates corrupted or malicious bundle
|
|
66
|
-
*/
|
|
67
|
-
EXTRACTION_FORMAT_ERROR = "EXTRACTION_FORMAT_ERROR",
|
|
68
|
-
|
|
69
60
|
/**
|
|
70
61
|
* Bundle missing required platform files.
|
|
71
|
-
* Thrown when
|
|
62
|
+
* Thrown when the staged bundle doesn't contain index.android.bundle (Android)
|
|
72
63
|
* or main.jsbundle (iOS).
|
|
73
64
|
* @retryable false - Indicates incorrectly built bundle
|
|
74
65
|
*/
|
|
75
66
|
INVALID_BUNDLE = "INVALID_BUNDLE",
|
|
76
67
|
|
|
77
68
|
/**
|
|
78
|
-
* Insufficient disk space for bundle
|
|
79
|
-
* Thrown when available disk space is less than required (file size * 2).
|
|
69
|
+
* Insufficient disk space for bundle staging.
|
|
80
70
|
* Error message includes required and available bytes.
|
|
81
71
|
* @retryable false - User must free up disk space
|
|
82
72
|
*/
|
|
@@ -102,7 +92,7 @@ export enum HotUpdaterErrorCode {
|
|
|
102
92
|
/**
|
|
103
93
|
* Bundle is in crashed history and cannot be applied.
|
|
104
94
|
* Thrown when attempting to install a bundle that previously caused a crash.
|
|
105
|
-
* Use
|
|
95
|
+
* Use hotUpdater.clearCrashHistory() to allow retrying this bundle.
|
|
106
96
|
* @retryable false - Bundle was marked as crashed for safety
|
|
107
97
|
*/
|
|
108
98
|
BUNDLE_IN_CRASHED_HISTORY = "BUNDLE_IN_CRASHED_HISTORY",
|
package/src/fetchJSON.ts
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
requestHeaders,
|
|
4
|
-
requestTimeout = 5000,
|
|
5
|
-
}: {
|
|
6
|
-
url: string;
|
|
7
|
-
requestHeaders?: Record<string, string>;
|
|
8
|
-
requestTimeout?: number;
|
|
9
|
-
}): Promise<T> => {
|
|
10
|
-
const controller = new AbortController();
|
|
11
|
-
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
1
|
+
import { fetchUpdateResponse, type UpdateRequest } from "./fetchUpdateResponse";
|
|
2
|
+
import { UpdateHttpError } from "./updateError";
|
|
12
3
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return (await response.json()) as T;
|
|
25
|
-
} catch (error: unknown) {
|
|
26
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
27
|
-
throw new Error("Request timed out");
|
|
28
|
-
}
|
|
29
|
-
throw error;
|
|
30
|
-
} finally {
|
|
31
|
-
clearTimeout(timeoutId);
|
|
4
|
+
export class FetchJSONResponseError extends UpdateHttpError {}
|
|
5
|
+
|
|
6
|
+
export const fetchJSON = async <T>(
|
|
7
|
+
input: Omit<UpdateRequest, "resource">,
|
|
8
|
+
): Promise<T> => {
|
|
9
|
+
const { response, body } = await fetchUpdateResponse({
|
|
10
|
+
...input,
|
|
11
|
+
resource: "artifact",
|
|
12
|
+
});
|
|
13
|
+
if (response.status !== 200) {
|
|
14
|
+
throw new FetchJSONResponseError(response.status, response.statusText);
|
|
32
15
|
}
|
|
16
|
+
return JSON.parse(body!) as T;
|
|
33
17
|
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { UpdateHttpResponse } from "@hot-updater/protocol";
|
|
2
|
+
|
|
3
|
+
export interface UpdateRequest {
|
|
4
|
+
readonly url: string;
|
|
5
|
+
readonly resource: UpdateHttpResponse["resource"];
|
|
6
|
+
readonly requestHeaders?: Record<string, string>;
|
|
7
|
+
readonly requestTimeout?: number;
|
|
8
|
+
readonly onResponse?: (response: UpdateHttpResponse) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Read once for parsing and diagnostics, under the same request timeout. */
|
|
12
|
+
export const fetchUpdateResponse = async ({
|
|
13
|
+
url,
|
|
14
|
+
resource,
|
|
15
|
+
requestHeaders,
|
|
16
|
+
requestTimeout = 5000,
|
|
17
|
+
onResponse,
|
|
18
|
+
}: UpdateRequest): Promise<{ response: Response; body: string | null }> => {
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
let timedOut = false;
|
|
21
|
+
const timeoutId = setTimeout(() => {
|
|
22
|
+
timedOut = true;
|
|
23
|
+
controller.abort();
|
|
24
|
+
}, requestTimeout);
|
|
25
|
+
try {
|
|
26
|
+
const headers = new Headers(requestHeaders);
|
|
27
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
headers,
|
|
30
|
+
signal: controller.signal,
|
|
31
|
+
});
|
|
32
|
+
let body: string | null = null;
|
|
33
|
+
try {
|
|
34
|
+
body = await response.text();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// A broken error body must not hide an HTTP error already received.
|
|
37
|
+
if (response.status === 200) throw error;
|
|
38
|
+
} finally {
|
|
39
|
+
onResponse?.({
|
|
40
|
+
resource,
|
|
41
|
+
// React Native URL implementations do not all expose pathname.
|
|
42
|
+
path:
|
|
43
|
+
url.replace(/^https?:\/\/[^/?#]*/i, "").split(/[?#]/, 1)[0] || "/",
|
|
44
|
+
status: response.status,
|
|
45
|
+
body,
|
|
46
|
+
bodyTruncated: false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return { response, body };
|
|
50
|
+
} catch (error: unknown) {
|
|
51
|
+
// Whatever the fetch rejects with once its timeout aborts it: an
|
|
52
|
+
// AbortError, or Expo's "fetch failed: FetchRequestCanceledException".
|
|
53
|
+
if (timedOut || (error instanceof Error && error.name === "AbortError")) {
|
|
54
|
+
throw new Error("Request timed out");
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
} finally {
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
}
|
|
60
|
+
};
|