@hot-updater/react-native 1.0.0-rc.3 → 1.0.0-rc.31
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 +124 -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 +4 -13
- 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 +54 -0
- package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
- package/lib/commonjs/httpClient.js +63 -94
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +71 -18
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +78 -60
- 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 +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 +132 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +43 -26
- 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 +117 -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 +4 -13
- 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 +49 -0
- package/lib/module/fetchUpdateResponse.js.map +1 -0
- package/lib/module/httpClient.js +62 -93
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +58 -20
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +71 -53
- 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 +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 +126 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +43 -26
- 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 +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 +4 -33
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -8
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +31 -22
- 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 +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 +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 +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 +4 -33
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -8
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +31 -22
- 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 +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 +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 +151 -88
- package/src/clientPlugin.ts +18 -0
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +14 -30
- package/src/fetchUpdateResponse.ts +54 -0
- package/src/httpClient.ts +88 -159
- package/src/index.ts +90 -24
- package/src/native.ts +106 -102
- package/src/pluginHost.ts +45 -0
- package/src/releaseCatalogCache.ts +43 -172
- 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 +57 -26
- 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 -615
- 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 -614
- 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 -611
- 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/appReady.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { AppReadyResult } from "./clientPlugin";
|
|
2
|
+
import type { HotUpdaterError } from "./error";
|
|
3
|
+
import {
|
|
4
|
+
getActiveUpdateState,
|
|
5
|
+
getBundleId,
|
|
6
|
+
getChannel,
|
|
7
|
+
type LaunchTransition,
|
|
8
|
+
type NotifyAppReadyResult,
|
|
9
|
+
readNotifyAppReady,
|
|
10
|
+
} from "./native";
|
|
11
|
+
import {
|
|
12
|
+
buildPluginEvent,
|
|
13
|
+
dispatchPluginHook,
|
|
14
|
+
emitPluginHook,
|
|
15
|
+
} from "./pluginHost";
|
|
16
|
+
|
|
17
|
+
export type NotifyAppReadyOptions = {
|
|
18
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
19
|
+
onError?: (error: HotUpdaterError | Error | unknown) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type RequestAnimationFrame = (callback: (timestamp: number) => void) => number;
|
|
23
|
+
|
|
24
|
+
let didEmitAppReady = false;
|
|
25
|
+
/** Settles once this runtime's launch was read, whether it was reported or failed. */
|
|
26
|
+
let appReadyRead: Promise<unknown> | null = null;
|
|
27
|
+
/** Why the previous main process exited, read with this runtime's launch. */
|
|
28
|
+
let previousProcessExit: string | null = null;
|
|
29
|
+
|
|
30
|
+
/** Android 11+: why the app's previous main process exited, once the launch was read. */
|
|
31
|
+
export const getPreviousProcessExit = (): string | null => previousProcessExit;
|
|
32
|
+
|
|
33
|
+
const waitForNextFrame = () =>
|
|
34
|
+
new Promise<void>((resolve) => {
|
|
35
|
+
const requestAnimationFrame = (
|
|
36
|
+
globalThis as typeof globalThis & {
|
|
37
|
+
requestAnimationFrame?: RequestAnimationFrame;
|
|
38
|
+
}
|
|
39
|
+
)?.requestAnimationFrame;
|
|
40
|
+
|
|
41
|
+
if (requestAnimationFrame) {
|
|
42
|
+
requestAnimationFrame(() => resolve());
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void Promise.resolve().then(resolve);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The Release the running bundle was selected as: the active or stable
|
|
51
|
+
* selection that names it on the current channel.
|
|
52
|
+
*/
|
|
53
|
+
export const getRunningReleaseId = (
|
|
54
|
+
bundleId: string,
|
|
55
|
+
channel: string,
|
|
56
|
+
): string | null => {
|
|
57
|
+
const state = getActiveUpdateState();
|
|
58
|
+
return (
|
|
59
|
+
[state.activeSelection, state.stableSelection].find(
|
|
60
|
+
(selection) =>
|
|
61
|
+
selection?.bundleId === bundleId && selection.channel === channel,
|
|
62
|
+
)?.releaseId ?? null
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const toAppReadyResult = (
|
|
67
|
+
result: NotifyAppReadyResult,
|
|
68
|
+
transition: LaunchTransition | null,
|
|
69
|
+
): AppReadyResult | null => {
|
|
70
|
+
const channel = getChannel();
|
|
71
|
+
if (result.status === "UNCHANGED") {
|
|
72
|
+
const bundleId = getBundleId();
|
|
73
|
+
return {
|
|
74
|
+
status: "UNCHANGED",
|
|
75
|
+
channel,
|
|
76
|
+
bundleId,
|
|
77
|
+
releaseId: getRunningReleaseId(bundleId, channel),
|
|
78
|
+
previousProcessExit,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Native persists the transition with every applied or recovered launch;
|
|
82
|
+
// a report without it cannot say what moved.
|
|
83
|
+
if (transition === null) return null;
|
|
84
|
+
return {
|
|
85
|
+
status: transition.type,
|
|
86
|
+
channel,
|
|
87
|
+
fromBundleId: transition.fromBundleId,
|
|
88
|
+
fromReleaseId: transition.fromReleaseId,
|
|
89
|
+
toBundleId: transition.toBundleId,
|
|
90
|
+
toReleaseId: transition.toReleaseId,
|
|
91
|
+
updateStrategy: transition.updateStrategy,
|
|
92
|
+
previousProcessExit,
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const notifyAppReady = async (
|
|
97
|
+
options: NotifyAppReadyOptions,
|
|
98
|
+
): Promise<NotifyAppReadyResult | undefined> => {
|
|
99
|
+
try {
|
|
100
|
+
let nativeReadResult: ReturnType<typeof readNotifyAppReady>;
|
|
101
|
+
do {
|
|
102
|
+
await waitForNextFrame();
|
|
103
|
+
nativeReadResult = readNotifyAppReady();
|
|
104
|
+
} while (nativeReadResult.pending);
|
|
105
|
+
|
|
106
|
+
const { result, transition } = nativeReadResult;
|
|
107
|
+
previousProcessExit = nativeReadResult.previousProcessExit;
|
|
108
|
+
if (!didEmitAppReady) {
|
|
109
|
+
didEmitAppReady = true;
|
|
110
|
+
emitPluginHook("onAppReady", () => toAppReadyResult(result, transition));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
options.onNotifyAppReady?.(result);
|
|
114
|
+
return result;
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const normalizedError =
|
|
117
|
+
error instanceof Error ? error : new Error(String(error));
|
|
118
|
+
options.onError?.(error);
|
|
119
|
+
console.warn("[HotUpdater] Failed to notify app ready:", normalizedError);
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** Reads this runtime's launch once native finalizes it and reports it to plugins. */
|
|
125
|
+
export const handleNotifyAppReady = (
|
|
126
|
+
options: NotifyAppReadyOptions,
|
|
127
|
+
): Promise<NotifyAppReadyResult | undefined> => {
|
|
128
|
+
const readiness = notifyAppReady(options);
|
|
129
|
+
appReadyRead ??= readiness;
|
|
130
|
+
return readiness;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Calls a plugin hook after this runtime's `onAppReady`, so plugins see the
|
|
135
|
+
* launch before anything that followed it. The event is built now, from the
|
|
136
|
+
* state it describes, and only when a plugin listens.
|
|
137
|
+
*/
|
|
138
|
+
export const emitAfterAppReady: typeof emitPluginHook = (
|
|
139
|
+
name,
|
|
140
|
+
createPayload,
|
|
141
|
+
) => {
|
|
142
|
+
const payload = buildPluginEvent(name, createPayload);
|
|
143
|
+
if (payload === null) return;
|
|
144
|
+
if (appReadyRead === null) {
|
|
145
|
+
dispatchPluginHook(name, payload);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
void appReadyRead.then(() => dispatchPluginHook(name, payload));
|
|
149
|
+
};
|
package/src/checkForUpdate.ts
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
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 {
|
|
15
|
+
emitAfterAppReady,
|
|
16
|
+
getPreviousProcessExit,
|
|
17
|
+
getRunningReleaseId,
|
|
18
|
+
} from "./appReady";
|
|
19
|
+
import type {
|
|
20
|
+
ReleaseTransitionKind,
|
|
21
|
+
UpdateErrorResource,
|
|
22
|
+
UpdateErrorStage,
|
|
23
|
+
} from "./clientPlugin";
|
|
13
24
|
import { HotUpdaterError, StaleReleaseCatalogError } from "./error";
|
|
14
|
-
import type { HotUpdaterHttpClient
|
|
25
|
+
import type { HotUpdaterHttpClient } from "./httpClient";
|
|
15
26
|
import {
|
|
16
27
|
acceptReleaseCatalog,
|
|
17
28
|
commitReleaseSelection,
|
|
@@ -22,19 +33,16 @@ import {
|
|
|
22
33
|
getCohort,
|
|
23
34
|
getDefaultChannel,
|
|
24
35
|
getFingerprintHash,
|
|
25
|
-
getInstallId,
|
|
26
36
|
getMinBundleId,
|
|
27
37
|
getCrashHistory,
|
|
28
|
-
getPersistedUserIdentity,
|
|
29
38
|
isReleaseSelectionCurrent,
|
|
30
39
|
isChannelSwitched,
|
|
31
|
-
|
|
40
|
+
stageBundle,
|
|
32
41
|
} from "./native";
|
|
33
|
-
import {
|
|
34
|
-
hasExpectedReleaseCatalogScope,
|
|
35
|
-
type ExpectedReleaseCatalogScope,
|
|
36
|
-
} from "./releaseCatalogCache";
|
|
37
42
|
import { hotUpdaterStore } from "./store";
|
|
43
|
+
import { classifyUpdateError, InvalidUpdateResponseError } from "./updateError";
|
|
44
|
+
|
|
45
|
+
export type { ReleaseTransitionKind };
|
|
38
46
|
|
|
39
47
|
export interface CheckForUpdateOptions {
|
|
40
48
|
/**
|
|
@@ -60,7 +68,7 @@ export interface CheckForUpdateOptions {
|
|
|
60
68
|
requestTimeout?: number;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
|
-
export type CheckForUpdateResult =
|
|
71
|
+
export type CheckForUpdateResult = {
|
|
64
72
|
readonly id: string;
|
|
65
73
|
readonly message: string | null;
|
|
66
74
|
readonly rolloutCohortCount: number;
|
|
@@ -76,14 +84,7 @@ export type CheckForUpdateResult = ArtifactInfo & {
|
|
|
76
84
|
updateBundle: () => Promise<boolean>;
|
|
77
85
|
};
|
|
78
86
|
|
|
79
|
-
export type ReleaseTransitionKind =
|
|
80
|
-
| "INSTALL"
|
|
81
|
-
| "ADOPT_RELEASE"
|
|
82
|
-
| "USE_EMBEDDED"
|
|
83
|
-
| "USE_BUILTIN";
|
|
84
|
-
|
|
85
87
|
export interface InternalCheckForUpdateOptions extends CheckForUpdateOptions {
|
|
86
|
-
insights?: boolean;
|
|
87
88
|
client: HotUpdaterHttpClient;
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -113,7 +114,7 @@ const validateCatalog = (
|
|
|
113
114
|
!/^sha256:[0-9a-f]{64}$/.test(catalog.catalogHash) ||
|
|
114
115
|
!hasExpectedReleaseCatalogScope(catalog, expectedScope)
|
|
115
116
|
) {
|
|
116
|
-
throw new
|
|
117
|
+
throw new InvalidUpdateResponseError("Received an invalid Release catalog");
|
|
117
118
|
}
|
|
118
119
|
};
|
|
119
120
|
|
|
@@ -121,48 +122,50 @@ const resetProgress = () => {
|
|
|
121
122
|
hotUpdaterStore.setState({
|
|
122
123
|
artifactType: null,
|
|
123
124
|
details: null,
|
|
124
|
-
downloadedBytes: undefined,
|
|
125
125
|
isUpdateDownloaded: false,
|
|
126
126
|
progress: 0,
|
|
127
|
-
totalBytes: undefined,
|
|
128
127
|
});
|
|
129
128
|
};
|
|
130
129
|
|
|
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
|
-
|
|
165
|
-
|
|
130
|
+
/** The running bundle, the channel checked, and the update's target. */
|
|
131
|
+
type UpdateErrorScope = {
|
|
132
|
+
readonly channel: string;
|
|
133
|
+
readonly bundleId: string;
|
|
134
|
+
readonly updateStrategy: "fingerprint" | "appVersion";
|
|
135
|
+
readonly targetBundleId?: string;
|
|
136
|
+
readonly targetReleaseId?: string | null;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Tells plugins why an update failed, when it failed as an update. An error
|
|
141
|
+
* the SDK's JavaScript raised happened in `stage`, fetching `resource`.
|
|
142
|
+
*/
|
|
143
|
+
export const reportUpdateError = (
|
|
144
|
+
error: unknown,
|
|
145
|
+
stage: UpdateErrorStage,
|
|
146
|
+
resource: UpdateErrorResource | undefined,
|
|
147
|
+
scope: UpdateErrorScope,
|
|
148
|
+
): void => {
|
|
149
|
+
emitAfterAppReady("onUpdateError", () => {
|
|
150
|
+
const classification = classifyUpdateError(error, stage, resource);
|
|
151
|
+
if (classification === null) return null;
|
|
152
|
+
const previousProcessExit = getPreviousProcessExit();
|
|
153
|
+
return {
|
|
154
|
+
...classification,
|
|
155
|
+
...(previousProcessExit === null ? {} : { previousProcessExit }),
|
|
156
|
+
channel: scope.channel,
|
|
157
|
+
bundleId: scope.bundleId,
|
|
158
|
+
releaseId: getRunningReleaseId(scope.bundleId, getChannel()),
|
|
159
|
+
updateStrategy: scope.updateStrategy,
|
|
160
|
+
...(scope.targetBundleId === undefined
|
|
161
|
+
? {}
|
|
162
|
+
: { targetBundleId: scope.targetBundleId }),
|
|
163
|
+
...(scope.targetReleaseId == null
|
|
164
|
+
? {}
|
|
165
|
+
: { targetReleaseId: scope.targetReleaseId }),
|
|
166
|
+
cause: error,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
166
169
|
};
|
|
167
170
|
|
|
168
171
|
async function checkForReleaseCatalogUpdate(input: {
|
|
@@ -213,12 +216,15 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
213
216
|
requestTimeout: options.requestTimeout,
|
|
214
217
|
updateStrategy: options.updateStrategy,
|
|
215
218
|
});
|
|
219
|
+
// The server says this scope has no catalog yet: no update, not a failure.
|
|
220
|
+
if (catalog === null) return null;
|
|
216
221
|
validateCatalog(catalog, expectedScope);
|
|
217
222
|
const catalogId = catalog.catalogId;
|
|
218
223
|
const scopeKey = catalog.scopeKey;
|
|
219
224
|
|
|
220
225
|
const crashedBundleIds = getCrashHistory();
|
|
221
|
-
const
|
|
226
|
+
const activeState = getActiveUpdateState();
|
|
227
|
+
const active = activeState.activeSelection;
|
|
222
228
|
const explicitScopeSwitch =
|
|
223
229
|
input.explicitChannel !== undefined &&
|
|
224
230
|
input.explicitChannel !== input.currentChannel;
|
|
@@ -265,7 +271,7 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
265
271
|
scopeKey,
|
|
266
272
|
})
|
|
267
273
|
) {
|
|
268
|
-
throw new
|
|
274
|
+
throw new InvalidUpdateResponseError(
|
|
269
275
|
"Rejected a stale or inconsistent Release catalog",
|
|
270
276
|
);
|
|
271
277
|
}
|
|
@@ -329,7 +335,20 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
329
335
|
scopeKey,
|
|
330
336
|
selectionContextHash,
|
|
331
337
|
};
|
|
332
|
-
const
|
|
338
|
+
const fromReleaseId =
|
|
339
|
+
active?.bundleId === input.currentBundleId
|
|
340
|
+
? active.releaseId
|
|
341
|
+
: activeState.stableSelection?.bundleId === input.currentBundleId
|
|
342
|
+
? activeState.stableSelection.releaseId
|
|
343
|
+
: null;
|
|
344
|
+
const errorScope = {
|
|
345
|
+
bundleId: input.currentBundleId,
|
|
346
|
+
channel: input.targetChannel,
|
|
347
|
+
targetBundleId: desired.bundleId,
|
|
348
|
+
targetReleaseId: desired.releaseId,
|
|
349
|
+
updateStrategy: options.updateStrategy,
|
|
350
|
+
};
|
|
351
|
+
const installSelection = async (): Promise<boolean> => {
|
|
333
352
|
resetProgress();
|
|
334
353
|
if (!isReleaseSelectionCurrent(guard)) {
|
|
335
354
|
throw new StaleReleaseCatalogError();
|
|
@@ -340,19 +359,14 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
340
359
|
guard,
|
|
341
360
|
selection: receipt,
|
|
342
361
|
});
|
|
343
|
-
if (committed && transitionKind === "ADOPT_RELEASE"
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
requestHeaders: options.requestHeaders,
|
|
352
|
-
requestTimeout: options.requestTimeout,
|
|
353
|
-
session,
|
|
354
|
-
updateStrategy: options.updateStrategy,
|
|
355
|
-
});
|
|
362
|
+
if (committed && transitionKind === "ADOPT_RELEASE") {
|
|
363
|
+
emitAfterAppReady("onUpdateCheck", () => ({
|
|
364
|
+
status: "UNCHANGED",
|
|
365
|
+
channel: receipt.channel,
|
|
366
|
+
bundleId: receipt.bundleId,
|
|
367
|
+
releaseId: receipt.releaseId,
|
|
368
|
+
previousReleaseId: active?.releaseId ?? null,
|
|
369
|
+
}));
|
|
356
370
|
}
|
|
357
371
|
return committed;
|
|
358
372
|
}
|
|
@@ -366,33 +380,64 @@ async function checkForReleaseCatalogUpdate(input: {
|
|
|
366
380
|
if (!isReleaseSelectionCurrent(guard)) {
|
|
367
381
|
throw new StaleReleaseCatalogError();
|
|
368
382
|
}
|
|
369
|
-
|
|
383
|
+
const delivery = await stageBundle({
|
|
384
|
+
assets: artifact.assets,
|
|
370
385
|
bundleId: desired.bundleId,
|
|
371
|
-
changedAssets: artifact.changedAssets ?? null,
|
|
372
386
|
channel: input.targetChannel,
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
manifestUrl: artifact.manifestUrl ?? null,
|
|
387
|
+
manifestFileHash: artifact.manifestFileHash,
|
|
388
|
+
manifestUrl: artifact.manifestUrl,
|
|
389
|
+
...(artifact.archiveUrl ? { archiveUrl: artifact.archiveUrl } : {}),
|
|
377
390
|
selection: receipt,
|
|
378
391
|
shouldSkipCurrentBundleIdCheck: true,
|
|
379
392
|
status: desired.status,
|
|
380
393
|
});
|
|
394
|
+
if (delivery !== null && desired.bundleId !== input.currentBundleId) {
|
|
395
|
+
emitAfterAppReady("onBundleDownloaded", () => ({
|
|
396
|
+
channel: input.targetChannel,
|
|
397
|
+
fromBundleId: input.currentBundleId,
|
|
398
|
+
fromReleaseId,
|
|
399
|
+
toBundleId: desired.bundleId,
|
|
400
|
+
toReleaseId: desired.releaseId,
|
|
401
|
+
updateStrategy: options.updateStrategy,
|
|
402
|
+
...delivery,
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
405
|
+
return true;
|
|
406
|
+
};
|
|
407
|
+
const updateBundleForSelection = async (): Promise<boolean> => {
|
|
408
|
+
try {
|
|
409
|
+
return await installSelection();
|
|
410
|
+
} catch (error) {
|
|
411
|
+
reportUpdateError(error, "download", "artifact", errorScope);
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
381
414
|
};
|
|
382
415
|
|
|
416
|
+
const shouldForceUpdate =
|
|
417
|
+
transitionKind === "ADOPT_RELEASE"
|
|
418
|
+
? false
|
|
419
|
+
: desired.status === "ROLLBACK"
|
|
420
|
+
? true
|
|
421
|
+
: (release?.shouldForceUpdate ?? false);
|
|
422
|
+
emitAfterAppReady("onUpdateCheck", () => ({
|
|
423
|
+
status: "UPDATE_AVAILABLE",
|
|
424
|
+
channel: input.targetChannel,
|
|
425
|
+
fromBundleId: input.currentBundleId,
|
|
426
|
+
fromReleaseId,
|
|
427
|
+
toBundleId: desired.bundleId,
|
|
428
|
+
toReleaseId: desired.releaseId,
|
|
429
|
+
transitionKind,
|
|
430
|
+
updateStatus: desired.status,
|
|
431
|
+
shouldForceUpdate,
|
|
432
|
+
updateStrategy: options.updateStrategy,
|
|
433
|
+
}));
|
|
434
|
+
|
|
383
435
|
return {
|
|
384
|
-
fileHash: null,
|
|
385
|
-
fileUrl: null,
|
|
386
436
|
id: desired.releaseId ?? desired.bundleId,
|
|
387
437
|
message: release?.message ?? null,
|
|
388
438
|
releaseId: desired.releaseId,
|
|
389
439
|
rolloutCohortCount: release?.rolloutCohortCount ?? 1000,
|
|
390
|
-
shouldForceUpdate
|
|
391
|
-
transitionKind === "ADOPT_RELEASE"
|
|
392
|
-
? false
|
|
393
|
-
: desired.status === "ROLLBACK"
|
|
394
|
-
? true
|
|
395
|
-
: (release?.shouldForceUpdate ?? false),
|
|
440
|
+
shouldForceUpdate,
|
|
396
441
|
status: desired.status,
|
|
397
442
|
targetCohorts: release?.targetCohorts ? [...release.targetCohorts] : [],
|
|
398
443
|
transitionKind,
|
|
@@ -441,7 +486,7 @@ export async function checkForUpdate(
|
|
|
441
486
|
const fingerprintHash = getFingerprintHash();
|
|
442
487
|
|
|
443
488
|
try {
|
|
444
|
-
|
|
489
|
+
const result = await checkForReleaseCatalogUpdate({
|
|
445
490
|
cohort,
|
|
446
491
|
currentAppVersion,
|
|
447
492
|
currentBundleId,
|
|
@@ -455,7 +500,25 @@ export async function checkForUpdate(
|
|
|
455
500
|
platform,
|
|
456
501
|
targetChannel,
|
|
457
502
|
});
|
|
503
|
+
if (result === null) {
|
|
504
|
+
emitAfterAppReady("onUpdateCheck", () => {
|
|
505
|
+
const releaseId = getRunningReleaseId(currentBundleId, currentChannel);
|
|
506
|
+
return {
|
|
507
|
+
status: "UNCHANGED",
|
|
508
|
+
channel: currentChannel,
|
|
509
|
+
bundleId: currentBundleId,
|
|
510
|
+
releaseId,
|
|
511
|
+
previousReleaseId: releaseId,
|
|
512
|
+
};
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
return result;
|
|
458
516
|
} catch (error) {
|
|
517
|
+
reportUpdateError(error, "check", "catalog", {
|
|
518
|
+
bundleId: currentBundleId,
|
|
519
|
+
channel: targetChannel,
|
|
520
|
+
updateStrategy: options.updateStrategy,
|
|
521
|
+
});
|
|
459
522
|
options.onError?.(error as Error);
|
|
460
523
|
return null;
|
|
461
524
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
defineClientPlugin,
|
|
3
|
+
type AppReadyResult,
|
|
4
|
+
type BundleDownloadedInfo,
|
|
5
|
+
type HotUpdaterClientContext,
|
|
6
|
+
type HotUpdaterClientHooks,
|
|
7
|
+
type HotUpdaterClientPlugin,
|
|
8
|
+
type HotUpdaterClientStorage,
|
|
9
|
+
type ReleaseTransitionKind,
|
|
10
|
+
type UpdateCheckResult,
|
|
11
|
+
type UpdateError,
|
|
12
|
+
type UpdateErrorReason,
|
|
13
|
+
type UpdateErrorResource,
|
|
14
|
+
type UpdateErrorStage,
|
|
15
|
+
type UpdateErrorTransport,
|
|
16
|
+
type UpdateHttpResponse,
|
|
17
|
+
type UpdateStrategy,
|
|
18
|
+
} 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
|
*/
|
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,54 @@
|
|
|
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
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
21
|
+
try {
|
|
22
|
+
const headers = new Headers(requestHeaders);
|
|
23
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
headers,
|
|
26
|
+
signal: controller.signal,
|
|
27
|
+
});
|
|
28
|
+
let body: string | null = null;
|
|
29
|
+
try {
|
|
30
|
+
body = await response.text();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// A broken error body must not hide an HTTP error already received.
|
|
33
|
+
if (response.status === 200) throw error;
|
|
34
|
+
} finally {
|
|
35
|
+
onResponse?.({
|
|
36
|
+
resource,
|
|
37
|
+
// React Native URL implementations do not all expose pathname.
|
|
38
|
+
path:
|
|
39
|
+
url.replace(/^https?:\/\/[^/?#]*/i, "").split(/[?#]/, 1)[0] || "/",
|
|
40
|
+
status: response.status,
|
|
41
|
+
body,
|
|
42
|
+
bodyTruncated: false,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return { response, body };
|
|
46
|
+
} catch (error: unknown) {
|
|
47
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
48
|
+
throw new Error("Request timed out");
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
} finally {
|
|
52
|
+
clearTimeout(timeoutId);
|
|
53
|
+
}
|
|
54
|
+
};
|