@hot-updater/react-native 0.27.1 → 0.29.0
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 +12 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +3 -0
- package/android/src/main/cpp/CMakeLists.txt +9 -0
- package/android/src/main/cpp/HotUpdaterRecovery.cpp +143 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +325 -210
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +73 -16
- package/android/src/main/java/com/hotupdater/CohortService.kt +73 -0
- package/android/src/main/java/com/hotupdater/DecompressService.kt +28 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +1 -1
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +51 -13
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +533 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryReceiver.kt +14 -0
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +55 -0
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +19 -7
- package/android/src/newarch/HotUpdaterModule.kt +16 -25
- package/android/src/oldarch/HotUpdaterModule.kt +20 -26
- package/android/src/oldarch/HotUpdaterSpec.kt +12 -2
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +340 -232
- package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -8
- package/ios/HotUpdater/Internal/CohortService.swift +63 -0
- package/ios/HotUpdater/Internal/DecompressService.swift +53 -30
- package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +9 -1
- package/ios/HotUpdater/Internal/HotUpdater.mm +376 -70
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +7 -0
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +4 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +321 -9
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +24 -8
- package/lib/commonjs/DefaultResolver.js +3 -5
- package/lib/commonjs/DefaultResolver.js.map +1 -1
- package/lib/commonjs/checkForUpdate.js +2 -0
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +211 -39
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +443 -0
- package/lib/commonjs/native.spec.js.map +1 -0
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js +4 -5
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/DefaultResolver.js +3 -5
- package/lib/module/DefaultResolver.js.map +1 -1
- package/lib/module/checkForUpdate.js +3 -1
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +14 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +204 -34
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +442 -0
- package/lib/module/native.spec.js.map +1 -0
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js +5 -6
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +14 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +43 -23
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +32 -8
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +6 -3
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +3 -6
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +14 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +43 -23
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +32 -8
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +6 -3
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +3 -6
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/DefaultResolver.ts +4 -4
- package/src/checkForUpdate.ts +4 -0
- package/src/index.ts +21 -0
- package/src/native.spec.ts +480 -0
- package/src/native.ts +285 -39
- package/src/specs/NativeHotUpdater.ts +36 -6
- package/src/types.ts +7 -3
- package/src/wrap.tsx +8 -12
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import React
|
|
3
3
|
|
|
4
|
+
private func hotUpdaterUncaughtExceptionHandler(_ exception: NSException) {
|
|
5
|
+
HotUpdaterRecoveryManager.shared.handleUncaughtException(exception)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@_silgen_name("HotUpdaterInstallSignalHandlers")
|
|
9
|
+
private func hotUpdaterInstallSignalHandlersSymbol(_ crashMarkerPath: NSString)
|
|
10
|
+
|
|
11
|
+
@_silgen_name("HotUpdaterUpdateSignalLaunchState")
|
|
12
|
+
private func hotUpdaterUpdateSignalLaunchStateSymbol(
|
|
13
|
+
_ bundleId: NSString?,
|
|
14
|
+
_ shouldRollback: ObjCBool
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
@_silgen_name("HotUpdaterPerformRecoveryReload")
|
|
18
|
+
private func hotUpdaterPerformRecoveryReloadSymbol() -> ObjCBool
|
|
19
|
+
|
|
20
|
+
private func hotUpdaterInstallSignalHandlers(_ crashMarkerPath: String) {
|
|
21
|
+
hotUpdaterInstallSignalHandlersSymbol(crashMarkerPath as NSString)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private func hotUpdaterUpdateSignalLaunchState(_ bundleId: String?, shouldRollback: Bool) {
|
|
25
|
+
hotUpdaterUpdateSignalLaunchStateSymbol(bundleId as NSString?, ObjCBool(shouldRollback))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private func hotUpdaterPerformRecoveryReload() -> Bool {
|
|
29
|
+
return hotUpdaterPerformRecoveryReloadSymbol().boolValue
|
|
30
|
+
}
|
|
31
|
+
|
|
4
32
|
@objcMembers public class HotUpdaterImpl: NSObject {
|
|
5
33
|
private let bundleStorage: BundleStorageService
|
|
6
34
|
private let preferences: PreferencesService
|
|
35
|
+
private let cohortService: CohortService
|
|
36
|
+
private let recoveryManager: HotUpdaterRecoveryManager
|
|
37
|
+
private var currentLaunchSelection: LaunchSelection?
|
|
7
38
|
|
|
8
39
|
private static let DEFAULT_CHANNEL = "production"
|
|
9
40
|
private static let CHANNEL_STORAGE_KEY = "HotUpdaterChannel"
|
|
@@ -27,8 +58,9 @@ import React
|
|
|
27
58
|
preferences: preferences,
|
|
28
59
|
isolationKey: isolationKey
|
|
29
60
|
)
|
|
61
|
+
let recoveryManager = HotUpdaterRecoveryManager.shared
|
|
30
62
|
|
|
31
|
-
self.init(bundleStorage: bundleStorage, preferences: preferences)
|
|
63
|
+
self.init(bundleStorage: bundleStorage, preferences: preferences, recoveryManager: recoveryManager)
|
|
32
64
|
}
|
|
33
65
|
|
|
34
66
|
/**
|
|
@@ -36,9 +68,11 @@ import React
|
|
|
36
68
|
* @param bundleStorage Service for bundle storage operations
|
|
37
69
|
* @param preferences Service for preference storage
|
|
38
70
|
*/
|
|
39
|
-
internal init(bundleStorage: BundleStorageService, preferences: PreferencesService) {
|
|
71
|
+
internal init(bundleStorage: BundleStorageService, preferences: PreferencesService, recoveryManager: HotUpdaterRecoveryManager) {
|
|
40
72
|
self.bundleStorage = bundleStorage
|
|
41
73
|
self.preferences = preferences
|
|
74
|
+
self.cohortService = CohortService()
|
|
75
|
+
self.recoveryManager = recoveryManager
|
|
42
76
|
super.init()
|
|
43
77
|
|
|
44
78
|
// Configure preferences with isolation key
|
|
@@ -112,6 +146,16 @@ import React
|
|
|
112
146
|
return Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_FINGERPRINT_HASH") as? String
|
|
113
147
|
}
|
|
114
148
|
|
|
149
|
+
// MARK: - Cohort Management
|
|
150
|
+
|
|
151
|
+
public func setCohort(_ cohort: String) {
|
|
152
|
+
cohortService.setCohort(cohort)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public func getCohort() -> String {
|
|
156
|
+
return cohortService.getCohort()
|
|
157
|
+
}
|
|
158
|
+
|
|
115
159
|
// MARK: - Bundle URL Management
|
|
116
160
|
|
|
117
161
|
/**
|
|
@@ -120,7 +164,7 @@ import React
|
|
|
120
164
|
* @return URL to the bundle or nil
|
|
121
165
|
*/
|
|
122
166
|
public func bundleURL(bundle: Bundle = Bundle.main) -> URL? {
|
|
123
|
-
return
|
|
167
|
+
return prepareLaunchIfNeeded(bundle: bundle).bundleURL
|
|
124
168
|
}
|
|
125
169
|
|
|
126
170
|
// MARK: - Bundle Update
|
|
@@ -269,13 +313,11 @@ import React
|
|
|
269
313
|
// MARK: - Rollback Support
|
|
270
314
|
|
|
271
315
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* @param bundleId The ID of the currently running bundle
|
|
275
|
-
* @return true if promotion was successful or no action was needed
|
|
316
|
+
* Returns the native launch report for the current process.
|
|
317
|
+
* This is read-only; startup success and rollback are finalized before JS reads it.
|
|
276
318
|
*/
|
|
277
|
-
public func notifyAppReady(
|
|
278
|
-
return bundleStorage.notifyAppReady(
|
|
319
|
+
public func notifyAppReady() -> [String: Any] {
|
|
320
|
+
return bundleStorage.notifyAppReady()
|
|
279
321
|
}
|
|
280
322
|
|
|
281
323
|
/**
|
|
@@ -304,6 +346,26 @@ import React
|
|
|
304
346
|
return bundleStorage.getBaseURL()
|
|
305
347
|
}
|
|
306
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Gets the current active bundle ID from bundle storage.
|
|
351
|
+
* Reads manifest.json first and falls back to the legacy BUNDLE_ID file.
|
|
352
|
+
* Built-in bundle fallback is handled in JS.
|
|
353
|
+
*/
|
|
354
|
+
public func getBundleId() -> String? {
|
|
355
|
+
return bundleStorage.getBundleId()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Gets the current manifest from bundle storage.
|
|
360
|
+
*/
|
|
361
|
+
public func getManifest() -> ManifestAssets {
|
|
362
|
+
return bundleStorage.getManifest()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
public func resetLaunchPreparation() {
|
|
366
|
+
currentLaunchSelection = nil
|
|
367
|
+
}
|
|
368
|
+
|
|
307
369
|
@objc
|
|
308
370
|
public func resetChannel(_ resolver: @escaping RCTPromiseResolveBlock,
|
|
309
371
|
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
@@ -316,6 +378,7 @@ import React
|
|
|
316
378
|
} catch {
|
|
317
379
|
NSLog("[HotUpdaterImpl] Failed to clear channel override: \(error)")
|
|
318
380
|
}
|
|
381
|
+
self.currentLaunchSelection = nil
|
|
319
382
|
resolver(success)
|
|
320
383
|
case .failure(let error):
|
|
321
384
|
let normalizedCode = HotUpdaterImpl.normalizeErrorCode(from: error)
|
|
@@ -323,4 +386,253 @@ import React
|
|
|
323
386
|
reject(normalizedCode, nsError.localizedDescription, nsError)
|
|
324
387
|
}
|
|
325
388
|
}
|
|
389
|
+
|
|
390
|
+
private func prepareLaunchIfNeeded(bundle: Bundle) -> LaunchSelection {
|
|
391
|
+
if let currentLaunchSelection {
|
|
392
|
+
return currentLaunchSelection
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let pendingRecovery = recoveryManager.consumePendingCrashRecovery()
|
|
396
|
+
let selection = bundleStorage.prepareLaunch(bundle: bundle, pendingRecovery: pendingRecovery)
|
|
397
|
+
recoveryManager.startMonitoring(bundleId: selection.launchedBundleId, shouldRollback: selection.shouldRollbackOnCrash) { [weak self] launchedBundleId in
|
|
398
|
+
self?.bundleStorage.markLaunchCompleted(bundleId: launchedBundleId)
|
|
399
|
+
}
|
|
400
|
+
currentLaunchSelection = selection
|
|
401
|
+
return selection
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@objcMembers
|
|
406
|
+
final class HotUpdaterRecoveryManager: NSObject {
|
|
407
|
+
static let shared = HotUpdaterRecoveryManager()
|
|
408
|
+
|
|
409
|
+
private let crashMarkerURL: URL
|
|
410
|
+
private var previousFatalHandler: RCTFatalHandler?
|
|
411
|
+
private var previousFatalExceptionHandler: RCTFatalExceptionHandler?
|
|
412
|
+
private var previousUncaughtExceptionHandler: (@convention(c) (NSException) -> Void)?
|
|
413
|
+
|
|
414
|
+
private var signalHandlersInstalled = false
|
|
415
|
+
private var handlersInstalled = false
|
|
416
|
+
private var isMonitoring = false
|
|
417
|
+
private var recoveryRequested = false
|
|
418
|
+
private var currentBundleId: String?
|
|
419
|
+
private var shouldRollbackOnCrash = false
|
|
420
|
+
private var contentAppearedCallback: ((String?) -> Void)?
|
|
421
|
+
private var stopMonitoringWorkItem: DispatchWorkItem?
|
|
422
|
+
|
|
423
|
+
private override init() {
|
|
424
|
+
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
|
425
|
+
self.crashMarkerURL = documentsPath
|
|
426
|
+
.appendingPathComponent("bundle-store", isDirectory: true)
|
|
427
|
+
.appendingPathComponent("recovery-crash-marker.json")
|
|
428
|
+
super.init()
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
func consumePendingCrashRecovery() -> PendingCrashRecovery? {
|
|
432
|
+
guard FileManager.default.fileExists(atPath: crashMarkerURL.path) else {
|
|
433
|
+
return nil
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
defer {
|
|
437
|
+
try? FileManager.default.removeItem(at: crashMarkerURL)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
do {
|
|
441
|
+
let data = try Data(contentsOf: crashMarkerURL)
|
|
442
|
+
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
|
|
443
|
+
return PendingCrashRecovery.from(json: json)
|
|
444
|
+
} catch {
|
|
445
|
+
NSLog("[HotUpdaterRecovery] Failed to read crash marker: \(error)")
|
|
446
|
+
return nil
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
func startMonitoring(
|
|
451
|
+
bundleId: String?,
|
|
452
|
+
shouldRollback: Bool,
|
|
453
|
+
onContentAppeared: @escaping (String?) -> Void
|
|
454
|
+
) {
|
|
455
|
+
currentBundleId = bundleId
|
|
456
|
+
shouldRollbackOnCrash = shouldRollback
|
|
457
|
+
recoveryRequested = false
|
|
458
|
+
contentAppearedCallback = onContentAppeared
|
|
459
|
+
isMonitoring = true
|
|
460
|
+
|
|
461
|
+
stopMonitoringWorkItem?.cancel()
|
|
462
|
+
stopMonitoringWorkItem = nil
|
|
463
|
+
|
|
464
|
+
installHandlersIfNeeded()
|
|
465
|
+
registerObservers()
|
|
466
|
+
installSignalHandlersIfNeeded()
|
|
467
|
+
hotUpdaterUpdateSignalLaunchState(bundleId, shouldRollback: shouldRollback)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
func handleUncaughtException(_ exception: NSException) {
|
|
471
|
+
writeCrashMarker()
|
|
472
|
+
if requestRecoveryReloadIfNeeded() {
|
|
473
|
+
return
|
|
474
|
+
}
|
|
475
|
+
previousUncaughtExceptionHandler?(exception)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private func installHandlersIfNeeded() {
|
|
479
|
+
guard !handlersInstalled else {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
previousFatalHandler = RCTGetFatalHandler()
|
|
484
|
+
previousFatalExceptionHandler = RCTGetFatalExceptionHandler()
|
|
485
|
+
previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler()
|
|
486
|
+
|
|
487
|
+
RCTSetFatalHandler { [weak self] error in
|
|
488
|
+
self?.writeCrashMarker()
|
|
489
|
+
if self?.requestRecoveryReloadIfNeeded() != true {
|
|
490
|
+
self?.previousFatalHandler?(error)
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
RCTSetFatalExceptionHandler { [weak self] exception in
|
|
495
|
+
self?.writeCrashMarker()
|
|
496
|
+
if self?.requestRecoveryReloadIfNeeded() != true {
|
|
497
|
+
self?.previousFatalExceptionHandler?(exception)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
NSSetUncaughtExceptionHandler(hotUpdaterUncaughtExceptionHandler)
|
|
502
|
+
handlersInstalled = true
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
private func installSignalHandlersIfNeeded() {
|
|
506
|
+
guard !signalHandlersInstalled else {
|
|
507
|
+
return
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
try? FileManager.default.createDirectory(
|
|
511
|
+
at: crashMarkerURL.deletingLastPathComponent(),
|
|
512
|
+
withIntermediateDirectories: true,
|
|
513
|
+
attributes: nil
|
|
514
|
+
)
|
|
515
|
+
hotUpdaterInstallSignalHandlers(crashMarkerURL.path)
|
|
516
|
+
signalHandlersInstalled = true
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private func registerObservers() {
|
|
520
|
+
unregisterObservers()
|
|
521
|
+
NotificationCenter.default.addObserver(
|
|
522
|
+
self,
|
|
523
|
+
selector: #selector(handleJavaScriptDidFailToLoad),
|
|
524
|
+
name: NSNotification.Name.RCTJavaScriptDidFailToLoad,
|
|
525
|
+
object: nil
|
|
526
|
+
)
|
|
527
|
+
NotificationCenter.default.addObserver(
|
|
528
|
+
self,
|
|
529
|
+
selector: #selector(handleContentDidAppear),
|
|
530
|
+
name: NSNotification.Name.RCTContentDidAppear,
|
|
531
|
+
object: nil
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private func unregisterObservers() {
|
|
536
|
+
NotificationCenter.default.removeObserver(
|
|
537
|
+
self,
|
|
538
|
+
name: NSNotification.Name.RCTJavaScriptDidFailToLoad,
|
|
539
|
+
object: nil
|
|
540
|
+
)
|
|
541
|
+
NotificationCenter.default.removeObserver(
|
|
542
|
+
self,
|
|
543
|
+
name: NSNotification.Name.RCTContentDidAppear,
|
|
544
|
+
object: nil
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@objc private func handleJavaScriptDidFailToLoad() {
|
|
549
|
+
if requestRecoveryReloadIfNeeded() {
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
unregisterObservers()
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
@objc private func handleContentDidAppear() {
|
|
556
|
+
guard isMonitoring else {
|
|
557
|
+
return
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
unregisterObservers()
|
|
561
|
+
contentAppearedCallback?(currentBundleId)
|
|
562
|
+
shouldRollbackOnCrash = false
|
|
563
|
+
hotUpdaterUpdateSignalLaunchState(currentBundleId, shouldRollback: false)
|
|
564
|
+
|
|
565
|
+
stopMonitoringWorkItem?.cancel()
|
|
566
|
+
let workItem = DispatchWorkItem { [weak self] in
|
|
567
|
+
self?.finishMonitoring()
|
|
568
|
+
}
|
|
569
|
+
stopMonitoringWorkItem = workItem
|
|
570
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(10), execute: workItem)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private func finishMonitoring() {
|
|
574
|
+
isMonitoring = false
|
|
575
|
+
recoveryRequested = false
|
|
576
|
+
stopMonitoringWorkItem = nil
|
|
577
|
+
currentBundleId = nil
|
|
578
|
+
shouldRollbackOnCrash = false
|
|
579
|
+
contentAppearedCallback = nil
|
|
580
|
+
hotUpdaterUpdateSignalLaunchState(nil, shouldRollback: false)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
private func requestRecoveryReloadIfNeeded() -> Bool {
|
|
584
|
+
guard isMonitoring, shouldRollbackOnCrash else {
|
|
585
|
+
return false
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
objc_sync_enter(self)
|
|
589
|
+
if recoveryRequested {
|
|
590
|
+
objc_sync_exit(self)
|
|
591
|
+
return true
|
|
592
|
+
}
|
|
593
|
+
recoveryRequested = true
|
|
594
|
+
objc_sync_exit(self)
|
|
595
|
+
|
|
596
|
+
let bundleId = currentBundleId
|
|
597
|
+
DispatchQueue.main.async { [weak self] in
|
|
598
|
+
guard let self else {
|
|
599
|
+
return
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
let started = hotUpdaterPerformRecoveryReload()
|
|
603
|
+
if !started {
|
|
604
|
+
objc_sync_enter(self)
|
|
605
|
+
self.recoveryRequested = false
|
|
606
|
+
objc_sync_exit(self)
|
|
607
|
+
NSLog("[HotUpdaterRecovery] Failed to trigger recovery reload")
|
|
608
|
+
} else {
|
|
609
|
+
NSLog("[HotUpdaterRecovery] Triggered recovery reload for bundleId=\(bundleId ?? "nil")")
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return true
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
private func writeCrashMarker() {
|
|
617
|
+
guard isMonitoring else {
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
do {
|
|
622
|
+
try FileManager.default.createDirectory(
|
|
623
|
+
at: crashMarkerURL.deletingLastPathComponent(),
|
|
624
|
+
withIntermediateDirectories: true,
|
|
625
|
+
attributes: nil
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
let payload: [String: Any] = [
|
|
629
|
+
"bundleId": currentBundleId ?? NSNull(),
|
|
630
|
+
"shouldRollback": shouldRollbackOnCrash,
|
|
631
|
+
]
|
|
632
|
+
let data = try JSONSerialization.data(withJSONObject: payload)
|
|
633
|
+
try data.write(to: crashMarkerURL, options: .atomic)
|
|
634
|
+
} catch {
|
|
635
|
+
NSLog("[HotUpdaterRecovery] Failed to write crash marker: \(error)")
|
|
636
|
+
}
|
|
637
|
+
}
|
|
326
638
|
}
|
|
@@ -25,15 +25,22 @@ class TarBrDecompressionStrategy: DecompressionStrategy {
|
|
|
25
25
|
return false
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if !isBrotli {
|
|
33
|
-
NSLog("[TarBrStrategy] Invalid file: not a .tar.br or .br file")
|
|
28
|
+
guard let compressedData = try? Data(contentsOf: URL(fileURLWithPath: file)) else {
|
|
29
|
+
NSLog("[TarBrStrategy] Invalid file: cannot read file data")
|
|
30
|
+
return false
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
do {
|
|
34
|
+
let tarEntries = try readTarEntries(from: compressedData)
|
|
35
|
+
if tarEntries.isEmpty {
|
|
36
|
+
NSLog("[TarBrStrategy] Invalid file: tar archive has no entries")
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
return true
|
|
40
|
+
} catch {
|
|
41
|
+
NSLog("[TarBrStrategy] Invalid file: Brotli/TAR validation failed - \(error.localizedDescription)")
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
func decompress(file: String, to destination: String, progressHandler: @escaping (Double) -> Void) throws {
|
|
@@ -63,7 +70,7 @@ class TarBrDecompressionStrategy: DecompressionStrategy {
|
|
|
63
70
|
|
|
64
71
|
let tarEntries: [TarEntry]
|
|
65
72
|
do {
|
|
66
|
-
tarEntries = try
|
|
73
|
+
tarEntries = try readTarEntries(fromDecompressedData: decompressedData)
|
|
67
74
|
NSLog("[TarBrStrategy] Tar extraction successful, found \(tarEntries.count) entries")
|
|
68
75
|
} catch {
|
|
69
76
|
throw NSError(
|
|
@@ -94,6 +101,15 @@ class TarBrDecompressionStrategy: DecompressionStrategy {
|
|
|
94
101
|
NSLog("[TarBrStrategy] Successfully extracted all entries")
|
|
95
102
|
}
|
|
96
103
|
|
|
104
|
+
private func readTarEntries(from compressedData: Data) throws -> [TarEntry] {
|
|
105
|
+
let decompressedData = try decompressBrotli(compressedData)
|
|
106
|
+
return try readTarEntries(fromDecompressedData: decompressedData)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private func readTarEntries(fromDecompressedData decompressedData: Data) throws -> [TarEntry] {
|
|
110
|
+
return try TarContainer.open(container: decompressedData)
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
private func decompressBrotli(_ data: Data) throws -> Data {
|
|
98
114
|
let bufferSize = 64 * 1024
|
|
99
115
|
var decompressedData = Data()
|
|
@@ -15,18 +15,16 @@ var _fetchUpdateInfo = require("./fetchUpdateInfo.js");
|
|
|
15
15
|
function createDefaultResolver(baseURL) {
|
|
16
16
|
return {
|
|
17
17
|
checkUpdate: async params => {
|
|
18
|
-
// Build URL based on strategy (existing buildUpdateUrl logic)
|
|
19
18
|
let url;
|
|
19
|
+
const cohortPath = `/${encodeURIComponent(params.cohort)}`;
|
|
20
20
|
if (params.updateStrategy === "fingerprint") {
|
|
21
21
|
if (!params.fingerprintHash) {
|
|
22
22
|
throw new Error("Fingerprint hash is required");
|
|
23
23
|
}
|
|
24
|
-
url = `${baseURL}/fingerprint/${params.platform}/${params.fingerprintHash}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
24
|
+
url = `${baseURL}/fingerprint/${params.platform}/${params.fingerprintHash}/${params.channel}/${params.minBundleId}/${params.bundleId}${cohortPath}`;
|
|
25
25
|
} else {
|
|
26
|
-
url = `${baseURL}/app-version/${params.platform}/${params.appVersion}/${params.channel}/${params.minBundleId}/${params.bundleId}`;
|
|
26
|
+
url = `${baseURL}/app-version/${params.platform}/${params.appVersion}/${params.channel}/${params.minBundleId}/${params.bundleId}${cohortPath}`;
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
// Use existing fetchUpdateInfo
|
|
30
28
|
return (0, _fetchUpdateInfo.fetchUpdateInfo)({
|
|
31
29
|
url,
|
|
32
30
|
requestHeaders: params.requestHeaders,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_fetchUpdateInfo","require","createDefaultResolver","baseURL","checkUpdate","params","url","updateStrategy","fingerprintHash","Error","platform","channel","minBundleId","bundleId","appVersion","fetchUpdateInfo","requestHeaders","requestTimeout"],"sourceRoot":"../../src","sources":["DefaultResolver.ts"],"mappings":";;;;;;AACA,IAAAA,gBAAA,GAAAC,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,qBAAqBA,CAACC,OAAe,EAAsB;EACzE,OAAO;IACLC,WAAW,EAAE,MACXC,MAAiC,IACC;MAClC
|
|
1
|
+
{"version":3,"names":["_fetchUpdateInfo","require","createDefaultResolver","baseURL","checkUpdate","params","url","cohortPath","encodeURIComponent","cohort","updateStrategy","fingerprintHash","Error","platform","channel","minBundleId","bundleId","appVersion","fetchUpdateInfo","requestHeaders","requestTimeout"],"sourceRoot":"../../src","sources":["DefaultResolver.ts"],"mappings":";;;;;;AACA,IAAAA,gBAAA,GAAAC,OAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,qBAAqBA,CAACC,OAAe,EAAsB;EACzE,OAAO;IACLC,WAAW,EAAE,MACXC,MAAiC,IACC;MAClC,IAAIC,GAAW;MACf,MAAMC,UAAU,GAAG,IAAIC,kBAAkB,CAACH,MAAM,CAACI,MAAM,CAAC,EAAE;MAE1D,IAAIJ,MAAM,CAACK,cAAc,KAAK,aAAa,EAAE;QAC3C,IAAI,CAACL,MAAM,CAACM,eAAe,EAAE;UAC3B,MAAM,IAAIC,KAAK,CAAC,8BAA8B,CAAC;QACjD;QACAN,GAAG,GAAG,GAAGH,OAAO,gBAAgBE,MAAM,CAACQ,QAAQ,IAAIR,MAAM,CAACM,eAAe,IAAIN,MAAM,CAACS,OAAO,IAAIT,MAAM,CAACU,WAAW,IAAIV,MAAM,CAACW,QAAQ,GAAGT,UAAU,EAAE;MACrJ,CAAC,MAAM;QACLD,GAAG,GAAG,GAAGH,OAAO,gBAAgBE,MAAM,CAACQ,QAAQ,IAAIR,MAAM,CAACY,UAAU,IAAIZ,MAAM,CAACS,OAAO,IAAIT,MAAM,CAACU,WAAW,IAAIV,MAAM,CAACW,QAAQ,GAAGT,UAAU,EAAE;MAChJ;MAEA,OAAO,IAAAW,gCAAe,EAAC;QACrBZ,GAAG;QACHa,cAAc,EAAEd,MAAM,CAACc,cAAc;QACrCC,cAAc,EAAEf,MAAM,CAACe;MACzB,CAAC,CAAC;IACJ;EACF,CAAC;AACH","ignoreList":[]}
|
|
@@ -33,6 +33,7 @@ async function checkForUpdate(options) {
|
|
|
33
33
|
const targetChannel = explicitChannel || currentChannel;
|
|
34
34
|
const isFirstRuntimeChannelSwitchAttempt = !isSwitched && explicitChannel !== undefined && explicitChannel !== defaultChannel;
|
|
35
35
|
const requestBundleId = isFirstRuntimeChannelSwitchAttempt ? minBundleId : currentBundleId;
|
|
36
|
+
const cohort = (0, _native.getCohort)();
|
|
36
37
|
if (!currentAppVersion) {
|
|
37
38
|
options.onError?.(new _error.HotUpdaterError("Failed to get app version"));
|
|
38
39
|
return null;
|
|
@@ -54,6 +55,7 @@ async function checkForUpdate(options) {
|
|
|
54
55
|
appVersion: currentAppVersion,
|
|
55
56
|
bundleId: requestBundleId,
|
|
56
57
|
minBundleId,
|
|
58
|
+
cohort,
|
|
57
59
|
channel: targetChannel,
|
|
58
60
|
updateStrategy: options.updateStrategy,
|
|
59
61
|
fingerprintHash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_error","_native","NIL_UUID","isResetToBuiltInResponse","updateInfo","status","id","fileUrl","checkForUpdate","options","__DEV__","includes","Platform","OS","onError","HotUpdaterError","currentAppVersion","getAppVersion","platform","currentBundleId","getBundleId","minBundleId","getMinBundleId","defaultChannel","getDefaultChannel","isSwitched","isChannelSwitched","currentChannel","getChannel","explicitChannel","channel","undefined","targetChannel","isFirstRuntimeChannelSwitchAttempt","requestBundleId","error","fingerprintHash","getFingerprintHash","resolver","checkUpdate","appVersion","bundleId","updateStrategy","requestHeaders","requestTimeout","updateBundle","resetChannel","runtimeChannel","fileHash","shouldSkipCurrentBundleIdCheck"],"sourceRoot":"../../src","sources":["checkForUpdate.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_error","_native","NIL_UUID","isResetToBuiltInResponse","updateInfo","status","id","fileUrl","checkForUpdate","options","__DEV__","includes","Platform","OS","onError","HotUpdaterError","currentAppVersion","getAppVersion","platform","currentBundleId","getBundleId","minBundleId","getMinBundleId","defaultChannel","getDefaultChannel","isSwitched","isChannelSwitched","currentChannel","getChannel","explicitChannel","channel","undefined","targetChannel","isFirstRuntimeChannelSwitchAttempt","requestBundleId","cohort","getCohort","error","fingerprintHash","getFingerprintHash","resolver","checkUpdate","appVersion","bundleId","updateStrategy","requestHeaders","requestTimeout","updateBundle","resetChannel","runtimeChannel","fileHash","shouldSkipCurrentBundleIdCheck"],"sourceRoot":"../../src","sources":["checkForUpdate.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -169,6 +169,10 @@ function createHotUpdaterClient() {
|
|
|
169
169
|
* Retrieves the initial bundle ID based on the build time of the native app.
|
|
170
170
|
*/
|
|
171
171
|
getMinBundleId: _native.getMinBundleId,
|
|
172
|
+
/**
|
|
173
|
+
* Fetches the current manifest for the active bundle.
|
|
174
|
+
*/
|
|
175
|
+
getManifest: _native.getManifest,
|
|
172
176
|
/**
|
|
173
177
|
* Fetches the current channel of the app.
|
|
174
178
|
*
|
|
@@ -202,6 +206,15 @@ function createHotUpdaterClient() {
|
|
|
202
206
|
* @returns {boolean} true when a non-default channel has been applied
|
|
203
207
|
*/
|
|
204
208
|
isChannelSwitched: _native.isChannelSwitched,
|
|
209
|
+
/**
|
|
210
|
+
* Sets the persisted cohort used for rollout calculations.
|
|
211
|
+
* Call `getCohort()` first if you need to restore the initial value later.
|
|
212
|
+
*/
|
|
213
|
+
setCohort: _native.setCohort,
|
|
214
|
+
/**
|
|
215
|
+
* Gets the persisted cohort used for rollout calculations.
|
|
216
|
+
*/
|
|
217
|
+
getCohort: _native.getCohort,
|
|
205
218
|
/**
|
|
206
219
|
* Adds a listener to HotUpdater events.
|
|
207
220
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_checkForUpdate","require","_DefaultResolver","_native","_store","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_wrap","_types","registerGlobalGetBaseURL","fn","getBaseURL","globalThis","HotUpdaterGetBaseURL","global","createHotUpdaterClient","globalConfig","resolver","ensureGlobalResolver","methodName","Error","wrap","options","normalizedOptions","baseURL","rest","createDefaultResolver","requestHeaders","requestTimeout","reload","setReloadBehavior","isUpdateDownloaded","hotUpdaterStore","getSnapshot","getAppVersion","getBundleId","getMinBundleId","getChannel","getDefaultChannel","isChannelSwitched","addListener","checkForUpdate","config","mergedConfig","updateBundle","params","resetChannel","ok","setState","progress","getFingerprintHash","getCrashHistory","clearCrashHistory","HotUpdater"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA;AAKA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;
|
|
1
|
+
{"version":3,"names":["_checkForUpdate","require","_DefaultResolver","_native","_store","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_wrap","_types","registerGlobalGetBaseURL","fn","getBaseURL","globalThis","HotUpdaterGetBaseURL","global","createHotUpdaterClient","globalConfig","resolver","ensureGlobalResolver","methodName","Error","wrap","options","normalizedOptions","baseURL","rest","createDefaultResolver","requestHeaders","requestTimeout","reload","setReloadBehavior","isUpdateDownloaded","hotUpdaterStore","getSnapshot","getAppVersion","getBundleId","getMinBundleId","getManifest","getChannel","getDefaultChannel","isChannelSwitched","setCohort","getCohort","addListener","checkForUpdate","config","mergedConfig","updateBundle","params","resetChannel","ok","setState","progress","getFingerprintHash","getCrashHistory","clearCrashHistory","HotUpdater"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"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CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,kBAAkB,EAAEA,CAAA,KAAMC,sBAAe,CAACC,WAAW,CAAC,CAAC,CAACF,kBAAkB;IAE1E;AACJ;AACA;IACIG,aAAa,EAAbA,qBAAa;IAEb;AACJ;AACA;IACIC,WAAW,EAAXA,mBAAW;IAEX;AACJ;AACA;IACIC,cAAc,EAAdA,sBAAc;IAEd;AACJ;AACA;IACIC,WAAW,EAAXA,mBAAW;IAEX;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,UAAU,EAAVA,kBAAU;IAEV;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA,yBAAiB;IAEjB;AACJ;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA,yBAAiB;IAEjB;AACJ;AACA;AACA;IACIC,SAAS,EAATA,iBAAS;IAET;AACJ;AACA;IACIC,SAAS,EAATA,iBAAS;IAET;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,WAAW,EAAXA,mBAAW;IAEX;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,cAAc,EAAGC,MAA6B,IAAK;MACjD,MAAM5B,QAAQ,GAAGC,oBAAoB,CAAC,gBAAgB,CAAC;MAEvD,MAAM4B,YAA2C,GAAG;QAClD,GAAGD,MAAM;QACT5B,QAAQ;QACRU,cAAc,EAAE;UACd,GAAGX,YAAY,CAACW,cAAc;UAC9B,GAAGkB,MAAM,CAAClB;QACZ,CAAC;QACDC,cAAc,EAAEiB,MAAM,CAACjB,cAAc,IAAIZ,YAAY,CAACY;MACxD,CAAC;MAED,OAAO,IAAAgB,8BAAc,EAACE,YAAY,CAAC;IACrC,CAAC;IAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,YAAY,EAAGC,MAAoB,IAAK;MACtC9B,oBAAoB,CAAC,cAAc,CAAC;MACpC,OAAO,IAAA6B,oBAAY,EAACC,MAAM,CAAC;IAC7B,CAAC;IAED;AACJ;AACA;AACA;AACA;IACIC,YAAY,EAAE,MAAAA,CAAA,KAAY;MACxB,MAAMC,EAAE,GAAG,MAAM,IAAAD,oBAAY,EAAC,CAAC;MAC/B,IAAIC,EAAE,EAAE;QACNlB,sBAAe,CAACmB,QAAQ,CAAC;UACvBpB,kBAAkB,EAAE,KAAK;UACzBqB,QAAQ,EAAE;QACZ,CAAC,CAAC;MACJ;MACA,OAAOF,EAAE;IACX,CAAC;IAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIG,kBAAkB,EAAlBA,0BAAkB;IAElB;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,eAAe,EAAfA,uBAAe;IAEf;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA;EACF,CAAC;AACH;AAEO,MAAMC,UAAU,GAAArD,OAAA,CAAAqD,UAAA,GAAGzC,sBAAsB,CAAC,CAAC","ignoreList":[]}
|