@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.15
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 +651 -637
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +10 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +0 -13
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +10 -14
- 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/OkHttpDownloadService.kt +19 -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 +40 -20
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +40 -20
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
- package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
- package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
- package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
- package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -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 +5 -27
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +149 -51
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
- 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/checkForUpdate.js +32 -23
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/error.js +4 -13
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +9 -2
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +46 -29
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/httpClient.spec.js +57 -5
- package/lib/commonjs/httpClient.spec.js.map +1 -1
- package/lib/commonjs/index.js +31 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +56 -11
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +15 -14
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +128 -22
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +28 -21
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyInsights.js +188 -0
- package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +161 -29
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +14 -7
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
- 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/store.notification.spec.js +12 -4
- package/lib/commonjs/store.notification.spec.js.map +1 -1
- package/lib/commonjs/store.spec.js +2 -136
- package/lib/commonjs/store.spec.js.map +1 -1
- package/lib/commonjs/wrap.js +11 -12
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +97 -25
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/checkForUpdate.js +32 -23
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/error.js +4 -13
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +7 -1
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +48 -31
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/httpClient.spec.js +57 -5
- package/lib/module/httpClient.spec.js.map +1 -1
- package/lib/module/index.js +32 -15
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +56 -11
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +13 -12
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +128 -22
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +23 -16
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyInsights.js +181 -0
- package/lib/module/notifyAppReadyInsights.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +153 -21
- package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +11 -4
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
- 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/store.notification.spec.js +12 -4
- package/lib/module/store.notification.spec.js.map +1 -1
- package/lib/module/store.spec.js +2 -136
- package/lib/module/store.spec.js.map +1 -1
- package/lib/module/wrap.js +11 -12
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +99 -25
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -3
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- 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 +4 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +6 -6
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -7
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +5 -9
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- 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 +8 -35
- 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/wrap.types.d.ts +5 -8
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +2 -3
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- 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 +4 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +6 -6
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -7
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +5 -9
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- 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 +8 -35
- 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/wrap.types.d.ts +5 -8
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/checkForUpdate.releaseCatalog.spec.ts +137 -61
- package/src/checkForUpdate.ts +35 -29
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +12 -1
- package/src/httpClient.spec.ts +70 -5
- package/src/httpClient.ts +78 -64
- package/src/index.spec.ts +59 -11
- package/src/index.ts +41 -17
- package/src/native.spec.ts +133 -22
- package/src/native.ts +21 -32
- package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +25 -17
- package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +141 -22
- package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
- package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +13 -5
- package/src/notifyAppReadyInsights.ts +293 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +8 -36
- package/src/store.notification.spec.ts +13 -3
- package/src/store.spec.ts +0 -157
- package/src/store.ts +2 -23
- package/src/wrap.spec.ts +109 -31
- package/src/wrap.tsx +11 -11
- package/src/wrap.types.ts +5 -8
- 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/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/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.js +0 -128
- package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/src/notifyAppReadyAnalytics.ts +0 -194
|
@@ -84,6 +84,7 @@ public struct BundleMetadata: Codable {
|
|
|
84
84
|
var stableSelection: PersistedSelection?
|
|
85
85
|
var stagingSelection: PersistedSelection?
|
|
86
86
|
var verificationPending: Bool
|
|
87
|
+
var launchInProgress: Bool
|
|
87
88
|
var pendingTransition: PendingBundleTransition?
|
|
88
89
|
var pendingSelectionTransition: PendingSelectionTransition?
|
|
89
90
|
var highestSeenCatalogs: [String: CatalogHighWater]
|
|
@@ -98,6 +99,7 @@ public struct BundleMetadata: Codable {
|
|
|
98
99
|
case stableSelection = "stable_selection"
|
|
99
100
|
case stagingSelection = "staging_selection"
|
|
100
101
|
case verificationPending = "verification_pending"
|
|
102
|
+
case launchInProgress = "launch_in_progress"
|
|
101
103
|
case pendingTransition = "pending_transition"
|
|
102
104
|
case pendingSelectionTransition = "pending_selection_transition"
|
|
103
105
|
case highestSeenCatalogs = "highest_seen_catalogs"
|
|
@@ -113,6 +115,7 @@ public struct BundleMetadata: Codable {
|
|
|
113
115
|
stableSelection: PersistedSelection? = nil,
|
|
114
116
|
stagingSelection: PersistedSelection? = nil,
|
|
115
117
|
verificationPending: Bool = false,
|
|
118
|
+
launchInProgress: Bool = false,
|
|
116
119
|
pendingTransition: PendingBundleTransition? = nil,
|
|
117
120
|
pendingSelectionTransition: PendingSelectionTransition? = nil,
|
|
118
121
|
highestSeenCatalogs: [String: CatalogHighWater] = [:],
|
|
@@ -126,6 +129,7 @@ public struct BundleMetadata: Codable {
|
|
|
126
129
|
self.stableSelection = stableSelection
|
|
127
130
|
self.stagingSelection = stagingSelection
|
|
128
131
|
self.verificationPending = verificationPending
|
|
132
|
+
self.launchInProgress = launchInProgress
|
|
129
133
|
self.pendingTransition = pendingTransition
|
|
130
134
|
self.pendingSelectionTransition = pendingSelectionTransition
|
|
131
135
|
self.highestSeenCatalogs = highestSeenCatalogs
|
|
@@ -144,6 +148,7 @@ public struct BundleMetadata: Codable {
|
|
|
144
148
|
stagingSelection = try values.decodeIfPresent(PersistedSelection.self, forKey: .stagingSelection)
|
|
145
149
|
?? stagingBundleId.map(PersistedSelection.legacyBundle)
|
|
146
150
|
verificationPending = try values.decodeIfPresent(Bool.self, forKey: .verificationPending) ?? false
|
|
151
|
+
launchInProgress = try values.decodeIfPresent(Bool.self, forKey: .launchInProgress) ?? false
|
|
147
152
|
pendingTransition = try values.decodeIfPresent(PendingBundleTransition.self, forKey: .pendingTransition)
|
|
148
153
|
pendingSelectionTransition = try values.decodeIfPresent(PendingSelectionTransition.self, forKey: .pendingSelectionTransition)
|
|
149
154
|
highestSeenCatalogs = try values.decodeIfPresent([String: CatalogHighWater].self, forKey: .highestSeenCatalogs) ?? [:]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
enum FileUtilities {
|
|
4
|
+
static func readUpToCount(from handle: FileHandle, count: Int) throws -> Data? {
|
|
5
|
+
guard count >= 0 else {
|
|
6
|
+
throw NSError(
|
|
7
|
+
domain: "FileUtilities",
|
|
8
|
+
code: 1,
|
|
9
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid read size: \(count)"]
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if count == 0 {
|
|
14
|
+
return Data()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) {
|
|
18
|
+
return try handle.read(upToCount: count)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return handle.readData(ofLength: count)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static func normalizedRelativePath(from rawPath: String) -> String? {
|
|
25
|
+
let candidate = rawPath.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
26
|
+
guard !candidate.isEmpty,
|
|
27
|
+
!candidate.contains("\0"),
|
|
28
|
+
!candidate.contains("\\"),
|
|
29
|
+
!candidate.hasPrefix("/"),
|
|
30
|
+
candidate.range(of: #"^[A-Za-z]:"#, options: .regularExpression) == nil
|
|
31
|
+
else {
|
|
32
|
+
return nil
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let components = candidate.split(separator: "/").map(String.init)
|
|
36
|
+
guard !components.isEmpty,
|
|
37
|
+
components.count == candidate.split(separator: "/", omittingEmptySubsequences: false).count,
|
|
38
|
+
!components.contains(".."),
|
|
39
|
+
!components.contains(".")
|
|
40
|
+
else {
|
|
41
|
+
return nil
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return components.joined(separator: "/")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static func fileURL(for relativePath: String, destinationRoot: String) throws -> URL {
|
|
48
|
+
let rootURL = URL(fileURLWithPath: destinationRoot, isDirectory: true)
|
|
49
|
+
let targetURL = rootURL.appendingPathComponent(relativePath)
|
|
50
|
+
let standardizedRoot = rootURL.standardizedFileURL.path
|
|
51
|
+
let standardizedTarget = targetURL.standardizedFileURL.path
|
|
52
|
+
|
|
53
|
+
guard standardizedTarget == standardizedRoot ||
|
|
54
|
+
standardizedTarget.hasPrefix(standardizedRoot + "/") else {
|
|
55
|
+
throw NSError(
|
|
56
|
+
domain: "FileUtilities",
|
|
57
|
+
code: 2,
|
|
58
|
+
userInfo: [NSLocalizedDescriptionKey: "Path traversal attempt detected: \(relativePath)"]
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return targetURL
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
#import "React/RCTConstants.h"
|
|
8
8
|
#import "React/RCTRootView.h"
|
|
9
9
|
#import "React/RCTUtils.h" // Needed for RCTPromiseResolveBlock/RejectBlock in Swift
|
|
10
|
-
#import <SSZipArchive/SSZipArchive.h>
|
|
11
10
|
#import "BsdiffPatchBridge.h"
|
|
12
11
|
|
|
13
12
|
@interface HotUpdaterRecoverySignalBridge : NSObject
|
|
@@ -274,8 +274,6 @@ extern "C" void *HotUpdaterCopyMinBundleId(void)
|
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
// Define Notification names used for observing Swift Core
|
|
277
|
-
NSNotificationName const HotUpdaterDownloadProgressUpdateNotification = @"HotUpdaterDownloadProgressUpdate";
|
|
278
|
-
NSNotificationName const HotUpdaterDownloadDidFinishNotification = @"HotUpdaterDownloadDidFinish";
|
|
279
277
|
NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUpdaterUpdateProgressDidChange";
|
|
280
278
|
|
|
281
279
|
@interface HotUpdaterRecoverySignalBridge : NSObject
|
|
@@ -300,8 +298,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
300
298
|
|
|
301
299
|
@implementation HotUpdater {
|
|
302
300
|
bool hasListeners;
|
|
303
|
-
// Keep track of tasks ONLY for removing observers when this ObjC instance is invalidated
|
|
304
|
-
NSMutableSet<NSURLSessionTask *> *observedTasks; // Changed to NSURLSessionTask for broader compatibility if needed
|
|
305
301
|
}
|
|
306
302
|
|
|
307
303
|
@synthesize bridge = _bridge;
|
|
@@ -314,16 +310,10 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
314
310
|
- (instancetype)init {
|
|
315
311
|
self = [super init];
|
|
316
312
|
if (self) {
|
|
317
|
-
observedTasks = [NSMutableSet set];
|
|
318
|
-
|
|
319
313
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
320
314
|
selector:@selector(handleDownloadProgress:)
|
|
321
315
|
name:HotUpdaterUpdateProgressDidChangeNotification
|
|
322
316
|
object:nil];
|
|
323
|
-
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
324
|
-
selector:@selector(handleDownloadCompletion:)
|
|
325
|
-
name:HotUpdaterDownloadDidFinishNotification
|
|
326
|
-
object:nil];
|
|
327
317
|
|
|
328
318
|
_lastUpdateTime = 0;
|
|
329
319
|
}
|
|
@@ -349,7 +339,6 @@ NSNotificationName const HotUpdaterUpdateProgressDidChangeNotification = @"HotUp
|
|
|
349
339
|
if (gHotUpdaterBridge == self.bridge) {
|
|
350
340
|
gHotUpdaterBridge = nil;
|
|
351
341
|
}
|
|
352
|
-
// Swift side should handle KVO observer removal for its tasks
|
|
353
342
|
[super invalidate];
|
|
354
343
|
}
|
|
355
344
|
|
|
@@ -520,14 +509,6 @@ RCT_EXPORT_MODULE();
|
|
|
520
509
|
}
|
|
521
510
|
}
|
|
522
511
|
|
|
523
|
-
- (void)handleDownloadCompletion:(NSNotification *)notification {
|
|
524
|
-
NSURLSessionTask *task = notification.object; // Task that finished
|
|
525
|
-
RCTLogInfo(@"[HotUpdater.mm] Received download completion notification for task: %@", task.originalRequest.URL);
|
|
526
|
-
// Swift side handles KVO observer removal internally now when task finishes.
|
|
527
|
-
// No specific cleanup needed here based on this notification anymore.
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
512
|
#pragma mark - React Native Events (Keep as is)
|
|
532
513
|
|
|
533
514
|
- (NSArray<NSString *> *)supportedEvents {
|
|
@@ -594,20 +575,17 @@ RCT_EXPORT_MODULE();
|
|
|
594
575
|
if (params.bundleId()) {
|
|
595
576
|
paramDict[@"bundleId"] = params.bundleId();
|
|
596
577
|
}
|
|
597
|
-
if (params.fileUrl()) {
|
|
598
|
-
paramDict[@"fileUrl"] = params.fileUrl();
|
|
599
|
-
}
|
|
600
|
-
if (params.fileHash()) {
|
|
601
|
-
paramDict[@"fileHash"] = params.fileHash();
|
|
602
|
-
}
|
|
603
578
|
if (params.manifestUrl()) {
|
|
604
579
|
paramDict[@"manifestUrl"] = params.manifestUrl();
|
|
605
580
|
}
|
|
606
581
|
if (params.manifestFileHash()) {
|
|
607
582
|
paramDict[@"manifestFileHash"] = params.manifestFileHash();
|
|
608
583
|
}
|
|
609
|
-
if (params.
|
|
610
|
-
paramDict[@"
|
|
584
|
+
if (params.archiveUrl()) {
|
|
585
|
+
paramDict[@"archiveUrl"] = params.archiveUrl();
|
|
586
|
+
}
|
|
587
|
+
if (params.assets()) {
|
|
588
|
+
paramDict[@"assets"] = params.assets();
|
|
611
589
|
}
|
|
612
590
|
if (params.channel()) {
|
|
613
591
|
paramDict[@"channel"] = params.channel();
|
|
@@ -17,6 +17,18 @@ private func hotUpdaterUpdateSignalLaunchStateSymbol(
|
|
|
17
17
|
@_silgen_name("HotUpdaterPerformRecoveryReload")
|
|
18
18
|
private func hotUpdaterPerformRecoveryReloadSymbol() -> ObjCBool
|
|
19
19
|
|
|
20
|
+
@_silgen_name("HotUpdaterMarkFailedRuntime")
|
|
21
|
+
private func hotUpdaterMarkFailedRuntime()
|
|
22
|
+
|
|
23
|
+
@_silgen_name("HotUpdaterIsFailedRuntime")
|
|
24
|
+
private func hotUpdaterIsFailedRuntime() -> ObjCBool
|
|
25
|
+
|
|
26
|
+
@_silgen_name("HotUpdaterInstallRecoveryHooks")
|
|
27
|
+
private func hotUpdaterInstallRecoveryHooks()
|
|
28
|
+
|
|
29
|
+
@_silgen_name("HotUpdaterAfterSurfaceStarts")
|
|
30
|
+
private func hotUpdaterAfterSurfaceStarts(_ action: @convention(block) @escaping () -> Void)
|
|
31
|
+
|
|
20
32
|
@_silgen_name("HotUpdaterCopyMinBundleId")
|
|
21
33
|
private func hotUpdaterCopyMinBundleIdSymbol() -> UnsafeMutableRawPointer
|
|
22
34
|
|
|
@@ -49,6 +61,21 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
49
61
|
private static let DEFAULT_CHANNEL = "production"
|
|
50
62
|
private static let CHANNEL_STORAGE_KEY = "HotUpdaterChannel"
|
|
51
63
|
|
|
64
|
+
private static func parseSafeByteSize(_ value: Any?) -> Int64? {
|
|
65
|
+
guard let number = value as? NSNumber,
|
|
66
|
+
String(cString: number.objCType) != "c" else {
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
69
|
+
let doubleValue = number.doubleValue
|
|
70
|
+
guard doubleValue.isFinite,
|
|
71
|
+
doubleValue >= 0,
|
|
72
|
+
doubleValue <= 9_007_199_254_740_991,
|
|
73
|
+
doubleValue.rounded(.towardZero) == doubleValue else {
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
return Int64(doubleValue)
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
// MARK: - Initialization
|
|
53
80
|
|
|
54
81
|
/**
|
|
@@ -59,12 +86,9 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
59
86
|
let isolationKey = HotUpdaterImpl.getIsolationKey()
|
|
60
87
|
let preferences = VersionedPreferencesService()
|
|
61
88
|
let downloadService = URLSessionDownloadService()
|
|
62
|
-
let decompressService = DecompressService()
|
|
63
|
-
|
|
64
89
|
let bundleStorage = BundleFileStorageService(
|
|
65
90
|
fileSystem: fileSystem,
|
|
66
91
|
downloadService: downloadService,
|
|
67
|
-
decompressService: decompressService,
|
|
68
92
|
preferences: preferences,
|
|
69
93
|
isolationKey: isolationKey,
|
|
70
94
|
builtInBundleIdProvider: { HotUpdaterImpl.minBundleId() }
|
|
@@ -219,7 +243,7 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
219
243
|
/**
|
|
220
244
|
* Updates the bundle from JavaScript bridge.
|
|
221
245
|
* This method acts as the primary error boundary for all bundle operations.
|
|
222
|
-
* @param params Dictionary with bundleId and
|
|
246
|
+
* @param params Dictionary with bundleId, manifest, and asset parameters
|
|
223
247
|
* @param resolve Promise resolve callback
|
|
224
248
|
* @param reject Promise reject callback
|
|
225
249
|
*/
|
|
@@ -243,36 +267,40 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
243
267
|
return
|
|
244
268
|
}
|
|
245
269
|
|
|
246
|
-
let
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid 'fileUrl' provided: \(fileUrlString)"])
|
|
253
|
-
reject("INVALID_FILE_URL", error.localizedDescription, error)
|
|
254
|
-
return
|
|
255
|
-
}
|
|
256
|
-
fileUrl = url
|
|
270
|
+
guard let manifestFileHash = data["manifestFileHash"] as? String,
|
|
271
|
+
!manifestFileHash.isEmpty else {
|
|
272
|
+
let error = NSError(domain: "HotUpdater", code: 0,
|
|
273
|
+
userInfo: [NSLocalizedDescriptionKey: "Missing manifest file hash"])
|
|
274
|
+
reject("INVALID_MANIFEST", error.localizedDescription, error)
|
|
275
|
+
return
|
|
257
276
|
}
|
|
258
|
-
|
|
259
|
-
// Extract fileHash if provided
|
|
260
|
-
let fileHash = data["fileHash"] as? String
|
|
261
|
-
let manifestFileHash = data["manifestFileHash"] as? String
|
|
262
277
|
let channel = data["channel"] as? String
|
|
263
278
|
let manifestUrlString = data["manifestUrl"] as? String ?? ""
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
279
|
+
guard let manifestUrl = URL(string: manifestUrlString) else {
|
|
280
|
+
let error = NSError(domain: "HotUpdater", code: 0,
|
|
281
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid 'manifestUrl' provided: \(manifestUrlString)"])
|
|
282
|
+
reject("INVALID_FILE_URL", error.localizedDescription, error)
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
let archiveUrl: URL?
|
|
286
|
+
if let archiveUrlString = data["archiveUrl"] as? String {
|
|
287
|
+
guard let parsedArchiveUrl = URL(string: archiveUrlString) else {
|
|
267
288
|
let error = NSError(domain: "HotUpdater", code: 0,
|
|
268
|
-
userInfo: [NSLocalizedDescriptionKey: "Invalid '
|
|
289
|
+
userInfo: [NSLocalizedDescriptionKey: "Invalid 'archiveUrl' provided: \(archiveUrlString)"])
|
|
269
290
|
reject("INVALID_FILE_URL", error.localizedDescription, error)
|
|
270
291
|
return
|
|
271
292
|
}
|
|
272
|
-
|
|
293
|
+
archiveUrl = parsedArchiveUrl
|
|
294
|
+
} else {
|
|
295
|
+
archiveUrl = nil
|
|
273
296
|
}
|
|
274
|
-
let
|
|
275
|
-
|
|
297
|
+
guard let assetsPayload = data["assets"] as? [String: [String: Any]] else {
|
|
298
|
+
let error = NSError(domain: "HotUpdater", code: 0,
|
|
299
|
+
userInfo: [NSLocalizedDescriptionKey: "Missing manifest assets"])
|
|
300
|
+
reject("INVALID_MANIFEST", error.localizedDescription, error)
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
let assets = assetsPayload.reduce(into: [String: ChangedAssetDescriptor]()) { partialResult, entry in
|
|
276
304
|
guard let fileHash = entry.value["fileHash"] as? String,
|
|
277
305
|
!fileHash.isEmpty
|
|
278
306
|
else {
|
|
@@ -296,7 +324,8 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
296
324
|
baseBundleId: baseBundleId,
|
|
297
325
|
baseFileHash: baseFileHash,
|
|
298
326
|
patchFileHash: patchFileHash,
|
|
299
|
-
patchUrl: patchUrl
|
|
327
|
+
patchUrl: patchUrl,
|
|
328
|
+
byteSize: Self.parseSafeByteSize(payload["byteSize"])
|
|
300
329
|
)
|
|
301
330
|
}
|
|
302
331
|
let filePayload = entry.value["file"] as? [String: Any]
|
|
@@ -327,10 +356,10 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
327
356
|
// Extract progress callback if provided
|
|
328
357
|
let progressCallback = data["progressCallback"] as? RCTResponseSenderBlock
|
|
329
358
|
|
|
330
|
-
NSLog("[HotUpdaterImpl] updateBundle called with bundleId: \(bundleId),
|
|
359
|
+
NSLog("[HotUpdaterImpl] updateBundle called with bundleId: \(bundleId), manifestUrl: \(manifestUrl.absoluteString)")
|
|
331
360
|
|
|
332
361
|
// Heavy work is delegated to bundle storage service with safe error handling
|
|
333
|
-
bundleStorage.updateBundle(bundleId: bundleId,
|
|
362
|
+
bundleStorage.updateBundle(bundleId: bundleId, manifestUrl: manifestUrl, manifestFileHash: manifestFileHash, archiveUrl: archiveUrl, assets: assets, progressHandler: { payload in
|
|
334
363
|
DispatchQueue.main.async {
|
|
335
364
|
NotificationCenter.default.post(
|
|
336
365
|
name: .updateProgressDidChange,
|
|
@@ -532,7 +561,6 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
532
561
|
"DIRECTORY_CREATION_FAILED",
|
|
533
562
|
"DOWNLOAD_FAILED",
|
|
534
563
|
"INCOMPLETE_DOWNLOAD",
|
|
535
|
-
"EXTRACTION_FORMAT_ERROR",
|
|
536
564
|
"INVALID_BUNDLE",
|
|
537
565
|
"INSUFFICIENT_DISK_SPACE",
|
|
538
566
|
"SIGNATURE_VERIFICATION_FAILED",
|
|
@@ -545,6 +573,11 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
545
573
|
// MARK: - Rollback Support
|
|
546
574
|
|
|
547
575
|
public func notifyAppReady() -> [String: Any] {
|
|
576
|
+
// A failed runtime can finish queued native calls while recovery starts.
|
|
577
|
+
// Keep its readiness loop from consuming the new launch's report.
|
|
578
|
+
if hotUpdaterIsFailedRuntime().boolValue {
|
|
579
|
+
return ["status": "PENDING"]
|
|
580
|
+
}
|
|
548
581
|
return bundleStorage.notifyAppReady()
|
|
549
582
|
}
|
|
550
583
|
|
|
@@ -597,7 +630,7 @@ private func hotUpdaterGetMinBundleId() -> String {
|
|
|
597
630
|
|
|
598
631
|
/**
|
|
599
632
|
* Gets the current active bundle ID from bundle storage.
|
|
600
|
-
* Reads
|
|
633
|
+
* Reads the required OTA manifest.json.
|
|
601
634
|
* Built-in bundle fallback is handled in JS.
|
|
602
635
|
*/
|
|
603
636
|
public func getBundleId() -> String? {
|
|
@@ -672,6 +705,7 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
672
705
|
private var handlersInstalled = false
|
|
673
706
|
private var isMonitoring = false
|
|
674
707
|
private var recoveryRequested = false
|
|
708
|
+
private var monitoringGeneration = 0
|
|
675
709
|
private var currentBundleId: String?
|
|
676
710
|
private var shouldRollbackOnCrash = false
|
|
677
711
|
private var contentAppearedCallback: ((String?) -> Void)?
|
|
@@ -709,6 +743,10 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
709
743
|
shouldRollback: Bool,
|
|
710
744
|
onContentAppeared: @escaping (String?) -> Void
|
|
711
745
|
) {
|
|
746
|
+
objc_sync_enter(self)
|
|
747
|
+
defer { objc_sync_exit(self) }
|
|
748
|
+
|
|
749
|
+
monitoringGeneration += 1
|
|
712
750
|
currentBundleId = bundleId
|
|
713
751
|
shouldRollbackOnCrash = shouldRollback
|
|
714
752
|
recoveryRequested = false
|
|
@@ -719,6 +757,7 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
719
757
|
stopMonitoringWorkItem = nil
|
|
720
758
|
|
|
721
759
|
installHandlersIfNeeded()
|
|
760
|
+
hotUpdaterInstallRecoveryHooks()
|
|
722
761
|
registerObservers()
|
|
723
762
|
installSignalHandlersIfNeeded()
|
|
724
763
|
hotUpdaterUpdateSignalLaunchState(bundleId, shouldRollback: shouldRollback)
|
|
@@ -726,9 +765,8 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
726
765
|
|
|
727
766
|
func handleUncaughtException(_ exception: NSException) {
|
|
728
767
|
writeCrashMarker()
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
}
|
|
768
|
+
// An uncaught native exception cannot safely continue in this process.
|
|
769
|
+
// Preserve the marker so the next launch can select the safe bundle.
|
|
732
770
|
previousUncaughtExceptionHandler?(exception)
|
|
733
771
|
}
|
|
734
772
|
|
|
@@ -742,16 +780,22 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
742
780
|
previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler()
|
|
743
781
|
|
|
744
782
|
RCTSetFatalHandler { [weak self] error in
|
|
745
|
-
|
|
746
|
-
if self?.requestRecoveryReloadIfNeeded(
|
|
747
|
-
self?.
|
|
783
|
+
guard !hotUpdaterIsFailedRuntime().boolValue else { return }
|
|
784
|
+
if self?.requestRecoveryReloadIfNeeded(onFailure: {
|
|
785
|
+
self?.forwardFatalError(error)
|
|
786
|
+
}) != true {
|
|
787
|
+
self?.writeCrashMarker()
|
|
788
|
+
self?.forwardFatalError(error)
|
|
748
789
|
}
|
|
749
790
|
}
|
|
750
791
|
|
|
751
792
|
RCTSetFatalExceptionHandler { [weak self] exception in
|
|
752
|
-
|
|
753
|
-
if self?.requestRecoveryReloadIfNeeded(
|
|
754
|
-
self?.
|
|
793
|
+
guard !hotUpdaterIsFailedRuntime().boolValue else { return }
|
|
794
|
+
if self?.requestRecoveryReloadIfNeeded(onFailure: {
|
|
795
|
+
self?.forwardFatalException(exception)
|
|
796
|
+
}) != true {
|
|
797
|
+
self?.writeCrashMarker()
|
|
798
|
+
self?.forwardFatalException(exception)
|
|
755
799
|
}
|
|
756
800
|
}
|
|
757
801
|
|
|
@@ -759,6 +803,33 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
759
803
|
handlersInstalled = true
|
|
760
804
|
}
|
|
761
805
|
|
|
806
|
+
private func forwardFatalError(_ error: Error?) {
|
|
807
|
+
if let previousFatalHandler {
|
|
808
|
+
previousFatalHandler(error)
|
|
809
|
+
} else {
|
|
810
|
+
let handler = RCTGetFatalHandler()
|
|
811
|
+
RCTSetFatalHandler(nil)
|
|
812
|
+
RCTFatal(error)
|
|
813
|
+
// React Native's DEBUG default handler can return.
|
|
814
|
+
if RCTGetFatalHandler() == nil {
|
|
815
|
+
RCTSetFatalHandler(handler)
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
private func forwardFatalException(_ exception: NSException?) {
|
|
821
|
+
if let previousFatalExceptionHandler {
|
|
822
|
+
previousFatalExceptionHandler(exception)
|
|
823
|
+
} else {
|
|
824
|
+
let handler = RCTGetFatalExceptionHandler()
|
|
825
|
+
RCTSetFatalExceptionHandler(nil)
|
|
826
|
+
RCTFatalException(exception)
|
|
827
|
+
if RCTGetFatalExceptionHandler() == nil {
|
|
828
|
+
RCTSetFatalExceptionHandler(handler)
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
762
833
|
private func installSignalHandlersIfNeeded() {
|
|
763
834
|
guard !signalHandlersInstalled else {
|
|
764
835
|
return
|
|
@@ -777,7 +848,7 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
777
848
|
unregisterObservers()
|
|
778
849
|
NotificationCenter.default.addObserver(
|
|
779
850
|
self,
|
|
780
|
-
selector: #selector(handleJavaScriptDidFailToLoad),
|
|
851
|
+
selector: #selector(handleJavaScriptDidFailToLoad(_:)),
|
|
781
852
|
name: NSNotification.Name.RCTJavaScriptDidFailToLoad,
|
|
782
853
|
object: nil
|
|
783
854
|
)
|
|
@@ -802,15 +873,25 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
802
873
|
)
|
|
803
874
|
}
|
|
804
875
|
|
|
805
|
-
@objc private func handleJavaScriptDidFailToLoad() {
|
|
806
|
-
|
|
876
|
+
@objc private func handleJavaScriptDidFailToLoad(_ notification: Notification) {
|
|
877
|
+
let error = notification.userInfo?["error"] as? NSError ?? NSError(
|
|
878
|
+
domain: "HotUpdaterRecovery",
|
|
879
|
+
code: 1,
|
|
880
|
+
userInfo: [NSLocalizedDescriptionKey: "JavaScript bundle failed to load"]
|
|
881
|
+
)
|
|
882
|
+
if requestRecoveryReloadIfNeeded(onFailure: { [weak self] in
|
|
883
|
+
self?.forwardFatalError(error)
|
|
884
|
+
}) {
|
|
807
885
|
return
|
|
808
886
|
}
|
|
809
887
|
unregisterObservers()
|
|
810
888
|
}
|
|
811
889
|
|
|
812
890
|
@objc private func handleContentDidAppear() {
|
|
813
|
-
|
|
891
|
+
objc_sync_enter(self)
|
|
892
|
+
defer { objc_sync_exit(self) }
|
|
893
|
+
|
|
894
|
+
guard isMonitoring, !recoveryRequested else {
|
|
814
895
|
return
|
|
815
896
|
}
|
|
816
897
|
|
|
@@ -820,14 +901,19 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
820
901
|
hotUpdaterUpdateSignalLaunchState(currentBundleId, shouldRollback: false)
|
|
821
902
|
|
|
822
903
|
stopMonitoringWorkItem?.cancel()
|
|
904
|
+
let generation = monitoringGeneration
|
|
823
905
|
let workItem = DispatchWorkItem { [weak self] in
|
|
824
|
-
self?.finishMonitoring()
|
|
906
|
+
self?.finishMonitoring(generation: generation)
|
|
825
907
|
}
|
|
826
908
|
stopMonitoringWorkItem = workItem
|
|
827
909
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(10), execute: workItem)
|
|
828
910
|
}
|
|
829
911
|
|
|
830
|
-
private func finishMonitoring() {
|
|
912
|
+
private func finishMonitoring(generation: Int) {
|
|
913
|
+
objc_sync_enter(self)
|
|
914
|
+
defer { objc_sync_exit(self) }
|
|
915
|
+
guard monitoringGeneration == generation else { return }
|
|
916
|
+
|
|
831
917
|
isMonitoring = false
|
|
832
918
|
recoveryRequested = false
|
|
833
919
|
stopMonitoringWorkItem = nil
|
|
@@ -837,31 +923,40 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
837
923
|
hotUpdaterUpdateSignalLaunchState(nil, shouldRollback: false)
|
|
838
924
|
}
|
|
839
925
|
|
|
840
|
-
private func requestRecoveryReloadIfNeeded() -> Bool {
|
|
926
|
+
private func requestRecoveryReloadIfNeeded(onFailure: @escaping () -> Void) -> Bool {
|
|
927
|
+
objc_sync_enter(self)
|
|
928
|
+
defer { objc_sync_exit(self) }
|
|
929
|
+
|
|
841
930
|
guard isMonitoring, shouldRollbackOnCrash else {
|
|
842
931
|
return false
|
|
843
932
|
}
|
|
844
933
|
|
|
845
|
-
|
|
934
|
+
hotUpdaterMarkFailedRuntime()
|
|
846
935
|
if recoveryRequested {
|
|
847
|
-
objc_sync_exit(self)
|
|
848
936
|
return true
|
|
849
937
|
}
|
|
850
938
|
recoveryRequested = true
|
|
851
|
-
|
|
939
|
+
writeCrashMarker()
|
|
852
940
|
|
|
853
941
|
let bundleId = currentBundleId
|
|
854
|
-
|
|
942
|
+
let generation = monitoringGeneration
|
|
943
|
+
hotUpdaterAfterSurfaceStarts { [weak self] in
|
|
855
944
|
guard let self else {
|
|
856
945
|
return
|
|
857
946
|
}
|
|
858
947
|
|
|
948
|
+
objc_sync_enter(self)
|
|
949
|
+
let isCurrentLaunch = self.monitoringGeneration == generation
|
|
950
|
+
objc_sync_exit(self)
|
|
951
|
+
guard isCurrentLaunch else { return }
|
|
952
|
+
|
|
859
953
|
let started = hotUpdaterPerformRecoveryReload()
|
|
860
954
|
if !started {
|
|
861
955
|
objc_sync_enter(self)
|
|
862
956
|
self.recoveryRequested = false
|
|
863
957
|
objc_sync_exit(self)
|
|
864
958
|
NSLog("[HotUpdaterRecovery] Failed to trigger recovery reload")
|
|
959
|
+
onFailure()
|
|
865
960
|
} else {
|
|
866
961
|
NSLog("[HotUpdaterRecovery] Triggered recovery reload for bundleId=\(bundleId ?? "nil")")
|
|
867
962
|
}
|
|
@@ -871,6 +966,9 @@ final class HotUpdaterRecoveryManager: NSObject {
|
|
|
871
966
|
}
|
|
872
967
|
|
|
873
968
|
private func writeCrashMarker() {
|
|
969
|
+
objc_sync_enter(self)
|
|
970
|
+
defer { objc_sync_exit(self) }
|
|
971
|
+
|
|
874
972
|
guard isMonitoring else {
|
|
875
973
|
return
|
|
876
974
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
3
|
extension Notification.Name {
|
|
4
|
-
static let downloadProgressUpdate = Notification.Name("HotUpdaterDownloadProgressUpdate")
|
|
5
|
-
static let downloadDidFinish = Notification.Name("HotUpdaterDownloadDidFinish")
|
|
6
4
|
static let updateProgressDidChange = Notification.Name("HotUpdaterUpdateProgressDidChange")
|
|
7
5
|
}
|