@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
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Strategy for handling TAR+Brotli compressed files
|
|
5
|
-
*/
|
|
6
|
-
class TarBrDecompressionStrategy: DecompressionStrategy {
|
|
7
|
-
private static let MIN_FILE_SIZE: UInt64 = 10
|
|
8
|
-
|
|
9
|
-
func isValid(file: String) -> Bool {
|
|
10
|
-
guard FileManager.default.fileExists(atPath: file) else {
|
|
11
|
-
NSLog("[TarBrStrategy] Invalid file: doesn't exist")
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
do {
|
|
16
|
-
let attributes = try FileManager.default.attributesOfItem(atPath: file)
|
|
17
|
-
guard let fileSize = attributes[.size] as? UInt64, fileSize >= Self.MIN_FILE_SIZE else {
|
|
18
|
-
NSLog("[TarBrStrategy] Invalid file: too small")
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
NSLog("[TarBrStrategy] Invalid file: cannot read attributes - \(error.localizedDescription)")
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return StreamingTarArchiveExtractor.containsTarEntries(file: file, algorithm: .brotli)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
30
|
-
NSLog("[TarBrStrategy] Starting extraction of \(file) to \(destination)")
|
|
31
|
-
try StreamingTarArchiveExtractor.extractCompressedTar(
|
|
32
|
-
file: file,
|
|
33
|
-
to: destination,
|
|
34
|
-
algorithm: .brotli,
|
|
35
|
-
progressHandler: progressHandler
|
|
36
|
-
)
|
|
37
|
-
NSLog("[TarBrStrategy] Successfully extracted all entries")
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Strategy for handling TAR+GZIP compressed files
|
|
5
|
-
*/
|
|
6
|
-
class TarGzDecompressionStrategy: DecompressionStrategy {
|
|
7
|
-
private static let MIN_FILE_SIZE: UInt64 = 10
|
|
8
|
-
|
|
9
|
-
func isValid(file: String) -> Bool {
|
|
10
|
-
guard FileManager.default.fileExists(atPath: file) else {
|
|
11
|
-
NSLog("[TarGzStrategy] Invalid file: doesn't exist")
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
do {
|
|
16
|
-
let attributes = try FileManager.default.attributesOfItem(atPath: file)
|
|
17
|
-
guard let fileSize = attributes[.size] as? UInt64, fileSize >= Self.MIN_FILE_SIZE else {
|
|
18
|
-
NSLog("[TarGzStrategy] Invalid file: too small")
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
} catch {
|
|
22
|
-
NSLog("[TarGzStrategy] Invalid file: cannot read attributes - \(error.localizedDescription)")
|
|
23
|
-
return false
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Check GZIP magic bytes (0x1F 0x8B)
|
|
27
|
-
guard let fileHandle = FileHandle(forReadingAtPath: file) else {
|
|
28
|
-
NSLog("[TarGzStrategy] Invalid file: cannot open file")
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
defer {
|
|
33
|
-
fileHandle.closeFile()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
guard let header = try? ArchiveExtractionUtilities.readUpToCount(from: fileHandle, count: 2),
|
|
37
|
-
header.count == 2 else {
|
|
38
|
-
NSLog("[TarGzStrategy] Invalid file: cannot read header")
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let isGzip = header[0] == 0x1F && header[1] == 0x8B
|
|
43
|
-
if !isGzip {
|
|
44
|
-
NSLog("[TarGzStrategy] Invalid file: wrong magic bytes (expected 0x1F 0x8B, got 0x\(String(format: "%02X", header[0])) 0x\(String(format: "%02X", header[1])))")
|
|
45
|
-
}
|
|
46
|
-
return isGzip
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
50
|
-
NSLog("[TarGzStrategy] Starting extraction of \(file) to \(destination)")
|
|
51
|
-
try StreamingTarArchiveExtractor.extractCompressedTar(
|
|
52
|
-
file: file,
|
|
53
|
-
to: destination,
|
|
54
|
-
algorithm: .gzip,
|
|
55
|
-
progressHandler: progressHandler
|
|
56
|
-
)
|
|
57
|
-
NSLog("[TarGzStrategy] Successfully extracted all entries")
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
import zlib
|
|
3
|
-
|
|
4
|
-
enum ZipArchiveExtractor {
|
|
5
|
-
private static let localFileHeaderSignature: UInt32 = 0x04034B50
|
|
6
|
-
private static let centralDirectoryHeaderSignature: UInt32 = 0x02014B50
|
|
7
|
-
private static let endOfCentralDirectorySignature: UInt32 = 0x06054B50
|
|
8
|
-
private static let storedMethod: UInt16 = 0
|
|
9
|
-
private static let deflatedMethod: UInt16 = 8
|
|
10
|
-
private static let encryptedFlag: UInt16 = 1 << 0
|
|
11
|
-
private static let maxCommentLength = 0xFFFF
|
|
12
|
-
private static let unixHostIdentifier: UInt8 = 3
|
|
13
|
-
private static let fileTypeMask: UInt16 = 0o170000
|
|
14
|
-
private static let symbolicLinkMode: UInt16 = 0o120000
|
|
15
|
-
private static let directoryMode: UInt16 = 0o040000
|
|
16
|
-
|
|
17
|
-
private struct CentralDirectoryEntry {
|
|
18
|
-
let path: String
|
|
19
|
-
let compressionMethod: UInt16
|
|
20
|
-
let flags: UInt16
|
|
21
|
-
let compressedSize: UInt64
|
|
22
|
-
let uncompressedSize: UInt64
|
|
23
|
-
let checksum: UInt32
|
|
24
|
-
let localHeaderOffset: UInt64
|
|
25
|
-
let isDirectory: Bool
|
|
26
|
-
let isSymbolicLink: Bool
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private struct EndOfCentralDirectoryRecord {
|
|
30
|
-
let totalEntries: UInt16
|
|
31
|
-
let centralDirectoryOffset: UInt64
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static func extract(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
35
|
-
let fileURL = URL(fileURLWithPath: file)
|
|
36
|
-
let fileSize = try archiveFileSize(at: file)
|
|
37
|
-
let handle = try FileHandle(forReadingFrom: fileURL)
|
|
38
|
-
|
|
39
|
-
defer {
|
|
40
|
-
try? handle.close()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let entries = try readCentralDirectoryEntries(from: handle, fileSize: fileSize)
|
|
44
|
-
|
|
45
|
-
let destinationURL = URL(fileURLWithPath: destination)
|
|
46
|
-
try ArchiveExtractionUtilities.ensureDirectory(at: destinationURL)
|
|
47
|
-
|
|
48
|
-
let totalCompressedBytes = entries.reduce(UInt64(0)) { $0 + max($1.compressedSize, 1) }
|
|
49
|
-
var processedCompressedBytes: UInt64 = 0
|
|
50
|
-
|
|
51
|
-
for entry in entries {
|
|
52
|
-
try extractEntry(entry, from: handle, to: destinationURL.standardizedFileURL.path)
|
|
53
|
-
processedCompressedBytes += max(entry.compressedSize, 1)
|
|
54
|
-
|
|
55
|
-
guard totalCompressedBytes > 0 else {
|
|
56
|
-
continue
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let progress = min(Double(processedCompressedBytes) / Double(totalCompressedBytes), 1.0)
|
|
60
|
-
progressHandler(progress)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
progressHandler(1.0)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private static func readCentralDirectoryEntries(
|
|
67
|
-
from handle: FileHandle,
|
|
68
|
-
fileSize: UInt64
|
|
69
|
-
) throws -> [CentralDirectoryEntry] {
|
|
70
|
-
let endRecord = try locateEndOfCentralDirectory(in: handle, fileSize: fileSize)
|
|
71
|
-
|
|
72
|
-
try ArchiveExtractionUtilities.seek(handle, to: endRecord.centralDirectoryOffset)
|
|
73
|
-
|
|
74
|
-
var entries: [CentralDirectoryEntry] = []
|
|
75
|
-
entries.reserveCapacity(Int(endRecord.totalEntries))
|
|
76
|
-
|
|
77
|
-
for _ in 0..<endRecord.totalEntries {
|
|
78
|
-
let header = try ArchiveExtractionUtilities.readExactly(from: handle, count: 46)
|
|
79
|
-
|
|
80
|
-
guard header.archiveUInt32LE(at: 0) == centralDirectoryHeaderSignature else {
|
|
81
|
-
throw NSError(
|
|
82
|
-
domain: "ZipArchiveExtractor",
|
|
83
|
-
code: 1,
|
|
84
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid ZIP central directory header"]
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let flags = header.archiveUInt16LE(at: 8)
|
|
89
|
-
let compressionMethod = header.archiveUInt16LE(at: 10)
|
|
90
|
-
let compressedSize = header.archiveUInt32LE(at: 20)
|
|
91
|
-
let uncompressedSize = header.archiveUInt32LE(at: 24)
|
|
92
|
-
let fileNameLength = Int(header.archiveUInt16LE(at: 28))
|
|
93
|
-
let extraFieldLength = Int(header.archiveUInt16LE(at: 30))
|
|
94
|
-
let commentLength = Int(header.archiveUInt16LE(at: 32))
|
|
95
|
-
let checksum = header.archiveUInt32LE(at: 16)
|
|
96
|
-
let localHeaderOffset = header.archiveUInt32LE(at: 42)
|
|
97
|
-
let versionMadeBy = header.archiveUInt16LE(at: 4)
|
|
98
|
-
let externalAttributes = header.archiveUInt32LE(at: 38)
|
|
99
|
-
|
|
100
|
-
guard compressedSize != UInt32.max,
|
|
101
|
-
uncompressedSize != UInt32.max,
|
|
102
|
-
localHeaderOffset != UInt32.max else {
|
|
103
|
-
throw NSError(
|
|
104
|
-
domain: "ZipArchiveExtractor",
|
|
105
|
-
code: 2,
|
|
106
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP64 archives are not supported"]
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let fileNameData = try ArchiveExtractionUtilities.readExactly(from: handle, count: fileNameLength)
|
|
111
|
-
let extraFieldData = try ArchiveExtractionUtilities.readExactly(from: handle, count: extraFieldLength)
|
|
112
|
-
_ = extraFieldData
|
|
113
|
-
if commentLength > 0 {
|
|
114
|
-
try ArchiveExtractionUtilities.skipBytes(UInt64(commentLength), in: handle)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let path = decodePath(from: fileNameData)
|
|
118
|
-
let mode = unixFileMode(versionMadeBy: versionMadeBy, externalAttributes: externalAttributes)
|
|
119
|
-
let isDirectory = path.hasSuffix("/") || mode == directoryMode
|
|
120
|
-
let isSymbolicLink = mode == symbolicLinkMode
|
|
121
|
-
|
|
122
|
-
entries.append(
|
|
123
|
-
CentralDirectoryEntry(
|
|
124
|
-
path: path,
|
|
125
|
-
compressionMethod: compressionMethod,
|
|
126
|
-
flags: flags,
|
|
127
|
-
compressedSize: UInt64(compressedSize),
|
|
128
|
-
uncompressedSize: UInt64(uncompressedSize),
|
|
129
|
-
checksum: checksum,
|
|
130
|
-
localHeaderOffset: UInt64(localHeaderOffset),
|
|
131
|
-
isDirectory: isDirectory,
|
|
132
|
-
isSymbolicLink: isSymbolicLink
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return entries
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private static func locateEndOfCentralDirectory(
|
|
141
|
-
in handle: FileHandle,
|
|
142
|
-
fileSize: UInt64
|
|
143
|
-
) throws -> EndOfCentralDirectoryRecord {
|
|
144
|
-
let minimumRecordLength = 22
|
|
145
|
-
let searchLength = Int(min(fileSize, UInt64(maxCommentLength + minimumRecordLength)))
|
|
146
|
-
let searchOffset = fileSize - UInt64(searchLength)
|
|
147
|
-
|
|
148
|
-
try ArchiveExtractionUtilities.seek(handle, to: searchOffset)
|
|
149
|
-
let tailData = try ArchiveExtractionUtilities.readExactly(from: handle, count: searchLength)
|
|
150
|
-
|
|
151
|
-
let signatureBytes: [UInt8] = [0x50, 0x4B, 0x05, 0x06]
|
|
152
|
-
|
|
153
|
-
guard let recordIndex = findLastSignature(signatureBytes, in: tailData) else {
|
|
154
|
-
throw NSError(
|
|
155
|
-
domain: "ZipArchiveExtractor",
|
|
156
|
-
code: 3,
|
|
157
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP end of central directory record not found"]
|
|
158
|
-
)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let minimumEndIndex = recordIndex + minimumRecordLength
|
|
162
|
-
guard minimumEndIndex <= tailData.count else {
|
|
163
|
-
throw NSError(
|
|
164
|
-
domain: "ZipArchiveExtractor",
|
|
165
|
-
code: 4,
|
|
166
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP end of central directory record is truncated"]
|
|
167
|
-
)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
let commentLength = Int(tailData.archiveUInt16LE(at: recordIndex + 20))
|
|
171
|
-
guard minimumEndIndex + commentLength == tailData.count else {
|
|
172
|
-
throw NSError(
|
|
173
|
-
domain: "ZipArchiveExtractor",
|
|
174
|
-
code: 5,
|
|
175
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP central directory comment is malformed"]
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return EndOfCentralDirectoryRecord(
|
|
180
|
-
totalEntries: tailData.archiveUInt16LE(at: recordIndex + 10),
|
|
181
|
-
centralDirectoryOffset: UInt64(tailData.archiveUInt32LE(at: recordIndex + 16))
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private static func extractEntry(
|
|
186
|
-
_ entry: CentralDirectoryEntry,
|
|
187
|
-
from handle: FileHandle,
|
|
188
|
-
to destinationRoot: String
|
|
189
|
-
) throws {
|
|
190
|
-
guard entry.flags & encryptedFlag == 0 else {
|
|
191
|
-
throw NSError(
|
|
192
|
-
domain: "ZipArchiveExtractor",
|
|
193
|
-
code: 6,
|
|
194
|
-
userInfo: [NSLocalizedDescriptionKey: "Encrypted ZIP entries are not supported"]
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
guard let relativePath = ArchiveExtractionUtilities.normalizedRelativePath(from: entry.path) else {
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
let targetURL = try ArchiveExtractionUtilities.extractionURL(
|
|
203
|
-
for: relativePath,
|
|
204
|
-
destinationRoot: destinationRoot
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
if entry.isDirectory {
|
|
208
|
-
try ArchiveExtractionUtilities.ensureDirectory(at: targetURL)
|
|
209
|
-
return
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if entry.isSymbolicLink {
|
|
213
|
-
NSLog("[ZipArchiveExtractor] Skipping symbolic link entry: \(entry.path)")
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
try ArchiveExtractionUtilities.seek(handle, to: entry.localHeaderOffset)
|
|
218
|
-
let localHeader = try ArchiveExtractionUtilities.readExactly(from: handle, count: 30)
|
|
219
|
-
|
|
220
|
-
guard localHeader.archiveUInt32LE(at: 0) == localFileHeaderSignature else {
|
|
221
|
-
throw NSError(
|
|
222
|
-
domain: "ZipArchiveExtractor",
|
|
223
|
-
code: 7,
|
|
224
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid ZIP local file header for \(entry.path)"]
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
let fileNameLength = UInt64(localHeader.archiveUInt16LE(at: 26))
|
|
229
|
-
let extraFieldLength = UInt64(localHeader.archiveUInt16LE(at: 28))
|
|
230
|
-
let dataOffset = entry.localHeaderOffset + 30 + fileNameLength + extraFieldLength
|
|
231
|
-
|
|
232
|
-
try ArchiveExtractionUtilities.seek(handle, to: dataOffset)
|
|
233
|
-
let outputHandle = try ArchiveExtractionUtilities.createOutputFile(at: targetURL)
|
|
234
|
-
|
|
235
|
-
defer {
|
|
236
|
-
try? outputHandle.close()
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
let extractionResult: (writtenSize: UInt64, checksum: UInt32)
|
|
240
|
-
|
|
241
|
-
switch entry.compressionMethod {
|
|
242
|
-
case storedMethod:
|
|
243
|
-
extractionResult = try extractStoredEntry(
|
|
244
|
-
from: handle,
|
|
245
|
-
compressedSize: entry.compressedSize,
|
|
246
|
-
to: outputHandle
|
|
247
|
-
)
|
|
248
|
-
case deflatedMethod:
|
|
249
|
-
extractionResult = try extractDeflatedEntry(
|
|
250
|
-
from: handle,
|
|
251
|
-
compressedSize: entry.compressedSize,
|
|
252
|
-
path: entry.path,
|
|
253
|
-
to: outputHandle
|
|
254
|
-
)
|
|
255
|
-
default:
|
|
256
|
-
throw NSError(
|
|
257
|
-
domain: "ZipArchiveExtractor",
|
|
258
|
-
code: 8,
|
|
259
|
-
userInfo: [NSLocalizedDescriptionKey: "Unsupported ZIP compression method \(entry.compressionMethod) for \(entry.path)"]
|
|
260
|
-
)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
guard extractionResult.writtenSize == entry.uncompressedSize else {
|
|
264
|
-
throw NSError(
|
|
265
|
-
domain: "ZipArchiveExtractor",
|
|
266
|
-
code: 9,
|
|
267
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP entry size mismatch for \(entry.path)"]
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
guard extractionResult.checksum == entry.checksum else {
|
|
272
|
-
throw NSError(
|
|
273
|
-
domain: "ZipArchiveExtractor",
|
|
274
|
-
code: 10,
|
|
275
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP entry checksum mismatch for \(entry.path)"]
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
private static func extractStoredEntry(
|
|
281
|
-
from handle: FileHandle,
|
|
282
|
-
compressedSize: UInt64,
|
|
283
|
-
to outputHandle: FileHandle
|
|
284
|
-
) throws -> (writtenSize: UInt64, checksum: UInt32) {
|
|
285
|
-
var remainingBytes = compressedSize
|
|
286
|
-
var totalWritten: UInt64 = 0
|
|
287
|
-
var checksum: uLong = crc32(0, nil, 0)
|
|
288
|
-
|
|
289
|
-
while remainingBytes > 0 {
|
|
290
|
-
let chunkSize = Int(min(remainingBytes, UInt64(ArchiveExtractionUtilities.bufferSize)))
|
|
291
|
-
let chunk = try ArchiveExtractionUtilities.readExactly(from: handle, count: chunkSize)
|
|
292
|
-
outputHandle.write(chunk)
|
|
293
|
-
totalWritten += UInt64(chunk.count)
|
|
294
|
-
checksum = updateCRC32(checksum, with: chunk)
|
|
295
|
-
remainingBytes -= UInt64(chunk.count)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return (totalWritten, UInt32(checksum))
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private static func extractDeflatedEntry(
|
|
302
|
-
from handle: FileHandle,
|
|
303
|
-
compressedSize: UInt64,
|
|
304
|
-
path: String,
|
|
305
|
-
to outputHandle: FileHandle
|
|
306
|
-
) throws -> (writtenSize: UInt64, checksum: UInt32) {
|
|
307
|
-
let outputBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: ArchiveExtractionUtilities.bufferSize)
|
|
308
|
-
|
|
309
|
-
defer {
|
|
310
|
-
outputBuffer.deallocate()
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
var stream = z_stream()
|
|
314
|
-
let initStatus = inflateInit2_(
|
|
315
|
-
&stream,
|
|
316
|
-
-MAX_WBITS,
|
|
317
|
-
ZLIB_VERSION,
|
|
318
|
-
Int32(MemoryLayout<z_stream>.size)
|
|
319
|
-
)
|
|
320
|
-
|
|
321
|
-
guard initStatus == Z_OK else {
|
|
322
|
-
throw NSError(
|
|
323
|
-
domain: "ZipArchiveExtractor",
|
|
324
|
-
code: 11,
|
|
325
|
-
userInfo: [NSLocalizedDescriptionKey: "Failed to initialize ZIP inflater"]
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
defer {
|
|
330
|
-
inflateEnd(&stream)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
var remainingBytes = compressedSize
|
|
334
|
-
var totalWritten: UInt64 = 0
|
|
335
|
-
var checksum: uLong = crc32(0, nil, 0)
|
|
336
|
-
var reachedStreamEnd = false
|
|
337
|
-
|
|
338
|
-
while remainingBytes > 0 {
|
|
339
|
-
let chunkSize = Int(min(remainingBytes, UInt64(ArchiveExtractionUtilities.bufferSize)))
|
|
340
|
-
let chunk = try ArchiveExtractionUtilities.readExactly(from: handle, count: chunkSize)
|
|
341
|
-
remainingBytes -= UInt64(chunk.count)
|
|
342
|
-
|
|
343
|
-
try chunk.withUnsafeBytes { rawBuffer in
|
|
344
|
-
guard let baseAddress = rawBuffer.bindMemory(to: UInt8.self).baseAddress else {
|
|
345
|
-
return
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
stream.next_in = UnsafeMutablePointer(mutating: baseAddress)
|
|
349
|
-
stream.avail_in = uInt(chunk.count)
|
|
350
|
-
var needsMoreInput = false
|
|
351
|
-
|
|
352
|
-
repeat {
|
|
353
|
-
stream.next_out = outputBuffer
|
|
354
|
-
stream.avail_out = uInt(ArchiveExtractionUtilities.bufferSize)
|
|
355
|
-
|
|
356
|
-
let status = inflate(&stream, Z_NO_FLUSH)
|
|
357
|
-
let producedBytes = ArchiveExtractionUtilities.bufferSize - Int(stream.avail_out)
|
|
358
|
-
if producedBytes > 0 {
|
|
359
|
-
let outputData = Data(bytes: outputBuffer, count: producedBytes)
|
|
360
|
-
outputHandle.write(outputData)
|
|
361
|
-
totalWritten += UInt64(producedBytes)
|
|
362
|
-
checksum = updateCRC32(checksum, with: outputData)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
switch status {
|
|
366
|
-
case Z_OK, Z_STREAM_END:
|
|
367
|
-
if status == Z_STREAM_END {
|
|
368
|
-
guard stream.avail_in == 0, remainingBytes == 0 else {
|
|
369
|
-
throw NSError(
|
|
370
|
-
domain: "ZipArchiveExtractor",
|
|
371
|
-
code: 12,
|
|
372
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP deflate stream ended unexpectedly"]
|
|
373
|
-
)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
reachedStreamEnd = true
|
|
377
|
-
}
|
|
378
|
-
case Z_BUF_ERROR:
|
|
379
|
-
guard stream.avail_in == 0, remainingBytes > 0 else {
|
|
380
|
-
let message = stream.msg.map { String(cString: $0) } ?? "Unknown zlib error"
|
|
381
|
-
throw NSError(
|
|
382
|
-
domain: "ZipArchiveExtractor",
|
|
383
|
-
code: 13,
|
|
384
|
-
userInfo: [
|
|
385
|
-
NSLocalizedDescriptionKey:
|
|
386
|
-
"ZIP deflate failed for \(path): status=\(status) totalIn=\(stream.total_in) totalOut=\(stream.total_out) \(message)"
|
|
387
|
-
]
|
|
388
|
-
)
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
needsMoreInput = true
|
|
392
|
-
|
|
393
|
-
default:
|
|
394
|
-
let message = stream.msg.map { String(cString: $0) } ?? "Unknown zlib error"
|
|
395
|
-
throw NSError(
|
|
396
|
-
domain: "ZipArchiveExtractor",
|
|
397
|
-
code: 13,
|
|
398
|
-
userInfo: [
|
|
399
|
-
NSLocalizedDescriptionKey:
|
|
400
|
-
"ZIP deflate failed for \(path): status=\(status) totalIn=\(stream.total_in) totalOut=\(stream.total_out) \(message)"
|
|
401
|
-
]
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
} while (stream.avail_in > 0 || stream.avail_out == 0) && !needsMoreInput && !reachedStreamEnd
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if reachedStreamEnd {
|
|
408
|
-
break
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
guard reachedStreamEnd || compressedSize == 0 else {
|
|
413
|
-
throw NSError(
|
|
414
|
-
domain: "ZipArchiveExtractor",
|
|
415
|
-
code: 14,
|
|
416
|
-
userInfo: [NSLocalizedDescriptionKey: "ZIP deflate stream did not terminate correctly"]
|
|
417
|
-
)
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return (totalWritten, UInt32(checksum))
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
private static func findLastSignature(_ signature: [UInt8], in data: Data) -> Int? {
|
|
424
|
-
guard signature.count <= data.count else {
|
|
425
|
-
return nil
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
let lastStartIndex = data.count - signature.count
|
|
429
|
-
for startIndex in stride(from: lastStartIndex, through: 0, by: -1) {
|
|
430
|
-
if Array(data[startIndex..<(startIndex + signature.count)]) == signature {
|
|
431
|
-
return startIndex
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return nil
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
private static func decodePath(from data: Data) -> String {
|
|
439
|
-
if let utf8Path = String(data: data, encoding: .utf8) {
|
|
440
|
-
return utf8Path
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return String(decoding: data, as: UTF8.self)
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
private static func updateCRC32(_ checksum: uLong, with data: Data) -> uLong {
|
|
447
|
-
data.withUnsafeBytes { rawBuffer in
|
|
448
|
-
guard let baseAddress = rawBuffer.bindMemory(to: Bytef.self).baseAddress else {
|
|
449
|
-
return checksum
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
return crc32(checksum, baseAddress, uInt(data.count))
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
private static func unixFileMode(versionMadeBy: UInt16, externalAttributes: UInt32) -> UInt16? {
|
|
457
|
-
let hostIdentifier = UInt8((versionMadeBy >> 8) & 0xFF)
|
|
458
|
-
guard hostIdentifier == unixHostIdentifier else {
|
|
459
|
-
return nil
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
let mode = UInt16((externalAttributes >> 16) & 0xFFFF) & fileTypeMask
|
|
463
|
-
guard mode != 0 else {
|
|
464
|
-
return nil
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return mode
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
private static func archiveFileSize(at path: String) throws -> UInt64 {
|
|
471
|
-
let attributes = try FileManager.default.attributesOfItem(atPath: path)
|
|
472
|
-
if let number = attributes[.size] as? NSNumber {
|
|
473
|
-
return number.uint64Value
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if let value = attributes[.size] as? UInt64 {
|
|
477
|
-
return value
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
return 0
|
|
481
|
-
}
|
|
482
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Strategy for handling ZIP compressed files
|
|
5
|
-
*/
|
|
6
|
-
class ZipDecompressionStrategy: DecompressionStrategy {
|
|
7
|
-
private static let ZIP_MAGIC_NUMBER: [UInt8] = [0x50, 0x4B, 0x03, 0x04]
|
|
8
|
-
private static let MIN_ZIP_SIZE: UInt64 = 22
|
|
9
|
-
|
|
10
|
-
func isValid(file: String) -> Bool {
|
|
11
|
-
guard FileManager.default.fileExists(atPath: file) else {
|
|
12
|
-
NSLog("[ZipStrategy] Invalid ZIP: file doesn't exist")
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
do {
|
|
17
|
-
let attributes = try FileManager.default.attributesOfItem(atPath: file)
|
|
18
|
-
guard let fileSize = attributes[.size] as? UInt64, fileSize >= Self.MIN_ZIP_SIZE else {
|
|
19
|
-
NSLog("[ZipStrategy] Invalid ZIP: file too small")
|
|
20
|
-
return false
|
|
21
|
-
}
|
|
22
|
-
} catch {
|
|
23
|
-
NSLog("[ZipStrategy] Invalid ZIP: cannot read attributes - \(error.localizedDescription)")
|
|
24
|
-
return false
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
guard let fileHandle = FileHandle(forReadingAtPath: file) else {
|
|
28
|
-
NSLog("[ZipStrategy] Invalid ZIP: cannot open file")
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
defer {
|
|
33
|
-
fileHandle.closeFile()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
guard let header = try? ArchiveExtractionUtilities.readUpToCount(from: fileHandle, count: 4),
|
|
37
|
-
header.count == 4 else {
|
|
38
|
-
NSLog("[ZipStrategy] Invalid ZIP: cannot read header")
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let magicBytes = [UInt8](header)
|
|
43
|
-
guard magicBytes == Self.ZIP_MAGIC_NUMBER else {
|
|
44
|
-
NSLog("[ZipStrategy] Invalid ZIP: wrong magic number")
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return true
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
52
|
-
NSLog("[ZipStrategy] Starting extraction of \(file) to \(destination)")
|
|
53
|
-
try ZipArchiveExtractor.extract(file: file, to: destination, progressHandler: progressHandler)
|
|
54
|
-
NSLog("[ZipStrategy] Successfully extracted all entries")
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _vitest = require("vitest");
|
|
4
|
-
var _catalogCacheNative = require("./catalogCacheNative.js");
|
|
5
|
-
const nativeModule = _vitest.vi.hoisted(() => ({}));
|
|
6
|
-
_vitest.vi.mock("./specs/NativeHotUpdater", () => ({
|
|
7
|
-
default: nativeModule
|
|
8
|
-
}));
|
|
9
|
-
(0, _vitest.describe)("Release Catalog native cache", () => {
|
|
10
|
-
(0, _vitest.beforeEach)(() => {
|
|
11
|
-
for (const key of Object.keys(nativeModule)) delete nativeModule[key];
|
|
12
|
-
});
|
|
13
|
-
(0, _vitest.it)("fails fast when the v1 native cache API is missing", async () => {
|
|
14
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).rejects.toThrow("Native module is missing 'getReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
|
|
15
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.writeNativeReleaseCatalogCache)("partition", "payload")).rejects.toThrow("Native module is missing 'setReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
|
|
16
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.removeNativeReleaseCatalogCache)("partition")).rejects.toThrow("Native module is missing 'removeReleaseCatalogCache()'. Rebuild the native app before using Release catalogs.");
|
|
17
|
-
});
|
|
18
|
-
(0, _vitest.it)("normalizes an undefined iOS TurboModule cache miss to null", async () => {
|
|
19
|
-
nativeModule.getReleaseCatalogCache = _vitest.vi.fn().mockResolvedValue(undefined);
|
|
20
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).resolves.toBeNull();
|
|
21
|
-
});
|
|
22
|
-
(0, _vitest.it)("treats native cache I/O failures as misses without hiding network work", async () => {
|
|
23
|
-
nativeModule.getReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("read"));
|
|
24
|
-
nativeModule.setReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("write"));
|
|
25
|
-
nativeModule.removeReleaseCatalogCache = _vitest.vi.fn().mockRejectedValue(new Error("remove"));
|
|
26
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.readNativeReleaseCatalogCache)("partition")).resolves.toBeNull();
|
|
27
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.writeNativeReleaseCatalogCache)("partition", "payload")).resolves.toBe(false);
|
|
28
|
-
await (0, _vitest.expect)((0, _catalogCacheNative.removeNativeReleaseCatalogCache)("partition")).resolves.toBeUndefined();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
//# sourceMappingURL=catalogCacheNative.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["_vitest","require","_catalogCacheNative","nativeModule","vi","hoisted","mock","default","describe","beforeEach","key","Object","keys","it","expect","readNativeReleaseCatalogCache","rejects","toThrow","writeNativeReleaseCatalogCache","removeNativeReleaseCatalogCache","getReleaseCatalogCache","fn","mockResolvedValue","undefined","resolves","toBeNull","mockRejectedValue","Error","setReleaseCatalogCache","removeReleaseCatalogCache","toBe","toBeUndefined"],"sourceRoot":"../../src","sources":["catalogCacheNative.spec.ts"],"mappings":";;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAQA,IAAAC,mBAAA,GAAAD,OAAA;AANA,MAAME,YAAY,GAAGC,UAAE,CAACC,OAAO,CAC7B,OAAO,CAAC,CAAC,CACX,CAAC;AAEDD,UAAE,CAACE,IAAI,CAAC,0BAA0B,EAAE,OAAO;EAAEC,OAAO,EAAEJ;AAAa,CAAC,CAAC,CAAC;AAQtE,IAAAK,gBAAQ,EAAC,8BAA8B,EAAE,MAAM;EAC7C,IAAAC,kBAAU,EAAC,MAAM;IACf,KAAK,MAAMC,GAAG,IAAIC,MAAM,CAACC,IAAI,CAACT,YAAY,CAAC,EAAE,OAAOA,YAAY,CAACO,GAAG,CAAC;EACvE,CAAC,CAAC;EAEF,IAAAG,UAAE,EAAC,oDAAoD,EAAE,YAAY;IACnE,MAAM,IAAAC,cAAM,EAAC,IAAAC,iDAA6B,EAAC,WAAW,CAAC,CAAC,CAACC,OAAO,CAACC,OAAO,CACtE,4GACF,CAAC;IACD,MAAM,IAAAH,cAAM,EACV,IAAAI,kDAA8B,EAAC,WAAW,EAAE,SAAS,CACvD,CAAC,CAACF,OAAO,CAACC,OAAO,CACf,4GACF,CAAC;IACD,MAAM,IAAAH,cAAM,EAAC,IAAAK,mDAA+B,EAAC,WAAW,CAAC,CAAC,CAACH,OAAO,CAACC,OAAO,CACxE,+GACF,CAAC;EACH,CAAC,CAAC;EAEF,IAAAJ,UAAE,EAAC,4DAA4D,EAAE,YAAY;IAC3EV,YAAY,CAACiB,sBAAsB,GAAGhB,UAAE,CAACiB,EAAE,CAAC,CAAC,CAACC,iBAAiB,CAACC,SAAS,CAAC;IAE1E,MAAM,IAAAT,cAAM,EACV,IAAAC,iDAA6B,EAAC,WAAW,CAC3C,CAAC,CAACS,QAAQ,CAACC,QAAQ,CAAC,CAAC;EACvB,CAAC,CAAC;EAEF,IAAAZ,UAAE,EAAC,wEAAwE,EAAE,YAAY;IACvFV,YAAY,CAACiB,sBAAsB,GAAGhB,UAAE,CACrCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,MAAM,CAAC,CAAC;IACvCxB,YAAY,CAACyB,sBAAsB,GAAGxB,UAAE,CACrCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxCxB,YAAY,CAAC0B,yBAAyB,GAAGzB,UAAE,CACxCiB,EAAE,CAAC,CAAC,CACJK,iBAAiB,CAAC,IAAIC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEzC,MAAM,IAAAb,cAAM,EACV,IAAAC,iDAA6B,EAAC,WAAW,CAC3C,CAAC,CAACS,QAAQ,CAACC,QAAQ,CAAC,CAAC;IACrB,MAAM,IAAAX,cAAM,EACV,IAAAI,kDAA8B,EAAC,WAAW,EAAE,SAAS,CACvD,CAAC,CAACM,QAAQ,CAACM,IAAI,CAAC,KAAK,CAAC;IACtB,MAAM,IAAAhB,cAAM,EACV,IAAAK,mDAA+B,EAAC,WAAW,CAC7C,CAAC,CAACK,QAAQ,CAACO,aAAa,CAAC,CAAC;EAC5B,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|