pulse-updates 1.0.5 → 1.0.7
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.
|
@@ -99,6 +99,10 @@ class PulseController private constructor() {
|
|
|
99
99
|
private var embeddedManifest: EmbeddedManifest? = null
|
|
100
100
|
private val executor = Executors.newSingleThreadExecutor()
|
|
101
101
|
|
|
102
|
+
// Cached manifest from last check (to avoid duplicate requests in fetch)
|
|
103
|
+
// Only valid for the immediate check->fetch sequence, cleared on use or new check
|
|
104
|
+
private var lastCheckManifest: ManifestModel? = null
|
|
105
|
+
|
|
102
106
|
// Embedded asset hashes set for download optimization
|
|
103
107
|
internal val embeddedAssetHashes: Set<String>
|
|
104
108
|
get() = embeddedManifest?.assets
|
|
@@ -526,11 +530,26 @@ class PulseController private constructor() {
|
|
|
526
530
|
}
|
|
527
531
|
|
|
528
532
|
executor.execute {
|
|
529
|
-
|
|
533
|
+
// Clear any previous cached manifest before new check
|
|
534
|
+
lastCheckManifest = null
|
|
535
|
+
|
|
536
|
+
PulseRemoteLoader.checkForUpdate(cfg, launchedUpdate?.updateId) { result ->
|
|
537
|
+
// Cache the manifest if update is available (for use by fetchUpdate)
|
|
538
|
+
result.fold(
|
|
539
|
+
onSuccess = { checkResult ->
|
|
540
|
+
if (checkResult.isAvailable && checkResult.manifest != null) {
|
|
541
|
+
lastCheckManifest = checkResult.manifest
|
|
542
|
+
pulseLog(TAG, "checkForUpdate: cached manifest for fetch")
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
onFailure = { }
|
|
546
|
+
)
|
|
547
|
+
callback(result)
|
|
548
|
+
}
|
|
530
549
|
}
|
|
531
550
|
}
|
|
532
551
|
|
|
533
|
-
fun fetchUpdate(callback: (Result<FetchResult>) -> Unit) {
|
|
552
|
+
fun fetchUpdate(cachedManifest: ManifestModel? = null, callback: (Result<FetchResult>) -> Unit) {
|
|
534
553
|
val cfg = config
|
|
535
554
|
val ctx = context
|
|
536
555
|
val dir = directory
|
|
@@ -545,8 +564,18 @@ class PulseController private constructor() {
|
|
|
545
564
|
return
|
|
546
565
|
}
|
|
547
566
|
|
|
567
|
+
// Use cached manifest from recent check if available
|
|
568
|
+
var manifestToUse = cachedManifest
|
|
569
|
+
if (manifestToUse == null && lastCheckManifest != null) {
|
|
570
|
+
pulseLog(TAG, "fetchUpdate: using cached manifest from recent check")
|
|
571
|
+
manifestToUse = lastCheckManifest
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Clear the cache after using it
|
|
575
|
+
lastCheckManifest = null
|
|
576
|
+
|
|
548
577
|
executor.execute {
|
|
549
|
-
PulseRemoteLoader.fetchUpdate(cfg, database, dir, callback)
|
|
578
|
+
PulseRemoteLoader.fetchUpdate(cfg, database, dir, manifestToUse, callback)
|
|
550
579
|
}
|
|
551
580
|
}
|
|
552
581
|
|
|
@@ -1141,8 +1170,22 @@ object PulseRemoteLoader {
|
|
|
1141
1170
|
config: PulseUpdatesConfig,
|
|
1142
1171
|
database: PulseDatabase?,
|
|
1143
1172
|
directory: File,
|
|
1173
|
+
cachedManifest: ManifestModel? = null,
|
|
1144
1174
|
callback: (Result<FetchResult>) -> Unit
|
|
1145
1175
|
) {
|
|
1176
|
+
// If we already have a manifest from a previous check, use it directly
|
|
1177
|
+
if (cachedManifest != null) {
|
|
1178
|
+
pulseLog(TAG, "fetchUpdate: using cached manifest, skipping check")
|
|
1179
|
+
downloadUpdate(cachedManifest, config, database, directory) { downloadResult ->
|
|
1180
|
+
downloadResult.fold(
|
|
1181
|
+
onSuccess = { callback(Result.success(FetchResult(isNew = true, manifest = cachedManifest))) },
|
|
1182
|
+
onFailure = { callback(Result.failure(it)) }
|
|
1183
|
+
)
|
|
1184
|
+
}
|
|
1185
|
+
return
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// No cached manifest, need to check first
|
|
1146
1189
|
checkForUpdate(config, PulseController.getInstance().launchedUpdate?.updateId) { result ->
|
|
1147
1190
|
result.fold(
|
|
1148
1191
|
onSuccess = { checkResult ->
|
|
@@ -1265,6 +1308,10 @@ object PulseRemoteLoader {
|
|
|
1265
1308
|
|
|
1266
1309
|
// Download assets and link them to the update
|
|
1267
1310
|
val embeddedHashes = PulseController.getInstance().embeddedAssetHashes
|
|
1311
|
+
pulseLog(TAG, "downloadUpdate: embeddedHashes count=${embeddedHashes.size}")
|
|
1312
|
+
if (embeddedHashes.isNotEmpty()) {
|
|
1313
|
+
pulseLog(TAG, "downloadUpdate: first few embedded hashes: ${embeddedHashes.take(3).map { it.take(16) }}")
|
|
1314
|
+
}
|
|
1268
1315
|
|
|
1269
1316
|
for (asset in manifest.assets) {
|
|
1270
1317
|
val assetHash = asset.hash.lowercase()
|
|
@@ -473,8 +473,8 @@ struct PulseEmbeddedAsset {
|
|
|
473
473
|
self.hash = hash
|
|
474
474
|
self.key = json["key"] as? String
|
|
475
475
|
self.type = json["type"] as? String ?? json["contentType"] as? String
|
|
476
|
-
self.nsBundleDir = json["nsBundleDir"] as? String
|
|
477
|
-
self.nsBundleFilename = json["nsBundleFilename"] as? String
|
|
476
|
+
self.nsBundleDir = json["mainBundleDir"] as? String ?? json["nsBundleDir"] as? String
|
|
477
|
+
self.nsBundleFilename = json["mainBundleFilename"] as? String ?? json["nsBundleFilename"] as? String
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
480
|
|
|
@@ -66,6 +66,10 @@ public final class PulseController {
|
|
|
66
66
|
internal var embeddedAssetHashes: [String: URL] = [:]
|
|
67
67
|
private var embeddedBundleHash: String?
|
|
68
68
|
|
|
69
|
+
// Cached manifest from last check (to avoid duplicate requests in fetch)
|
|
70
|
+
// Only valid for the immediate check->fetch sequence, cleared on use or new check
|
|
71
|
+
private var lastCheckManifest: PulseManifestModel?
|
|
72
|
+
|
|
69
73
|
// MARK: - Native Config (from Info.plist, like expo-updates)
|
|
70
74
|
|
|
71
75
|
/// Load config from Info.plist (called before JS starts)
|
|
@@ -404,12 +408,12 @@ public final class PulseController {
|
|
|
404
408
|
private func performBackgroundUpdateCheck() {
|
|
405
409
|
pulseLog("Performing background update check")
|
|
406
410
|
|
|
407
|
-
checkForUpdate { [weak self] result in
|
|
411
|
+
checkForUpdate { [weak self] (result: Result<PulseCheckResult, Error>) in
|
|
408
412
|
switch result {
|
|
409
413
|
case .success(let checkResult):
|
|
410
414
|
if checkResult.isAvailable {
|
|
411
415
|
pulseLog("Background check: update available, fetching...")
|
|
412
|
-
self?.fetchUpdate { fetchResult in
|
|
416
|
+
self?.fetchUpdate { (fetchResult: Result<PulseFetchResult, Error>) in
|
|
413
417
|
switch fetchResult {
|
|
414
418
|
case .success(let fetch):
|
|
415
419
|
if fetch.isNew {
|
|
@@ -475,15 +479,24 @@ public final class PulseController {
|
|
|
475
479
|
return
|
|
476
480
|
}
|
|
477
481
|
|
|
482
|
+
// Clear any previous cached manifest before new check
|
|
483
|
+
lastCheckManifest = nil
|
|
484
|
+
|
|
478
485
|
PulseRemoteLoader.checkForUpdate(
|
|
479
486
|
config: config,
|
|
480
|
-
currentUpdateId: launchedUpdate?.updateId
|
|
481
|
-
|
|
482
|
-
|
|
487
|
+
currentUpdateId: launchedUpdate?.updateId
|
|
488
|
+
) { [weak self] (result: Result<PulseCheckResult, Error>) in
|
|
489
|
+
// Cache the manifest for subsequent fetchUpdate call
|
|
490
|
+
if case .success(let checkResult) = result, checkResult.isAvailable {
|
|
491
|
+
self?.lastCheckManifest = checkResult.manifest
|
|
492
|
+
}
|
|
493
|
+
completion(result)
|
|
494
|
+
}
|
|
483
495
|
}
|
|
484
496
|
|
|
485
497
|
/// Fetch and download an update
|
|
486
|
-
|
|
498
|
+
/// - Parameter cachedManifest: Optional manifest from a previous checkForUpdate call to avoid duplicate request
|
|
499
|
+
public func fetchUpdate(cachedManifest: PulseManifestModel? = nil, completion: @escaping (Result<PulseFetchResult, Error>) -> Void) {
|
|
487
500
|
guard let config = config else {
|
|
488
501
|
completion(.failure(PulseUpdatesError.notConfigured))
|
|
489
502
|
return
|
|
@@ -494,10 +507,21 @@ public final class PulseController {
|
|
|
494
507
|
return
|
|
495
508
|
}
|
|
496
509
|
|
|
510
|
+
// Use cached manifest from recent checkForUpdate if available
|
|
511
|
+
var manifestToUse = cachedManifest
|
|
512
|
+
if manifestToUse == nil, let cached = lastCheckManifest {
|
|
513
|
+
pulseLog("fetchUpdate: using cached manifest from recent check")
|
|
514
|
+
manifestToUse = cached
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Clear cache after use
|
|
518
|
+
lastCheckManifest = nil
|
|
519
|
+
|
|
497
520
|
PulseRemoteLoader.fetchUpdate(
|
|
498
521
|
config: config,
|
|
499
522
|
database: database,
|
|
500
523
|
directory: directory,
|
|
524
|
+
cachedManifest: manifestToUse,
|
|
501
525
|
completion: completion
|
|
502
526
|
)
|
|
503
527
|
}
|
|
@@ -594,7 +618,7 @@ public final class PulseController {
|
|
|
594
618
|
}
|
|
595
619
|
}
|
|
596
620
|
|
|
597
|
-
pulseLog("Loaded embedded manifest: \(embeddedManifest?.updateId ?? "unknown")")
|
|
621
|
+
pulseLog("Loaded embedded manifest: \(embeddedManifest?.updateId ?? "unknown"), embeddedAssetHashes count: \(embeddedAssetHashes.count)")
|
|
598
622
|
}
|
|
599
623
|
|
|
600
624
|
private func seedEmbeddedUpdateIfNeeded() {
|
|
@@ -661,7 +685,7 @@ public final class PulseController {
|
|
|
661
685
|
|
|
662
686
|
self.launcher = launcher
|
|
663
687
|
|
|
664
|
-
launcher.launch { [weak self] success, error in
|
|
688
|
+
launcher.launch { [weak self] (success: Bool, error: Error?) in
|
|
665
689
|
guard let self = self else {
|
|
666
690
|
completion(false)
|
|
667
691
|
return
|
|
@@ -931,10 +955,25 @@ final class PulseRemoteLoader {
|
|
|
931
955
|
config: PulseUpdatesConfig,
|
|
932
956
|
database: PulseDatabase?,
|
|
933
957
|
directory: URL,
|
|
958
|
+
cachedManifest: PulseManifestModel? = nil,
|
|
934
959
|
completion: @escaping (Result<PulseFetchResult, Error>) -> Void
|
|
935
960
|
) {
|
|
936
|
-
//
|
|
937
|
-
|
|
961
|
+
// If we already have a manifest from a previous check, use it directly
|
|
962
|
+
if let manifest = cachedManifest {
|
|
963
|
+
pulseLog("fetchUpdate: using cached manifest, skipping check")
|
|
964
|
+
downloadUpdate(manifest: manifest, config: config, database: database, directory: directory) { (downloadResult: Result<Void, Error>) in
|
|
965
|
+
switch downloadResult {
|
|
966
|
+
case .failure(let error):
|
|
967
|
+
completion(.failure(error))
|
|
968
|
+
case .success:
|
|
969
|
+
completion(.success(PulseFetchResult(isNew: true, manifest: manifest)))
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// No cached manifest, need to check first
|
|
976
|
+
checkForUpdate(config: config, currentUpdateId: PulseController.shared.launchedUpdate?.updateId) { (result: Result<PulseCheckResult, Error>) in
|
|
938
977
|
switch result {
|
|
939
978
|
case .failure(let error):
|
|
940
979
|
completion(.failure(error))
|
|
@@ -946,7 +985,7 @@ final class PulseRemoteLoader {
|
|
|
946
985
|
}
|
|
947
986
|
|
|
948
987
|
// Download the update
|
|
949
|
-
downloadUpdate(manifest: manifest, config: config, database: database, directory: directory) { downloadResult in
|
|
988
|
+
downloadUpdate(manifest: manifest, config: config, database: database, directory: directory) { (downloadResult: Result<Void, Error>) in
|
|
950
989
|
switch downloadResult {
|
|
951
990
|
case .failure(let error):
|
|
952
991
|
completion(.failure(error))
|
|
@@ -1019,7 +1058,7 @@ final class PulseRemoteLoader {
|
|
|
1019
1058
|
try? database?.addUpdate(update)
|
|
1020
1059
|
|
|
1021
1060
|
// Download bundle
|
|
1022
|
-
downloadBundle(manifest: manifest, directory: directory, stagingDir: stagingDir, database: database, updateId: updateId) { bundleResult in
|
|
1061
|
+
downloadBundle(manifest: manifest, directory: directory, stagingDir: stagingDir, database: database, updateId: updateId) { (bundleResult: Result<Void, Error>) in
|
|
1023
1062
|
switch bundleResult {
|
|
1024
1063
|
case .failure(let error):
|
|
1025
1064
|
try? database?.setStatus(.failed, forUpdateId: updateId)
|
|
@@ -1028,7 +1067,7 @@ final class PulseRemoteLoader {
|
|
|
1028
1067
|
|
|
1029
1068
|
case .success:
|
|
1030
1069
|
// Download assets
|
|
1031
|
-
downloadAssets(manifest: manifest, directory: directory, stagingDir: stagingDir, database: database, updateId: updateId) { assetsResult in
|
|
1070
|
+
downloadAssets(manifest: manifest, directory: directory, stagingDir: stagingDir, database: database, updateId: updateId) { (assetsResult: Result<Void, Error>) in
|
|
1032
1071
|
switch assetsResult {
|
|
1033
1072
|
case .failure(let error):
|
|
1034
1073
|
try? database?.setStatus(.failed, forUpdateId: updateId)
|
|
@@ -1082,7 +1121,7 @@ final class PulseRemoteLoader {
|
|
|
1082
1121
|
|
|
1083
1122
|
let tempPath = stagingDir.appendingPathComponent("bundle.tmp")
|
|
1084
1123
|
|
|
1085
|
-
downloadFile(from: url, to: tempPath) { result in
|
|
1124
|
+
downloadFile(from: url, to: tempPath) { (result: Result<Void, Error>) in
|
|
1086
1125
|
switch result {
|
|
1087
1126
|
case .failure(let error):
|
|
1088
1127
|
completion(.failure(error))
|
|
@@ -1123,6 +1162,12 @@ final class PulseRemoteLoader {
|
|
|
1123
1162
|
let group = DispatchGroup()
|
|
1124
1163
|
var downloadError: Error?
|
|
1125
1164
|
|
|
1165
|
+
let embeddedHashes = PulseController.shared.embeddedAssetHashes
|
|
1166
|
+
pulseLog("downloadAssets: embeddedAssetHashes count=\(embeddedHashes.count)")
|
|
1167
|
+
if !embeddedHashes.isEmpty {
|
|
1168
|
+
pulseLog("downloadAssets: first few embedded hashes: \(Array(embeddedHashes.keys.prefix(3)).map { String($0.prefix(16)) })")
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1126
1171
|
for asset in assets {
|
|
1127
1172
|
group.enter()
|
|
1128
1173
|
|
|
@@ -1152,7 +1197,7 @@ final class PulseRemoteLoader {
|
|
|
1152
1197
|
|
|
1153
1198
|
let tempPath = stagingDir.appendingPathComponent("asset-\(assetHash).tmp")
|
|
1154
1199
|
|
|
1155
|
-
downloadFile(from: url, to: tempPath) { result in
|
|
1200
|
+
downloadFile(from: url, to: tempPath) { (result: Result<Void, Error>) in
|
|
1156
1201
|
switch result {
|
|
1157
1202
|
case .failure(let error):
|
|
1158
1203
|
downloadError = error
|