pulse-updates 1.0.7 → 1.0.9

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.
@@ -199,6 +199,9 @@ class PulseController private constructor() {
199
199
  fun initializeWithoutStarting() {
200
200
  if (isStarted) return
201
201
 
202
+ // Mark as started to prevent re-initialization during reload
203
+ isStarted = true
204
+
202
205
  createDirectories()
203
206
  initializeDatabase()
204
207
  loadEmbeddedManifest()
@@ -538,8 +541,18 @@ class PulseController private constructor() {
538
541
  result.fold(
539
542
  onSuccess = { checkResult ->
540
543
  if (checkResult.isAvailable && checkResult.manifest != null) {
544
+ // Compare commitTime: if embedded is newer, don't show update
545
+ val embeddedCommitTime = embeddedManifest?.commitTime ?: 0L
546
+ val otaCommitTime = checkResult.manifest.commitTime?.time ?: 0L
547
+
548
+ if (embeddedCommitTime > 0 && otaCommitTime > 0 && embeddedCommitTime >= otaCommitTime) {
549
+ pulseLog(TAG, "checkForUpdate: embedded is newer or same (embedded=$embeddedCommitTime >= ota=$otaCommitTime), no update needed")
550
+ callback(Result.success(CheckResult(isAvailable = false)))
551
+ return@checkForUpdate
552
+ }
553
+
541
554
  lastCheckManifest = checkResult.manifest
542
- pulseLog(TAG, "checkForUpdate: cached manifest for fetch")
555
+ pulseLog(TAG, "checkForUpdate: cached manifest for fetch (ota=$otaCommitTime > embedded=$embeddedCommitTime)")
543
556
  }
544
557
  },
545
558
  onFailure = { }
@@ -746,6 +759,11 @@ class PulseController private constructor() {
746
759
  bundleHash = update.bundleHash,
747
760
  scopeKey = update.scopeKey
748
761
  )
762
+
763
+ // Load manifest from database for metadata (build number, etc.)
764
+ update.manifest?.let { manifest ->
765
+ launchedManifestJson = manifest.toString()
766
+ }
749
767
  }
750
768
  }
751
769
 
@@ -114,6 +114,12 @@ class PulseAppLauncher(
114
114
  try {
115
115
  val assets = database.assetsForUpdate(update.updateId)
116
116
 
117
+ pulseLog(TAG, "ensureAllAssetsExist for updateId=${update.updateId.take(16)}... found ${assets.size} assets")
118
+
119
+ // Initialize asset map with embedded assets (these are referenced directly, not copied)
120
+ val mutableAssetFilesMap = buildEmbeddedAssetsMap().toMutableMap()
121
+ pulseLog(TAG, "Embedded assets map has ${mutableAssetFilesMap.size} entries")
122
+
117
123
  if (assets.isEmpty()) {
118
124
  // No assets to verify, just find the bundle
119
125
  update.bundleHash?.let { hash ->
@@ -123,16 +129,59 @@ class PulseAppLauncher(
123
129
  return
124
130
  }
125
131
 
126
- // Initialize asset map with embedded assets (fallback)
127
- val mutableAssetFilesMap = buildEmbeddedAssetsMap().toMutableMap()
132
+ // Separate assets into those we need to verify vs those already in embedded
133
+ val assetsToVerify = mutableListOf<PulseAsset>()
134
+
135
+ for (asset in assets) {
136
+ if (asset.isLaunchAsset) {
137
+ // Always verify the launch asset (bundle) - it must be downloaded
138
+ assetsToVerify.add(asset)
139
+ } else {
140
+ val hashKey = asset.hash.lowercase()
141
+ // If asset hash is already in embedded map, we can use it directly
142
+ if (mutableAssetFilesMap.containsKey(hashKey)) {
143
+ // Already referenced from embedded, no action needed
144
+ continue
145
+ }
146
+ // Check if downloaded version exists
147
+ val localFile = assetStorePath(asset.hash)
148
+ if (localFile.exists()) {
149
+ // Use downloaded version
150
+ val localUri = localFile.toURI().toString()
151
+ mutableAssetFilesMap[hashKey] = localUri
152
+ asset.key?.let { key ->
153
+ mutableAssetFilesMap[key] = localUri
154
+ }
155
+ } else {
156
+ // Need to verify/download this asset
157
+ assetsToVerify.add(asset)
158
+ }
159
+ }
160
+ }
161
+
162
+ pulseLog(TAG, "${assets.size - assetsToVerify.size} assets from embedded, ${assetsToVerify.size} to verify/download")
128
163
 
129
- val totalAssets = assets.size
164
+ if (assetsToVerify.isEmpty()) {
165
+ // All assets accounted for, but we need the bundle
166
+ update.bundleHash?.let { hash ->
167
+ launchAssetFile = bundleStorePath(hash)
168
+ if (launchAssetFile?.exists() != true) {
169
+ callback(false, LauncherException("Bundle not found"))
170
+ return
171
+ }
172
+ }
173
+ assetFilesMap = mutableAssetFilesMap
174
+ callback(launchAssetFile != null, null)
175
+ return
176
+ }
177
+
178
+ val totalAssets = assetsToVerify.size
130
179
  val completedAssets = java.util.concurrent.atomic.AtomicInteger(0)
131
180
  val launchError = AtomicReference<Exception?>(null)
132
181
  val launchAssetFound = AtomicBoolean(false)
133
182
  val latch = CountDownLatch(totalAssets)
134
183
 
135
- for (asset in assets) {
184
+ for (asset in assetsToVerify) {
136
185
  val localFile = assetStorePath(asset.hash)
137
186
 
138
187
  Thread {
@@ -146,9 +195,8 @@ class PulseAppLauncher(
146
195
  } else {
147
196
  val localUri = localFile.toURI().toString()
148
197
  synchronized(mutableAssetFilesMap) {
149
- // Store by SHA256 hash
150
- mutableAssetFilesMap[asset.hash] = localUri
151
- // Also store by path-based key for Metro compatibility
198
+ val hashKey = asset.hash.lowercase()
199
+ mutableAssetFilesMap[hashKey] = localUri
152
200
  asset.key?.let { key ->
153
201
  mutableAssetFilesMap[key] = localUri
154
202
  }
@@ -368,11 +416,25 @@ data class EmbeddedManifest(
368
416
  fun fromJson(json: String): EmbeddedManifest? {
369
417
  return try {
370
418
  val root = org.json.JSONObject(json)
371
- val manifest = root.optJSONObject("manifest") ?: return null
419
+ // Support both formats: wrapped in "manifest" key or directly at root
420
+ val manifest = root.optJSONObject("manifest") ?: root
372
421
 
373
422
  val updateId = manifest.getString("updateId")
374
423
  val runtimeVersion = manifest.getString("runtimeVersion")
375
- val commitTime = manifest.optLong("commitTime", System.currentTimeMillis())
424
+
425
+ // Support both commitTime (ms) and createdAt (ISO 8601)
426
+ val commitTime = if (manifest.has("commitTime")) {
427
+ manifest.optLong("commitTime", System.currentTimeMillis())
428
+ } else if (manifest.has("createdAt")) {
429
+ try {
430
+ java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", java.util.Locale.US)
431
+ .parse(manifest.getString("createdAt"))?.time ?: System.currentTimeMillis()
432
+ } catch (e: Exception) {
433
+ System.currentTimeMillis()
434
+ }
435
+ } else {
436
+ System.currentTimeMillis()
437
+ }
376
438
 
377
439
  val assets = mutableListOf<EmbeddedAsset>()
378
440
 
@@ -383,9 +445,21 @@ data class EmbeddedManifest(
383
445
  }
384
446
  }
385
447
 
386
- // Parse launch asset
448
+ // Parse launch asset (either separate or from bundle field)
387
449
  manifest.optJSONObject("launchAsset")?.let { launchAssetJson ->
388
450
  EmbeddedAsset.fromJson(launchAssetJson, isLaunchAsset = true)?.let { assets.add(it) }
451
+ } ?: manifest.optJSONObject("bundle")?.let { bundleJson ->
452
+ val bundleHash = bundleJson.optString("hash", "")
453
+ if (bundleHash.isNotEmpty()) {
454
+ assets.add(EmbeddedAsset(
455
+ key = "bundle",
456
+ hash = bundleHash,
457
+ type = bundleJson.optString("contentType", null),
458
+ mainBundleDir = null,
459
+ mainBundleFilename = null,
460
+ isLaunchAsset = true
461
+ ))
462
+ }
389
463
  }
390
464
 
391
465
  EmbeddedManifest(updateId, runtimeVersion, commitTime, assets)
@@ -440,10 +514,20 @@ data class PulseUpdateInfo(
440
514
  )
441
515
 
442
516
  class PulseDefaultSelectionPolicy : PulseSelectionPolicy {
517
+ /**
518
+ * Select the best update to launch
519
+ * Priority: 1) Ready updates (newest by commitTime), 2) Embedded updates (fallback)
520
+ */
443
521
  override fun selectUpdateToLaunch(updates: List<PulseUpdate>): PulseUpdate? {
444
- return updates
445
- .filter { it.status == PulseUpdateStatus.READY || it.status == PulseUpdateStatus.EMBEDDED }
446
- .maxByOrNull { it.commitTime.time }
522
+ // First, try to find the newest ready (downloaded) update
523
+ val readyUpdates = updates.filter { it.status == PulseUpdateStatus.READY }
524
+ val newestReady = readyUpdates.maxByOrNull { it.commitTime.time }
525
+ if (newestReady != null) {
526
+ return newestReady
527
+ }
528
+
529
+ // Fall back to embedded update
530
+ return updates.find { it.status == PulseUpdateStatus.EMBEDDED }
447
531
  }
448
532
 
449
533
  override fun selectUpdatesToDelete(launchedUpdate: PulseUpdateInfo, allUpdates: List<PulseUpdateInfo>): List<PulseUpdateInfo> {
@@ -126,6 +126,9 @@ final class PulseAppLauncher {
126
126
  do {
127
127
  let assets = try database.assets(forUpdateId: update.updateId)
128
128
 
129
+ // Initialize asset map with embedded assets (these are referenced directly, not copied)
130
+ assetFilesMap = buildEmbeddedAssetsMap()
131
+
129
132
  if assets.isEmpty {
130
133
  // No assets to verify, just find the bundle
131
134
  if let bundleHash = update.bundleHash {
@@ -135,15 +138,54 @@ final class PulseAppLauncher {
135
138
  return
136
139
  }
137
140
 
138
- // Initialize asset map with embedded assets (fallback)
139
- assetFilesMap = buildEmbeddedAssetsMap()
141
+ // Separate assets into those we need to verify vs those already in embedded
142
+ var assetsToVerify: [PulseAsset] = []
143
+
144
+ for asset in assets {
145
+ if asset.isLaunchAsset {
146
+ // Always verify the launch asset (bundle) - it must be downloaded
147
+ assetsToVerify.append(asset)
148
+ } else {
149
+ let hashKey = asset.hash.lowercased()
150
+ // If asset hash is already in embedded map, we can use it directly
151
+ if assetFilesMap[hashKey] != nil {
152
+ // Already referenced from embedded, no action needed
153
+ continue
154
+ }
155
+ // Check if downloaded version exists
156
+ let localUrl = assetStorePath(hash: asset.hash)
157
+ if FileManager.default.fileExists(atPath: localUrl.path) {
158
+ // Use downloaded version
159
+ if let key = asset.key {
160
+ assetFilesMap[key] = localUrl.absoluteString
161
+ }
162
+ assetFilesMap[hashKey] = localUrl.absoluteString
163
+ } else {
164
+ // Need to verify/download this asset
165
+ assetsToVerify.append(asset)
166
+ }
167
+ }
168
+ }
169
+
170
+ if assetsToVerify.isEmpty {
171
+ // All assets accounted for, but we need the bundle
172
+ if let bundleHash = update.bundleHash {
173
+ launchAssetUrl = bundleStorePath(hash: bundleHash)
174
+ if !FileManager.default.fileExists(atPath: launchAssetUrl!.path) {
175
+ completion(false, PulseLauncherError.bundleNotFound)
176
+ return
177
+ }
178
+ }
179
+ completion(launchAssetUrl != nil, nil)
180
+ return
181
+ }
140
182
 
141
183
  completedAssets = 0
142
184
  launchError = nil
143
185
 
144
- let totalAssets = assets.count
186
+ let totalAssets = assetsToVerify.count
145
187
 
146
- for asset in assets {
188
+ for asset in assetsToVerify {
147
189
  let localUrl = assetStorePath(hash: asset.hash)
148
190
 
149
191
  ensureAssetExists(asset: asset, localUrl: localUrl) { [weak self] exists in
@@ -155,8 +197,12 @@ final class PulseAppLauncher {
155
197
  if exists {
156
198
  if asset.isLaunchAsset {
157
199
  self.launchAssetUrl = localUrl
158
- } else if let key = asset.key {
159
- self.assetFilesMap[key] = localUrl.absoluteString
200
+ } else {
201
+ let hashKey = asset.hash.lowercased()
202
+ self.assetFilesMap[hashKey] = localUrl.absoluteString
203
+ if let key = asset.key {
204
+ self.assetFilesMap[key] = localUrl.absoluteString
205
+ }
160
206
  }
161
207
  }
162
208
 
@@ -229,22 +275,44 @@ final class PulseAppLauncher {
229
275
  }
230
276
 
231
277
  guard let match = matchingAsset,
232
- let bundleFilename = match.nsBundleFilename else {
278
+ let bundleFilename = match.nsBundleFilename,
279
+ let assetType = match.type else {
233
280
  completion(false)
234
281
  return
235
282
  }
236
283
 
237
- // Get bundle path
284
+ // Get bundle path using FileManager (handles deep nested paths)
238
285
  DispatchQueue.global(qos: .userInitiated).async {
239
- let bundlePath: String?
286
+ let bundleRoot = Bundle.main.bundlePath
287
+ let fileManager = FileManager.default
288
+ let ext = assetType.components(separatedBy: "/").last ?? "png"
289
+ var sourcePath: String? = nil
290
+
291
+ // 1. Try in assets folder with subdirectory path
292
+ if let dir = match.nsBundleDir, !dir.isEmpty {
293
+ let path = "\(bundleRoot)/assets/\(dir)/\(bundleFilename).\(ext)"
294
+ if fileManager.fileExists(atPath: path) {
295
+ sourcePath = path
296
+ }
297
+ }
240
298
 
241
- if let bundleDir = match.nsBundleDir, !bundleDir.isEmpty {
242
- bundlePath = Bundle.main.path(forResource: bundleFilename, ofType: nil, inDirectory: bundleDir)
243
- } else {
244
- bundlePath = Bundle.main.path(forResource: bundleFilename, ofType: nil)
299
+ // 2. Try without assets/ prefix
300
+ if sourcePath == nil, let dir = match.nsBundleDir, !dir.isEmpty {
301
+ let path = "\(bundleRoot)/\(dir)/\(bundleFilename).\(ext)"
302
+ if fileManager.fileExists(atPath: path) {
303
+ sourcePath = path
304
+ }
245
305
  }
246
306
 
247
- guard let sourcePath = bundlePath else {
307
+ // 3. Try at root
308
+ if sourcePath == nil {
309
+ let path = "\(bundleRoot)/\(bundleFilename).\(ext)"
310
+ if fileManager.fileExists(atPath: path) {
311
+ sourcePath = path
312
+ }
313
+ }
314
+
315
+ guard let finalSourcePath = sourcePath else {
248
316
  self.launcherQueue.async {
249
317
  completion(false)
250
318
  }
@@ -260,13 +328,13 @@ final class PulseAppLauncher {
260
328
  )
261
329
 
262
330
  // Copy file
263
- try FileManager.default.copyItem(atPath: sourcePath, toPath: localUrl.path)
331
+ try FileManager.default.copyItem(atPath: finalSourcePath, toPath: localUrl.path)
264
332
 
265
333
  self.launcherQueue.async {
266
334
  completion(true)
267
335
  }
268
336
  } catch {
269
- print("[PulseUpdates] Failed to copy asset from bundle: \(error)")
337
+ pulseLogWarn("Failed to copy asset from bundle: \(error)")
270
338
  self.launcherQueue.async {
271
339
  completion(false)
272
340
  }
@@ -310,7 +378,7 @@ final class PulseAppLauncher {
310
378
  completion(true, nil)
311
379
  }
312
380
  } catch {
313
- print("[PulseUpdates] Failed to download asset: \(error)")
381
+ pulseLogWarn("Failed to download asset: \(error)")
314
382
  self.launcherQueue.async {
315
383
  completion(false, error)
316
384
  }
@@ -337,29 +405,59 @@ final class PulseAppLauncher {
337
405
 
338
406
  // MARK: - Embedded Assets Map
339
407
 
340
- /// Build map of embedded assets (key -> file URL)
408
+ /// Build map of embedded assets (hash -> file URL, key -> file URL)
409
+ /// Uses FileManager to find assets with deep nested paths
341
410
  private func buildEmbeddedAssetsMap() -> [String: String] {
342
411
  guard let embedded = embeddedManifest ?? loadEmbeddedManifest() else {
343
412
  return [:]
344
413
  }
345
414
 
346
415
  var map: [String: String] = [:]
416
+ let bundleRoot = Bundle.main.bundlePath
417
+ let fileManager = FileManager.default
347
418
 
348
419
  for asset in embedded.assets where !asset.isLaunchAsset {
349
- guard let key = asset.key,
350
- let filename = asset.nsBundleFilename else {
420
+ guard let filename = asset.nsBundleFilename,
421
+ let assetType = asset.type else {
351
422
  continue
352
423
  }
353
424
 
354
- let bundlePath: String?
425
+ // Get file extension from type (e.g., "image/png" -> "png")
426
+ let ext = assetType.components(separatedBy: "/").last ?? "png"
427
+ var bundlePath: String? = nil
428
+
429
+ // 1. Try in assets folder with subdirectory path (RN stores assets here)
355
430
  if let dir = asset.nsBundleDir, !dir.isEmpty {
356
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil, inDirectory: dir)
357
- } else {
358
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil)
431
+ let path = "\(bundleRoot)/assets/\(dir)/\(filename).\(ext)"
432
+ if fileManager.fileExists(atPath: path) {
433
+ bundlePath = path
434
+ }
435
+ }
436
+
437
+ // 2. Try without assets/ prefix
438
+ if bundlePath == nil, let dir = asset.nsBundleDir, !dir.isEmpty {
439
+ let path = "\(bundleRoot)/\(dir)/\(filename).\(ext)"
440
+ if fileManager.fileExists(atPath: path) {
441
+ bundlePath = path
442
+ }
443
+ }
444
+
445
+ // 3. Try at root
446
+ if bundlePath == nil {
447
+ let path = "\(bundleRoot)/\(filename).\(ext)"
448
+ if fileManager.fileExists(atPath: path) {
449
+ bundlePath = path
450
+ }
359
451
  }
360
452
 
361
453
  if let path = bundlePath {
362
- map[key] = URL(fileURLWithPath: path).absoluteString
454
+ let url = URL(fileURLWithPath: path).absoluteString
455
+ // Store by hash (lowercased for consistency)
456
+ map[asset.hash.lowercased()] = url
457
+ // Also store by key for Metro compatibility
458
+ if let key = asset.key {
459
+ map[key] = url
460
+ }
363
461
  }
364
462
  }
365
463
 
@@ -373,13 +471,16 @@ final class PulseAppLauncher {
373
471
  // MARK: - Embedded Manifest
374
472
 
375
473
  private func loadEmbeddedManifest() -> PulseEmbeddedManifest? {
376
- guard let url = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json"),
377
- let data = try? Data(contentsOf: url),
378
- let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
379
- return nil
474
+ // Try pulse/ subdirectory first, then root
475
+ let paths = ["pulse/embedded-manifest.json", "embedded-manifest.json"]
476
+ for path in paths {
477
+ let url = Bundle.main.bundleURL.appendingPathComponent(path)
478
+ if let data = try? Data(contentsOf: url),
479
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
480
+ return PulseEmbeddedManifest(json: json)
481
+ }
380
482
  }
381
-
382
- return PulseEmbeddedManifest(json: json)
483
+ return nil
383
484
  }
384
485
  }
385
486
 
@@ -420,8 +521,10 @@ struct PulseEmbeddedManifest {
420
521
  let assets: [PulseEmbeddedAsset]
421
522
 
422
523
  init?(json: [String: Any]) {
423
- guard let manifest = json["manifest"] as? [String: Any],
424
- let updateId = manifest["updateId"] as? String,
524
+ // Support both formats: wrapped in "manifest" key or directly at root
525
+ let manifest = (json["manifest"] as? [String: Any]) ?? json
526
+
527
+ guard let updateId = manifest["updateId"] as? String,
425
528
  let runtimeVersion = manifest["runtimeVersion"] as? String else {
426
529
  return nil
427
530
  }
@@ -431,6 +534,11 @@ struct PulseEmbeddedManifest {
431
534
 
432
535
  if let commitTimeMs = manifest["commitTime"] as? Double {
433
536
  self.commitTime = Date(timeIntervalSince1970: commitTimeMs / 1000)
537
+ } else if let createdAt = manifest["createdAt"] as? String {
538
+ // Parse ISO 8601 date
539
+ let formatter = ISO8601DateFormatter()
540
+ formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
541
+ self.commitTime = formatter.date(from: createdAt) ?? Date()
434
542
  } else {
435
543
  self.commitTime = Date()
436
544
  }
@@ -446,11 +554,22 @@ struct PulseEmbeddedManifest {
446
554
  }
447
555
  }
448
556
 
449
- // Parse launch asset
557
+ // Parse launch asset (either separate or from bundle field)
450
558
  if let launchAssetJson = manifest["launchAsset"] as? [String: Any],
451
559
  var launchAsset = PulseEmbeddedAsset(json: launchAssetJson) {
452
560
  launchAsset.isLaunchAsset = true
453
561
  parsedAssets.append(launchAsset)
562
+ } else if let bundleJson = manifest["bundle"] as? [String: Any] {
563
+ // Create launch asset from bundle field
564
+ var bundleAsset = PulseEmbeddedAsset(
565
+ key: "bundle",
566
+ hash: (bundleJson["hash"] as? String) ?? "",
567
+ type: bundleJson["contentType"] as? String,
568
+ nsBundleDir: nil,
569
+ nsBundleFilename: nil
570
+ )
571
+ bundleAsset.isLaunchAsset = true
572
+ parsedAssets.append(bundleAsset)
454
573
  }
455
574
 
456
575
  self.assets = parsedAssets
@@ -465,6 +584,14 @@ struct PulseEmbeddedAsset {
465
584
  let nsBundleFilename: String?
466
585
  var isLaunchAsset: Bool = false
467
586
 
587
+ init(key: String?, hash: String, type: String?, nsBundleDir: String?, nsBundleFilename: String?) {
588
+ self.key = key
589
+ self.hash = hash
590
+ self.type = type
591
+ self.nsBundleDir = nsBundleDir
592
+ self.nsBundleFilename = nsBundleFilename
593
+ }
594
+
468
595
  init?(json: [String: Any]) {
469
596
  guard let hash = json["hash"] as? String else {
470
597
  return nil
@@ -492,12 +619,17 @@ protocol PulseSelectionPolicy {
492
619
 
493
620
  struct PulseDefaultSelectionPolicy: PulseSelectionPolicy {
494
621
 
495
- /// Select the newest ready or embedded update by commit time
622
+ /// Select the best update to launch
623
+ /// Priority: 1) Ready updates (newest by commitTime), 2) Embedded updates (fallback)
496
624
  func selectUpdateToLaunch(from updates: [PulseUpdate]) -> PulseUpdate? {
497
- return updates
498
- .filter { $0.status == .ready || $0.status == .embedded }
499
- .sorted { $0.commitTime > $1.commitTime }
500
- .first
625
+ // First, try to find the newest ready (downloaded) update
626
+ let readyUpdates = updates.filter { $0.status == .ready }
627
+ if let newest = readyUpdates.sorted(by: { $0.commitTime > $1.commitTime }).first {
628
+ return newest
629
+ }
630
+
631
+ // Fall back to embedded update
632
+ return updates.first { $0.status == .embedded }
501
633
  }
502
634
 
503
635
  /// Select updates to delete - keeps launched update and one backup
@@ -70,6 +70,11 @@ public final class PulseController {
70
70
  // Only valid for the immediate check->fetch sequence, cleared on use or new check
71
71
  private var lastCheckManifest: PulseManifestModel?
72
72
 
73
+ // Flag to prevent concurrent fetch operations (with lock for thread safety)
74
+ private var isFetching = false
75
+ private var pendingFetchCallbacks: [(Result<PulseFetchResult, Error>) -> Void] = []
76
+ private let fetchLock = NSLock()
77
+
73
78
  // MARK: - Native Config (from Info.plist, like expo-updates)
74
79
 
75
80
  /// Load config from Info.plist (called before JS starts)
@@ -146,6 +151,9 @@ public final class PulseController {
146
151
  public func initializeWithoutStarting() {
147
152
  guard !isStarted else { return }
148
153
 
154
+ // Mark as started to prevent re-initialization during reload
155
+ isStarted = true
156
+
149
157
  createDirectories()
150
158
  initializeDatabase()
151
159
  loadEmbeddedManifest()
@@ -296,17 +304,40 @@ public final class PulseController {
296
304
  guard let manifest = embeddedManifest else { return [:] }
297
305
 
298
306
  var assets: [String: String] = [:]
307
+ let bundleRoot = Bundle.main.bundlePath
308
+ let fileManager = FileManager.default
299
309
 
300
310
  for asset in manifest.assets where !asset.isLaunchAsset {
301
- guard let filename = asset.nsBundleFilename else {
311
+ guard let filename = asset.nsBundleFilename, let assetType = asset.type else {
302
312
  continue
303
313
  }
304
314
 
305
- let bundlePath: String?
315
+ // Get file extension from type (e.g., "image/png" -> "png")
316
+ let ext = assetType.components(separatedBy: "/").last ?? "png"
317
+ var bundlePath: String? = nil
318
+
319
+ // 1. Try in assets folder with subdirectory path (RN stores assets here)
306
320
  if let dir = asset.nsBundleDir, !dir.isEmpty {
307
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil, inDirectory: dir)
308
- } else {
309
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil)
321
+ let path = "\(bundleRoot)/assets/\(dir)/\(filename).\(ext)"
322
+ if fileManager.fileExists(atPath: path) {
323
+ bundlePath = path
324
+ }
325
+ }
326
+
327
+ // 2. Try without assets/ prefix
328
+ if bundlePath == nil, let dir = asset.nsBundleDir, !dir.isEmpty {
329
+ let path = "\(bundleRoot)/\(dir)/\(filename).\(ext)"
330
+ if fileManager.fileExists(atPath: path) {
331
+ bundlePath = path
332
+ }
333
+ }
334
+
335
+ // 3. Try at root
336
+ if bundlePath == nil {
337
+ let path = "\(bundleRoot)/\(filename).\(ext)"
338
+ if fileManager.fileExists(atPath: path) {
339
+ bundlePath = path
340
+ }
310
341
  }
311
342
 
312
343
  if let path = bundlePath {
@@ -486,9 +517,25 @@ public final class PulseController {
486
517
  config: config,
487
518
  currentUpdateId: launchedUpdate?.updateId
488
519
  ) { [weak self] (result: Result<PulseCheckResult, Error>) in
520
+ guard let self = self else {
521
+ completion(result)
522
+ return
523
+ }
524
+
489
525
  // Cache the manifest for subsequent fetchUpdate call
490
- if case .success(let checkResult) = result, checkResult.isAvailable {
491
- self?.lastCheckManifest = checkResult.manifest
526
+ if case .success(let checkResult) = result, checkResult.isAvailable, let otaManifest = checkResult.manifest {
527
+ // Compare commitTime: if embedded is newer, don't show update
528
+ let embeddedCommitTime = self.embeddedManifest?.commitTime ?? Date.distantPast
529
+ let otaCommitTime = otaManifest.commitTime ?? Date.distantPast
530
+
531
+ if embeddedCommitTime >= otaCommitTime {
532
+ pulseLog("checkForUpdate: embedded is newer or same (embedded=\(embeddedCommitTime) >= ota=\(otaCommitTime)), no update needed")
533
+ completion(.success(PulseCheckResult(isAvailable: false)))
534
+ return
535
+ }
536
+
537
+ self.lastCheckManifest = otaManifest
538
+ pulseLog("checkForUpdate: cached manifest for fetch (ota=\(otaCommitTime) > embedded=\(embeddedCommitTime))")
492
539
  }
493
540
  completion(result)
494
541
  }
@@ -507,6 +554,17 @@ public final class PulseController {
507
554
  return
508
555
  }
509
556
 
557
+ // Thread-safe check and set of isFetching flag
558
+ fetchLock.lock()
559
+ if isFetching {
560
+ pendingFetchCallbacks.append(completion)
561
+ fetchLock.unlock()
562
+ pulseLog("fetchUpdate: already fetching, queuing callback")
563
+ return
564
+ }
565
+ isFetching = true
566
+ fetchLock.unlock()
567
+
510
568
  // Use cached manifest from recent checkForUpdate if available
511
569
  var manifestToUse = cachedManifest
512
570
  if manifestToUse == nil, let cached = lastCheckManifest {
@@ -521,9 +579,28 @@ public final class PulseController {
521
579
  config: config,
522
580
  database: database,
523
581
  directory: directory,
524
- cachedManifest: manifestToUse,
525
- completion: completion
526
- )
582
+ cachedManifest: manifestToUse
583
+ ) { [weak self] result in
584
+ guard let self = self else {
585
+ completion(result)
586
+ return
587
+ }
588
+
589
+ // Thread-safe reset of isFetching and get pending callbacks
590
+ self.fetchLock.lock()
591
+ self.isFetching = false
592
+ let callbacks = self.pendingFetchCallbacks
593
+ self.pendingFetchCallbacks.removeAll()
594
+ self.fetchLock.unlock()
595
+
596
+ // Call original completion
597
+ completion(result)
598
+
599
+ // Call all queued callbacks with the same result
600
+ for callback in callbacks {
601
+ callback(result)
602
+ }
603
+ }
527
604
  }
528
605
 
529
606
  /// Reload the app with the new update
@@ -586,11 +663,13 @@ public final class PulseController {
586
663
 
587
664
  private func loadEmbeddedManifest() {
588
665
  // Try pulse/ subdirectory first, then root
589
- guard let url = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json", subdirectory: "pulse")
590
- ?? Bundle.main.url(forResource: "embedded-manifest", withExtension: "json"),
666
+ let urlInPulse = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json", subdirectory: "pulse")
667
+ let urlInRoot = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json")
668
+
669
+ guard let url = urlInPulse ?? urlInRoot,
591
670
  let data = try? Data(contentsOf: url),
592
671
  let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
593
- pulseLog("No embedded manifest found")
672
+ pulseLog("No embedded manifest found or failed to parse")
594
673
  return
595
674
  }
596
675
 
@@ -598,17 +677,50 @@ public final class PulseController {
598
677
 
599
678
  // Cache embedded asset hashes
600
679
  if let manifest = embeddedManifest {
680
+ var foundCount = 0
681
+ var notFoundCount = 0
682
+ let bundleRoot = Bundle.main.bundlePath
683
+ let fileManager = FileManager.default
684
+
601
685
  for asset in manifest.assets {
602
- if let filename = asset.nsBundleFilename {
603
- let bundlePath: String?
686
+ if let filename = asset.nsBundleFilename, let assetType = asset.type {
687
+ // Get file extension from type (e.g., "image/png" -> "png")
688
+ let ext = assetType.components(separatedBy: "/").last ?? "png"
689
+ var bundlePath: String? = nil
690
+
691
+ // 1. Try in assets folder with subdirectory path (RN stores assets here)
604
692
  if let dir = asset.nsBundleDir, !dir.isEmpty {
605
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil, inDirectory: dir)
606
- } else {
607
- bundlePath = Bundle.main.path(forResource: filename, ofType: nil)
693
+ let path = "\(bundleRoot)/assets/\(dir)/\(filename).\(ext)"
694
+ if fileManager.fileExists(atPath: path) {
695
+ bundlePath = path
696
+ }
697
+ }
698
+
699
+ // 2. Try without assets/ prefix
700
+ if bundlePath == nil, let dir = asset.nsBundleDir, !dir.isEmpty {
701
+ let path = "\(bundleRoot)/\(dir)/\(filename).\(ext)"
702
+ if fileManager.fileExists(atPath: path) {
703
+ bundlePath = path
704
+ }
705
+ }
706
+
707
+ // 3. Try at root
708
+ if bundlePath == nil {
709
+ let path = "\(bundleRoot)/\(filename).\(ext)"
710
+ if fileManager.fileExists(atPath: path) {
711
+ bundlePath = path
712
+ }
608
713
  }
609
714
 
610
715
  if let path = bundlePath {
611
716
  embeddedAssetHashes[asset.hash.lowercased()] = URL(fileURLWithPath: path)
717
+ foundCount += 1
718
+ } else {
719
+ notFoundCount += 1
720
+ }
721
+ } else {
722
+ if !asset.isLaunchAsset {
723
+ notFoundCount += 1
612
724
  }
613
725
  }
614
726
 
@@ -616,6 +728,7 @@ public final class PulseController {
616
728
  embeddedBundleHash = asset.hash.lowercased()
617
729
  }
618
730
  }
731
+ pulseLog("Assets found in bundle: \(foundCount), not found: \(notFoundCount)")
619
732
  }
620
733
 
621
734
  pulseLog("Loaded embedded manifest: \(embeddedManifest?.updateId ?? "unknown"), embeddedAssetHashes count: \(embeddedAssetHashes.count)")
@@ -708,6 +821,13 @@ public final class PulseController {
708
821
  bundleHash: update.bundleHash,
709
822
  scopeKey: update.scopeKey
710
823
  )
824
+
825
+ // Load manifest from database for metadata (build number, etc.)
826
+ if let manifest = update.manifest,
827
+ let data = try? JSONSerialization.data(withJSONObject: manifest, options: []),
828
+ let jsonString = String(data: data, encoding: .utf8) {
829
+ self.launchedManifestJson = jsonString
830
+ }
711
831
  }
712
832
  }
713
833
 
@@ -1133,9 +1253,16 @@ final class PulseRemoteLoader {
1133
1253
  }
1134
1254
 
1135
1255
  // Move to final destination
1136
- try? FileManager.default.createDirectory(at: destination.deletingLastPathComponent(), withIntermediateDirectories: true)
1137
- try? FileManager.default.removeItem(at: destination)
1138
- try? FileManager.default.moveItem(at: tempPath, to: destination)
1256
+ do {
1257
+ try FileManager.default.createDirectory(at: destination.deletingLastPathComponent(), withIntermediateDirectories: true)
1258
+ if FileManager.default.fileExists(atPath: destination.path) {
1259
+ try FileManager.default.removeItem(at: destination)
1260
+ }
1261
+ try FileManager.default.moveItem(at: tempPath, to: destination)
1262
+ } catch {
1263
+ completion(.failure(error))
1264
+ return
1265
+ }
1139
1266
 
1140
1267
  // Store in database as launch asset
1141
1268
  storeBundleInDatabase()
@@ -1161,12 +1288,7 @@ final class PulseRemoteLoader {
1161
1288
 
1162
1289
  let group = DispatchGroup()
1163
1290
  var downloadError: Error?
1164
-
1165
1291
  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
1292
 
1171
1293
  for asset in assets {
1172
1294
  group.enter()
@@ -98,7 +98,8 @@ public class PulseUpdates: RCTEventEmitter {
98
98
  // Local assets map for expo-asset compatibility
99
99
  // Key: asset key from manifest, Value: local file:// URL
100
100
  // expo-updates exposes this as localAssets
101
- state["localAssets"] = controller.localAssets.isEmpty ? [:] : controller.localAssets
101
+ let assets = controller.localAssets
102
+ state["localAssets"] = assets.isEmpty ? [:] : assets
102
103
 
103
104
  return state
104
105
  }
@@ -105,22 +105,9 @@ function resolveLocalAsset(asset) {
105
105
  const keysToTry = buildAssetKeys(asset);
106
106
  for (const key of keysToTry) {
107
107
  if (localAssets[key]) {
108
- // Debug: log successful resolution
109
- if (__DEV__) {
110
- console.log(`[PulseAssetResolver] Resolved ${asset.name}.${asset.type} with key=${key.substring(0, 16)}...`);
111
- }
112
108
  return localAssets[key];
113
109
  }
114
110
  }
115
-
116
- // Debug: log failed resolution
117
- if (__DEV__) {
118
- console.log(`[PulseAssetResolver] MISS for ${asset.name}.${asset.type}`, {
119
- triedKeys: keysToTry.map(k => k.substring(0, 16) + '...'),
120
- localAssetsCount: Object.keys(localAssets).length,
121
- localAssetsSample: Object.keys(localAssets).slice(0, 3).map(k => k.substring(0, 16) + '...')
122
- });
123
- }
124
111
  return null;
125
112
  }
126
113
 
@@ -1 +1 @@
1
- {"version":3,"names":["localAssets","isPatched","originalDefaultAsset","applyPatch","AssetSourceResolver","require","default","prototype","defaultAsset","Object","keys","length","localUri","resolveLocalAsset","asset","fromSource","e","call","apply","initializeAssetResolver","assets","updateLocalAssets","keysToTry","buildAssetKeys","key","__DEV__","console","log","name","type","substring","triedKeys","map","k","localAssetsCount","localAssetsSample","slice","httpServerLocation","hash","fileHashes","push","values","forEach","h","location","startsWith","scales","scale","scaleSuffix","_default","exports"],"sourceRoot":"../../src","sources":["assetResolver.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["localAssets","isPatched","originalDefaultAsset","applyPatch","AssetSourceResolver","require","default","prototype","defaultAsset","Object","keys","length","localUri","resolveLocalAsset","asset","fromSource","e","call","apply","initializeAssetResolver","assets","updateLocalAssets","keysToTry","buildAssetKeys","key","name","type","httpServerLocation","hash","fileHashes","push","values","forEach","h","location","startsWith","slice","scales","scale","scaleSuffix","_default","exports"],"sourceRoot":"../../src","sources":["assetResolver.ts"],"mappings":"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","ignoreList":[]}
@@ -97,22 +97,9 @@ function resolveLocalAsset(asset) {
97
97
  const keysToTry = buildAssetKeys(asset);
98
98
  for (const key of keysToTry) {
99
99
  if (localAssets[key]) {
100
- // Debug: log successful resolution
101
- if (__DEV__) {
102
- console.log(`[PulseAssetResolver] Resolved ${asset.name}.${asset.type} with key=${key.substring(0, 16)}...`);
103
- }
104
100
  return localAssets[key];
105
101
  }
106
102
  }
107
-
108
- // Debug: log failed resolution
109
- if (__DEV__) {
110
- console.log(`[PulseAssetResolver] MISS for ${asset.name}.${asset.type}`, {
111
- triedKeys: keysToTry.map(k => k.substring(0, 16) + '...'),
112
- localAssetsCount: Object.keys(localAssets).length,
113
- localAssetsSample: Object.keys(localAssets).slice(0, 3).map(k => k.substring(0, 16) + '...')
114
- });
115
- }
116
103
  return null;
117
104
  }
118
105
 
@@ -1 +1 @@
1
- {"version":3,"names":["localAssets","isPatched","originalDefaultAsset","applyPatch","AssetSourceResolver","require","default","prototype","defaultAsset","Object","keys","length","localUri","resolveLocalAsset","asset","fromSource","e","call","apply","initializeAssetResolver","assets","updateLocalAssets","keysToTry","buildAssetKeys","key","__DEV__","console","log","name","type","substring","triedKeys","map","k","localAssetsCount","localAssetsSample","slice","httpServerLocation","hash","fileHashes","push","values","forEach","h","location","startsWith","scales","scale","scaleSuffix"],"sourceRoot":"../../src","sources":["assetResolver.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["localAssets","isPatched","originalDefaultAsset","applyPatch","AssetSourceResolver","require","default","prototype","defaultAsset","Object","keys","length","localUri","resolveLocalAsset","asset","fromSource","e","call","apply","initializeAssetResolver","assets","updateLocalAssets","keysToTry","buildAssetKeys","key","name","type","httpServerLocation","hash","fileHashes","push","values","forEach","h","location","startsWith","slice","scales","scale","scaleSuffix"],"sourceRoot":"../../src","sources":["assetResolver.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"assetResolver.d.ts","sourceRoot":"","sources":["../../src/assetResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2DH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAGnF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAE7E;;;;;AAsGD,wBAGE"}
1
+ {"version":3,"file":"assetResolver.d.ts","sourceRoot":"","sources":["../../src/assetResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA2DH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAGnF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAE7E;;;;;AAyFD,wBAGE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulse-updates",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "OTA updates for React Native - lightweight alternative to expo-updates",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -226,6 +226,13 @@ async function main() {
226
226
  const isDrawable = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(type);
227
227
  assetInfo.resourcesFolder = isDrawable ? 'drawable' : 'raw';
228
228
  assetInfo.resourcesFilename = getAndroidResourceIdentifier(relative, name);
229
+ // Also set mainBundleDir/mainBundleFilename for Kotlin code compatibility
230
+ // Assets are copied to pulse/assets/<relative_path> in the APK
231
+ const relativeDir = path.dirname(relative);
232
+ assetInfo.mainBundleDir = relativeDir && relativeDir !== '.'
233
+ ? `pulse/assets/${relativeDir}`
234
+ : 'pulse/assets';
235
+ assetInfo.mainBundleFilename = path.basename(relative);
229
236
  }
230
237
 
231
238
  assets.push(assetInfo);
@@ -101,23 +101,10 @@ function resolveLocalAsset(asset: any): string | null {
101
101
 
102
102
  for (const key of keysToTry) {
103
103
  if (localAssets[key]) {
104
- // Debug: log successful resolution
105
- if (__DEV__) {
106
- console.log(`[PulseAssetResolver] Resolved ${asset.name}.${asset.type} with key=${key.substring(0, 16)}...`);
107
- }
108
104
  return localAssets[key];
109
105
  }
110
106
  }
111
107
 
112
- // Debug: log failed resolution
113
- if (__DEV__) {
114
- console.log(`[PulseAssetResolver] MISS for ${asset.name}.${asset.type}`, {
115
- triedKeys: keysToTry.map(k => k.substring(0, 16) + '...'),
116
- localAssetsCount: Object.keys(localAssets).length,
117
- localAssetsSample: Object.keys(localAssets).slice(0, 3).map(k => k.substring(0, 16) + '...')
118
- });
119
- }
120
-
121
108
  return null;
122
109
  }
123
110