@hot-updater/react-native 1.0.0-rc.2 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -2,48 +2,21 @@ import type { TurboModule } from "react-native";
|
|
|
2
2
|
import { TurboModuleRegistry } from "react-native";
|
|
3
3
|
import type { UnsafeObject } from "react-native/Libraries/Types/CodegenTypes";
|
|
4
4
|
|
|
5
|
-
export interface ChangedAsset {
|
|
6
|
-
file?: {
|
|
7
|
-
compression?: "br" | null;
|
|
8
|
-
url: string;
|
|
9
|
-
} | null;
|
|
10
|
-
fileHash: string;
|
|
11
|
-
patch?: {
|
|
12
|
-
algorithm: "bsdiff";
|
|
13
|
-
baseBundleId: string;
|
|
14
|
-
baseFileHash: string;
|
|
15
|
-
patchFileHash: string;
|
|
16
|
-
patchUrl: string;
|
|
17
|
-
} | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
5
|
export interface UpdateBundleParams {
|
|
21
6
|
bundleId: string;
|
|
22
7
|
channel?: string;
|
|
23
|
-
fileUrl: string | null;
|
|
24
8
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* Format depends on signing configuration:
|
|
28
|
-
* - Signed: `sig:<base64_signature>` - Native will verify signature (and implicitly hash)
|
|
29
|
-
* - Unsigned: `<hex_hash>` - Native will verify SHA256 hash only
|
|
30
|
-
*
|
|
31
|
-
* Native determines verification mode by checking for "sig:" prefix.
|
|
9
|
+
* Signed manifest URL for installation.
|
|
32
10
|
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Optional signed manifest URL for manifest-driven installation.
|
|
36
|
-
*/
|
|
37
|
-
manifestUrl?: string | null;
|
|
11
|
+
manifestUrl: string;
|
|
38
12
|
/**
|
|
39
13
|
* File hash/signature for the manifest file itself.
|
|
40
14
|
*/
|
|
41
|
-
manifestFileHash
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
changedAssets?: UnsafeObject | null;
|
|
15
|
+
manifestFileHash: string;
|
|
16
|
+
/** Optional tar.br URL; integrity and sizes come from the verified manifest. */
|
|
17
|
+
archiveUrl?: string | null;
|
|
18
|
+
/** Full protocol v1 target file descriptor map. */
|
|
19
|
+
assets: UnsafeObject;
|
|
47
20
|
/** Full Release Catalog selection receipt committed with the staged Bundle. */
|
|
48
21
|
selection?: UnsafeObject | null;
|
|
49
22
|
}
|
|
@@ -61,18 +34,21 @@ export interface Spec extends TurboModule {
|
|
|
61
34
|
* Downloads and applies a bundle update.
|
|
62
35
|
*
|
|
63
36
|
* @param params - Update bundle parameters
|
|
64
|
-
* @returns Promise that resolves to
|
|
37
|
+
* @returns Promise that resolves, once the bundle is staged, to how it
|
|
38
|
+
* arrived: `{ delivery, patchFallback }`. `delivery` is "patch" when a
|
|
39
|
+
* bsdiff patch produced a file, "manifest" when only changed files were
|
|
40
|
+
* downloaded, or "archive" for the full archive; `patchFallback` is true
|
|
41
|
+
* when a patch was tried but the file or archive was downloaded instead.
|
|
65
42
|
* @throws {HotUpdaterErrorCode} Rejects with one of the following error codes:
|
|
66
43
|
*
|
|
67
44
|
* Parameter validation:
|
|
68
45
|
* - MISSING_BUNDLE_ID: Missing or empty bundleId
|
|
69
|
-
* - INVALID_FILE_URL: Invalid
|
|
46
|
+
* - INVALID_FILE_URL: Invalid manifest or asset URL provided
|
|
70
47
|
*
|
|
71
48
|
* Bundle storage:
|
|
72
49
|
* - DIRECTORY_CREATION_FAILED: Failed to create bundle directory
|
|
73
50
|
* - DOWNLOAD_FAILED: Failed to download bundle
|
|
74
51
|
* - INCOMPLETE_DOWNLOAD: Download incomplete (size mismatch)
|
|
75
|
-
* - EXTRACTION_FORMAT_ERROR: Invalid or corrupted archive format
|
|
76
52
|
* - INVALID_BUNDLE: Bundle missing required platform files
|
|
77
53
|
* - INSUFFICIENT_DISK_SPACE: Insufficient disk space
|
|
78
54
|
* - MOVE_OPERATION_FAILED: Failed to move bundle files
|
|
@@ -87,8 +63,20 @@ export interface Spec extends TurboModule {
|
|
|
87
63
|
*
|
|
88
64
|
* Note: iOS normalizes rare signature/storage errors to SIGNATURE_VERIFICATION_FAILED
|
|
89
65
|
* or UNKNOWN_ERROR to keep the JS error surface small.
|
|
66
|
+
*
|
|
67
|
+
* A rejection from the download or install also carries `userInfo`
|
|
68
|
+
* `{ stage, reason, resource?, httpStatus?, transport?, originCode? }`:
|
|
69
|
+
* `stage` is "download" (transfer and verification) or "install" (patch,
|
|
70
|
+
* extract, move into place); `reason` is one of "network", "http",
|
|
71
|
+
* "invalid_response", "hash_mismatch", "signature", "patch", "extract",
|
|
72
|
+
* "storage", or "unknown"; `resource` is what was being fetched or
|
|
73
|
+
* applied: "manifest", "file", "patch", or "archive". `httpStatus` is
|
|
74
|
+
* present when `reason` is "http", with `originCode`, the storage
|
|
75
|
+
* origin's XML error `<Code>` when the body names one. `transport` is
|
|
76
|
+
* present when no response arrived: "timeout", "dns", "tls",
|
|
77
|
+
* "connection", "offline", or "cancelled".
|
|
90
78
|
*/
|
|
91
|
-
updateBundle(params: UpdateBundleParams): Promise<
|
|
79
|
+
updateBundle(params: UpdateBundleParams): Promise<UnsafeObject>;
|
|
92
80
|
|
|
93
81
|
/** Accepts and durably advances the catalog high-water for a scope. */
|
|
94
82
|
acceptReleaseCatalog(params: UnsafeObject): boolean;
|
|
@@ -126,6 +114,13 @@ export interface Spec extends TurboModule {
|
|
|
126
114
|
toBundleId?: string;
|
|
127
115
|
updateStrategy?: "fingerprint" | "appVersion";
|
|
128
116
|
crashedBundleId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Android 11+: why the app's previous main process exited, the
|
|
119
|
+
* `ApplicationExitInfo` reason without its `REASON_` prefix (for
|
|
120
|
+
* example "CRASH", "ANR", "LOW_MEMORY", "USER_REQUESTED"), read once
|
|
121
|
+
* when the process starts. Absent on iOS and older Android.
|
|
122
|
+
*/
|
|
123
|
+
previousProcessExit?: string;
|
|
129
124
|
};
|
|
130
125
|
|
|
131
126
|
/**
|
|
@@ -192,24 +187,22 @@ export interface Spec extends TurboModule {
|
|
|
192
187
|
getCohort: () => string;
|
|
193
188
|
|
|
194
189
|
/**
|
|
195
|
-
* Gets the
|
|
190
|
+
* Gets the install id: a random id native creates once per app
|
|
191
|
+
* installation and keeps out of device backups.
|
|
196
192
|
*/
|
|
197
193
|
getInstallId: () => string;
|
|
198
194
|
|
|
199
195
|
/**
|
|
200
|
-
*
|
|
201
|
-
|
|
202
|
-
getUserId: () => string | null;
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Gets the persisted nullable username associated with this installation.
|
|
196
|
+
* Reads a value from the SDK's persistent key-value store, which native
|
|
197
|
+
* keeps out of device backups. Returns null when the key has no value.
|
|
206
198
|
*/
|
|
207
|
-
|
|
199
|
+
getStorageItem: (key: string) => string | null;
|
|
208
200
|
|
|
209
201
|
/**
|
|
210
|
-
*
|
|
202
|
+
* Writes a value to the SDK's persistent key-value store; null removes the
|
|
203
|
+
* key.
|
|
211
204
|
*/
|
|
212
|
-
|
|
205
|
+
setStorageItem: (key: string, value: string | null) => void;
|
|
213
206
|
|
|
214
207
|
// EventEmitter
|
|
215
208
|
addListener(eventName: string): void;
|
package/src/store.ts
CHANGED
|
@@ -9,10 +9,8 @@ import { addListener } from "./native";
|
|
|
9
9
|
|
|
10
10
|
export type HotUpdaterState = {
|
|
11
11
|
progress: number;
|
|
12
|
-
downloadedBytes: number | undefined;
|
|
13
|
-
totalBytes: number | undefined;
|
|
14
12
|
isUpdateDownloaded: boolean;
|
|
15
|
-
artifactType: "
|
|
13
|
+
artifactType: "diff" | null;
|
|
16
14
|
details: HotUpdaterDiffProgressDetails | null;
|
|
17
15
|
};
|
|
18
16
|
|
|
@@ -56,8 +54,6 @@ const areDiffDetailsEqual = (
|
|
|
56
54
|
const areStatesEqual = (left: HotUpdaterState, right: HotUpdaterState) => {
|
|
57
55
|
return (
|
|
58
56
|
left.progress === right.progress &&
|
|
59
|
-
left.downloadedBytes === right.downloadedBytes &&
|
|
60
|
-
left.totalBytes === right.totalBytes &&
|
|
61
57
|
left.isUpdateDownloaded === right.isUpdateDownloaded &&
|
|
62
58
|
left.artifactType === right.artifactType &&
|
|
63
59
|
areDiffDetailsEqual(left.details, right.details)
|
|
@@ -78,8 +74,6 @@ const normalizeDownloadPath = (value: string | undefined) => {
|
|
|
78
74
|
const createHotUpdaterStore = () => {
|
|
79
75
|
let state: HotUpdaterState = {
|
|
80
76
|
progress: 0,
|
|
81
|
-
downloadedBytes: undefined,
|
|
82
|
-
totalBytes: undefined,
|
|
83
77
|
isUpdateDownloaded: false,
|
|
84
78
|
artifactType: null,
|
|
85
79
|
details: null,
|
|
@@ -165,10 +159,6 @@ const createHotUpdaterStore = () => {
|
|
|
165
159
|
nextState.details = state.details;
|
|
166
160
|
}
|
|
167
161
|
|
|
168
|
-
if (nextState.artifactType !== "diff") {
|
|
169
|
-
nextState.details = null;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
162
|
if (areStatesEqual(state, nextState)) {
|
|
173
163
|
return;
|
|
174
164
|
}
|
|
@@ -180,19 +170,8 @@ const createHotUpdaterStore = () => {
|
|
|
180
170
|
const applyProgressEvent = (event: HotUpdaterProgressEvent) => {
|
|
181
171
|
setState({
|
|
182
172
|
artifactType: event.artifactType,
|
|
183
|
-
details:
|
|
184
|
-
event.artifactType === "diff"
|
|
185
|
-
? normalizeDiffDetails(event.details)
|
|
186
|
-
: null,
|
|
187
|
-
downloadedBytes:
|
|
188
|
-
event.artifactType === "archive"
|
|
189
|
-
? normalizeByteCount(event.downloadedBytes)
|
|
190
|
-
: undefined,
|
|
173
|
+
details: normalizeDiffDetails(event.details),
|
|
191
174
|
progress: event.progress,
|
|
192
|
-
totalBytes:
|
|
193
|
-
event.artifactType === "archive"
|
|
194
|
-
? normalizeByteCount(event.totalBytes)
|
|
195
|
-
: undefined,
|
|
196
175
|
});
|
|
197
176
|
};
|
|
198
177
|
|
package/src/types.ts
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
UpdateError,
|
|
3
|
+
UpdateErrorReason,
|
|
4
|
+
UpdateErrorResource,
|
|
5
|
+
UpdateErrorStage,
|
|
6
|
+
UpdateErrorTransport,
|
|
7
|
+
} from "./clientPlugin";
|
|
8
|
+
import { HotUpdaterError } from "./error";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A response the SDK cannot use: a malformed or stale catalog, an artifact
|
|
12
|
+
* in another protocol, or an invalid URL.
|
|
13
|
+
*/
|
|
14
|
+
export class InvalidUpdateResponseError extends HotUpdaterError {
|
|
15
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "InvalidUpdateResponseError";
|
|
18
|
+
if (options?.cause !== undefined) this.cause = options.cause;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A request the server answered with a status the SDK does not accept. */
|
|
23
|
+
export class UpdateHttpError extends HotUpdaterError {
|
|
24
|
+
constructor(
|
|
25
|
+
readonly status: number,
|
|
26
|
+
statusText: string,
|
|
27
|
+
) {
|
|
28
|
+
super(
|
|
29
|
+
`Request failed with HTTP ${status}${statusText ? ` ${statusText}` : ""}`,
|
|
30
|
+
);
|
|
31
|
+
this.name = "UpdateHttpError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const STAGES: readonly UpdateErrorStage[] = ["check", "download", "install"];
|
|
36
|
+
const REASONS: readonly UpdateErrorReason[] = [
|
|
37
|
+
"network",
|
|
38
|
+
"http",
|
|
39
|
+
"invalid_response",
|
|
40
|
+
"hash_mismatch",
|
|
41
|
+
"signature",
|
|
42
|
+
"patch",
|
|
43
|
+
"extract",
|
|
44
|
+
"storage",
|
|
45
|
+
"unknown",
|
|
46
|
+
];
|
|
47
|
+
const RESOURCES: readonly UpdateErrorResource[] = [
|
|
48
|
+
"catalog",
|
|
49
|
+
"artifact",
|
|
50
|
+
"manifest",
|
|
51
|
+
"file",
|
|
52
|
+
"patch",
|
|
53
|
+
"archive",
|
|
54
|
+
];
|
|
55
|
+
const TRANSPORTS: readonly UpdateErrorTransport[] = [
|
|
56
|
+
"timeout",
|
|
57
|
+
"dns",
|
|
58
|
+
"tls",
|
|
59
|
+
"connection",
|
|
60
|
+
"offline",
|
|
61
|
+
"cancelled",
|
|
62
|
+
];
|
|
63
|
+
/** An S3, R2, or GCS error code: letters, digits, and punctuation only. */
|
|
64
|
+
const ORIGIN_CODE = /^[A-Za-z0-9._-]{1,64}$/;
|
|
65
|
+
|
|
66
|
+
type Classification = Pick<
|
|
67
|
+
UpdateError,
|
|
68
|
+
"stage" | "reason" | "resource" | "httpStatus" | "originCode" | "transport"
|
|
69
|
+
>;
|
|
70
|
+
|
|
71
|
+
const oneOf = <T extends string>(
|
|
72
|
+
values: readonly T[],
|
|
73
|
+
value: unknown,
|
|
74
|
+
): value is T => values.includes(value as T);
|
|
75
|
+
|
|
76
|
+
const readNativeClassification = (
|
|
77
|
+
error: object,
|
|
78
|
+
): Classification | null | undefined => {
|
|
79
|
+
if (!("code" in error) || typeof error.code !== "string") return undefined;
|
|
80
|
+
const userInfo =
|
|
81
|
+
"userInfo" in error && typeof error.userInfo === "object"
|
|
82
|
+
? (error.userInfo as Record<string, unknown> | null)
|
|
83
|
+
: null;
|
|
84
|
+
const { stage, reason, resource, transport, originCode } = userInfo ?? {};
|
|
85
|
+
// Native classifies every failure of its download and install; a rejection
|
|
86
|
+
// without it, such as a stale selection, is not an update failure.
|
|
87
|
+
if (!oneOf(STAGES, stage) || !oneOf(REASONS, reason)) return null;
|
|
88
|
+
const httpStatus = Number(userInfo?.httpStatus);
|
|
89
|
+
return {
|
|
90
|
+
stage,
|
|
91
|
+
reason,
|
|
92
|
+
...(oneOf(RESOURCES, resource) ? { resource } : {}),
|
|
93
|
+
...(reason === "http" && Number.isSafeInteger(httpStatus)
|
|
94
|
+
? { httpStatus }
|
|
95
|
+
: {}),
|
|
96
|
+
...(reason === "http" &&
|
|
97
|
+
typeof originCode === "string" &&
|
|
98
|
+
ORIGIN_CODE.test(originCode)
|
|
99
|
+
? { originCode }
|
|
100
|
+
: {}),
|
|
101
|
+
...(reason === "network" && oneOf(TRANSPORTS, transport)
|
|
102
|
+
? { transport }
|
|
103
|
+
: {}),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Where and why an update failed, or null when the error is not an update
|
|
109
|
+
* failure. Native rejections carry their own classification; an error the
|
|
110
|
+
* SDK's JavaScript raised happened in `stage`, fetching `resource` when one
|
|
111
|
+
* is given.
|
|
112
|
+
*/
|
|
113
|
+
export const classifyUpdateError = (
|
|
114
|
+
error: unknown,
|
|
115
|
+
stage: UpdateErrorStage,
|
|
116
|
+
resource?: UpdateErrorResource,
|
|
117
|
+
): Classification | null => {
|
|
118
|
+
if (typeof error === "object" && error !== null) {
|
|
119
|
+
const native = readNativeClassification(error);
|
|
120
|
+
if (native !== undefined) return native;
|
|
121
|
+
}
|
|
122
|
+
const scope = {
|
|
123
|
+
stage,
|
|
124
|
+
...(resource === undefined ? {} : { resource }),
|
|
125
|
+
};
|
|
126
|
+
// Names, not classes, so an error from another copy of a module still
|
|
127
|
+
// classifies, as it does across test module resets.
|
|
128
|
+
const name = error instanceof Error ? error.name : null;
|
|
129
|
+
// A selection that became stale is a race between checks, not a failure.
|
|
130
|
+
if (name === "StaleReleaseCatalogError") return null;
|
|
131
|
+
if (name === "UpdateHttpError") {
|
|
132
|
+
return {
|
|
133
|
+
...scope,
|
|
134
|
+
reason: "http",
|
|
135
|
+
httpStatus: (error as UpdateHttpError).status,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (name === "InvalidUpdateResponseError" || name === "SyntaxError") {
|
|
139
|
+
return { ...scope, reason: "invalid_response" };
|
|
140
|
+
}
|
|
141
|
+
if (error instanceof Error && error.message === "Request timed out") {
|
|
142
|
+
return { ...scope, reason: "network", transport: "timeout" };
|
|
143
|
+
}
|
|
144
|
+
if (name === "AbortError") {
|
|
145
|
+
return { ...scope, reason: "network", transport: "cancelled" };
|
|
146
|
+
}
|
|
147
|
+
if (name === "TypeError") {
|
|
148
|
+
// fetch rejects with a TypeError when no response arrives, without
|
|
149
|
+
// saying why.
|
|
150
|
+
return { ...scope, reason: "network" };
|
|
151
|
+
}
|
|
152
|
+
return { ...scope, reason: "unknown" };
|
|
153
|
+
};
|
package/src/wrap.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { handleNotifyAppReady } from "./appReady";
|
|
3
4
|
import { checkForUpdate } from "./checkForUpdate";
|
|
4
5
|
import { useEventCallback } from "./hooks/useEventCallback";
|
|
5
|
-
import {
|
|
6
|
-
import { handleNotifyAppReady } from "./notifyAppReadyInsights";
|
|
6
|
+
import { getUpdateId, reload } from "./native";
|
|
7
7
|
import { useHotUpdaterStore } from "./store";
|
|
8
8
|
import type {
|
|
9
|
+
HotUpdaterFallbackComponentProps,
|
|
9
10
|
InternalInitOptions,
|
|
10
11
|
InternalWrapOptions,
|
|
11
12
|
UpdateStatus,
|
|
@@ -32,11 +33,46 @@ export function wrap(
|
|
|
32
33
|
) => React.ComponentType<P> {
|
|
33
34
|
const { reloadOnForceUpdate = true, ...restOptions } = options;
|
|
34
35
|
|
|
36
|
+
// Progress is subscribed only where it is rendered or reported, so progress
|
|
37
|
+
// events don't re-render the wrapped app.
|
|
38
|
+
const ProgressReporter = ({
|
|
39
|
+
onProgress,
|
|
40
|
+
}: {
|
|
41
|
+
onProgress: (progress: number) => void;
|
|
42
|
+
}) => {
|
|
43
|
+
const progress = useHotUpdaterStore((state) => state.progress);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
onProgress(progress);
|
|
47
|
+
}, [progress]);
|
|
48
|
+
|
|
49
|
+
return null;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const FallbackWithProgress = ({
|
|
53
|
+
Fallback,
|
|
54
|
+
status,
|
|
55
|
+
message,
|
|
56
|
+
}: {
|
|
57
|
+
Fallback: React.FC<HotUpdaterFallbackComponentProps>;
|
|
58
|
+
status: HotUpdaterFallbackComponentProps["status"];
|
|
59
|
+
message: string | null;
|
|
60
|
+
}) => {
|
|
61
|
+
const progressState = useHotUpdaterStore((state) => state);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Fallback
|
|
65
|
+
artifactType={progressState.artifactType}
|
|
66
|
+
details={progressState.details}
|
|
67
|
+
progress={progressState.progress}
|
|
68
|
+
status={status}
|
|
69
|
+
message={message}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
35
74
|
return <P extends object>(WrappedComponent: React.ComponentType<P>) => {
|
|
36
75
|
const HotUpdaterHOC: React.FC<P> = (props: P) => {
|
|
37
|
-
const progressState = useHotUpdaterStore((state) => state);
|
|
38
|
-
const progress = progressState.progress;
|
|
39
|
-
|
|
40
76
|
const [message, setMessage] = useState<string | null>(null);
|
|
41
77
|
const [updateStatus, setUpdateStatus] =
|
|
42
78
|
useState<UpdateStatus>("CHECK_FOR_UPDATE");
|
|
@@ -45,8 +81,8 @@ export function wrap(
|
|
|
45
81
|
try {
|
|
46
82
|
setUpdateStatus("CHECK_FOR_UPDATE");
|
|
47
83
|
|
|
84
|
+
const readiness = handleNotifyAppReady(restOptions);
|
|
48
85
|
const updateInfo = await checkForUpdate({
|
|
49
|
-
insights: restOptions.insights,
|
|
50
86
|
client: restOptions.client,
|
|
51
87
|
updateStrategy: restOptions.updateStrategy,
|
|
52
88
|
requestHeaders: restOptions.requestHeaders,
|
|
@@ -54,6 +90,7 @@ export function wrap(
|
|
|
54
90
|
onError: restOptions.onError,
|
|
55
91
|
});
|
|
56
92
|
|
|
93
|
+
await readiness;
|
|
57
94
|
setMessage(updateInfo?.message ?? null);
|
|
58
95
|
|
|
59
96
|
if (!updateInfo) {
|
|
@@ -61,7 +98,7 @@ export function wrap(
|
|
|
61
98
|
status: "UP_TO_DATE",
|
|
62
99
|
shouldForceUpdate: false,
|
|
63
100
|
message: null,
|
|
64
|
-
id:
|
|
101
|
+
id: getUpdateId(),
|
|
65
102
|
});
|
|
66
103
|
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
67
104
|
return;
|
|
@@ -111,39 +148,33 @@ export function wrap(
|
|
|
111
148
|
}
|
|
112
149
|
});
|
|
113
150
|
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
restOptions.onProgress?.(progress);
|
|
116
|
-
}, [progress]);
|
|
117
|
-
|
|
118
|
-
// Read the native launch report after the first render commit.
|
|
119
|
-
useEffect(() => {
|
|
120
|
-
void handleNotifyAppReady(restOptions);
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
151
|
// Start update check
|
|
124
152
|
useEffect(() => {
|
|
125
153
|
initHotUpdater();
|
|
126
154
|
}, []);
|
|
127
155
|
|
|
128
|
-
|
|
156
|
+
const content =
|
|
129
157
|
restOptions.fallbackComponent &&
|
|
130
|
-
updateStatus !== "UPDATE_PROCESS_COMPLETED"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<Fallback
|
|
135
|
-
artifactType={progressState.artifactType}
|
|
136
|
-
details={progressState.details}
|
|
137
|
-
downloadedBytes={progressState.downloadedBytes}
|
|
138
|
-
progress={progress}
|
|
158
|
+
updateStatus !== "UPDATE_PROCESS_COMPLETED" ? (
|
|
159
|
+
<FallbackWithProgress
|
|
160
|
+
Fallback={restOptions.fallbackComponent}
|
|
139
161
|
status={updateStatus}
|
|
140
162
|
message={message}
|
|
141
|
-
totalBytes={progressState.totalBytes}
|
|
142
163
|
/>
|
|
164
|
+
) : (
|
|
165
|
+
<WrappedComponent {...props} />
|
|
143
166
|
);
|
|
167
|
+
|
|
168
|
+
if (!restOptions.onProgress) {
|
|
169
|
+
return content;
|
|
144
170
|
}
|
|
145
171
|
|
|
146
|
-
return
|
|
172
|
+
return (
|
|
173
|
+
<>
|
|
174
|
+
{content}
|
|
175
|
+
<ProgressReporter onProgress={restOptions.onProgress} />
|
|
176
|
+
</>
|
|
177
|
+
);
|
|
147
178
|
};
|
|
148
179
|
|
|
149
180
|
return HotUpdaterHOC as React.ComponentType<P>;
|
package/src/wrap.types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
|
|
3
|
+
import type { HotUpdaterClientPlugin } from "./clientPlugin";
|
|
3
4
|
import type { HotUpdaterError } from "./error";
|
|
4
5
|
import type { HotUpdaterHttpClient } from "./httpClient";
|
|
5
6
|
import type { NotifyAppReadyResult } from "./native";
|
|
@@ -21,8 +22,6 @@ export type UpdateStatus =
|
|
|
21
22
|
export type HotUpdaterFallbackComponentProps = {
|
|
22
23
|
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
23
24
|
progress: number;
|
|
24
|
-
downloadedBytes: HotUpdaterState["downloadedBytes"];
|
|
25
|
-
totalBytes: HotUpdaterState["totalBytes"];
|
|
26
25
|
message: string | null;
|
|
27
26
|
artifactType: HotUpdaterState["artifactType"];
|
|
28
27
|
details: HotUpdaterState["details"];
|
|
@@ -30,11 +29,12 @@ export type HotUpdaterFallbackComponentProps = {
|
|
|
30
29
|
|
|
31
30
|
interface CommonHotUpdaterOptions {
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Client plugins, such as `insights()` from `@hot-updater/react-native`.
|
|
33
|
+
* Each plugin's `setup` runs
|
|
34
|
+
* once, and its hooks observe launches, update checks, downloads, and
|
|
35
|
+
* update failures. Plugin ids must be unique.
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
plugins?: readonly HotUpdaterClientPlugin[];
|
|
38
38
|
/** Base URL of a server exposing the Hot Updater v1 client HTTP protocol. */
|
|
39
39
|
baseURL: HotUpdaterBaseURL;
|
|
40
40
|
requestHeaders?: Record<string, string>;
|
|
@@ -56,7 +56,6 @@ export type HotUpdaterInitOptions = CommonHotUpdaterOptions;
|
|
|
56
56
|
export type HotUpdaterOptions = AutoUpdateOptions;
|
|
57
57
|
|
|
58
58
|
type InternalCommonOptions = {
|
|
59
|
-
insights?: boolean;
|
|
60
59
|
client: HotUpdaterHttpClient;
|
|
61
60
|
requestHeaders?: Record<string, string>;
|
|
62
61
|
requestTimeout?: number;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import java.io.File
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Unified decompression service that uses Strategy pattern to handle multiple compression formats.
|
|
8
|
-
* Automatically detects format by trying each strategy's validation and delegates to appropriate decompression strategy.
|
|
9
|
-
*/
|
|
10
|
-
class DecompressService {
|
|
11
|
-
companion object {
|
|
12
|
-
private const val TAG = "DecompressService"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Strategies with reliable file signatures that can be validated cheaply.
|
|
16
|
-
// TAR.BR is attempted only as the final fallback because Brotli has no reliable magic bytes.
|
|
17
|
-
private val signatureStrategies =
|
|
18
|
-
listOf(
|
|
19
|
-
ZipDecompressionStrategy(),
|
|
20
|
-
TarGzDecompressionStrategy(),
|
|
21
|
-
)
|
|
22
|
-
private val tarBrStrategy = TarBrDecompressionStrategy()
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Extracts a compressed file to the destination directory.
|
|
26
|
-
* Automatically detects compression format by trying each strategy's validation.
|
|
27
|
-
* @param filePath Path to the compressed file
|
|
28
|
-
* @param destinationPath Path to the destination directory
|
|
29
|
-
* @param progressCallback Callback for progress updates (0.0 - 1.0)
|
|
30
|
-
* @return true if extraction was successful, false otherwise
|
|
31
|
-
*/
|
|
32
|
-
fun extractZipFile(
|
|
33
|
-
filePath: String,
|
|
34
|
-
destinationPath: String,
|
|
35
|
-
progressCallback: (Double) -> Unit,
|
|
36
|
-
): Boolean {
|
|
37
|
-
// Collect file information for better error messages
|
|
38
|
-
val file = File(filePath)
|
|
39
|
-
val fileName = file.name
|
|
40
|
-
val fileSize = if (file.exists()) file.length() else 0L
|
|
41
|
-
|
|
42
|
-
// Try each signature-based strategy first.
|
|
43
|
-
for (strategy in signatureStrategies) {
|
|
44
|
-
if (strategy.isValid(filePath)) {
|
|
45
|
-
Log.d(TAG, "Using strategy for $fileName")
|
|
46
|
-
return strategy.decompress(filePath, destinationPath, progressCallback)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
Log.d(TAG, "No ZIP/TAR.GZ signature matched for $fileName, trying TAR.BR fallback")
|
|
51
|
-
|
|
52
|
-
if (tarBrStrategy.decompress(filePath, destinationPath, progressCallback)) {
|
|
53
|
-
Log.d(TAG, "Using TAR.BR fallback for $fileName")
|
|
54
|
-
return true
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
val errorMessage = createInvalidArchiveMessage(fileName, fileSize)
|
|
58
|
-
Log.e(TAG, errorMessage)
|
|
59
|
-
return false
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Validates if a file matches one of the signature-based archive formats.
|
|
64
|
-
* @param filePath Path to the file to validate
|
|
65
|
-
* @return true if the file is a valid compressed archive
|
|
66
|
-
*/
|
|
67
|
-
fun isValidZipFile(filePath: String): Boolean {
|
|
68
|
-
for (strategy in signatureStrategies) {
|
|
69
|
-
if (strategy.isValid(filePath)) {
|
|
70
|
-
return true
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
Log.d(TAG, "No ZIP/TAR.GZ signature matched for file: $filePath. TAR.BR is handled during extraction fallback.")
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private fun createInvalidArchiveMessage(
|
|
78
|
-
fileName: String,
|
|
79
|
-
fileSize: Long,
|
|
80
|
-
): String =
|
|
81
|
-
"""
|
|
82
|
-
The downloaded bundle file is not a valid compressed archive: $fileName ($fileSize bytes)
|
|
83
|
-
|
|
84
|
-
Supported formats:
|
|
85
|
-
- ZIP archives (.zip)
|
|
86
|
-
- GZIP compressed TAR archives (.tar.gz)
|
|
87
|
-
- Brotli compressed TAR archives (.tar.br)
|
|
88
|
-
""".trimIndent()
|
|
89
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Interface for decompression strategies
|
|
5
|
-
*/
|
|
6
|
-
interface DecompressionStrategy {
|
|
7
|
-
/**
|
|
8
|
-
* Validates if a file can be decompressed by this strategy
|
|
9
|
-
* @param filePath Path to the file to validate
|
|
10
|
-
* @return true if the file is valid for this strategy
|
|
11
|
-
*/
|
|
12
|
-
fun isValid(filePath: String): Boolean
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Decompresses a file to the destination directory
|
|
16
|
-
* @param filePath Path to the compressed file
|
|
17
|
-
* @param destinationPath Path to the destination directory
|
|
18
|
-
* @param progressCallback Callback for progress updates (0.0 - 1.0)
|
|
19
|
-
* @return true if decompression was successful, false otherwise
|
|
20
|
-
*/
|
|
21
|
-
fun decompress(
|
|
22
|
-
filePath: String,
|
|
23
|
-
destinationPath: String,
|
|
24
|
-
progressCallback: (Double) -> Unit,
|
|
25
|
-
): Boolean
|
|
26
|
-
}
|