pulse-updates 1.0.3 → 1.0.5

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,13 @@ class PulseController private constructor() {
99
99
  private var embeddedManifest: EmbeddedManifest? = null
100
100
  private val executor = Executors.newSingleThreadExecutor()
101
101
 
102
+ // Embedded asset hashes set for download optimization
103
+ internal val embeddedAssetHashes: Set<String>
104
+ get() = embeddedManifest?.assets
105
+ ?.filter { !it.isLaunchAsset }
106
+ ?.map { it.hash.lowercase() }
107
+ ?.toSet() ?: emptySet()
108
+
102
109
  // Native runtime version (from config, embedded manifest, or app version)
103
110
  private val nativeRuntimeVersion: String
104
111
  get() {
@@ -602,13 +609,19 @@ class PulseController private constructor() {
602
609
  private fun loadEmbeddedManifest() {
603
610
  val ctx = context ?: return
604
611
 
605
- try {
606
- val json = ctx.assets.open("embedded-manifest.json").bufferedReader().use { it.readText() }
607
- embeddedManifest = EmbeddedManifest.fromJson(json)
608
- pulseLog(TAG, "Loaded embedded manifest: ${embeddedManifest?.updateId}")
609
- } catch (e: Exception) {
610
- pulseLog(TAG, "No embedded manifest found")
612
+ // Try pulse/ subdirectory first, then root
613
+ val paths = listOf("pulse/embedded-manifest.json", "embedded-manifest.json")
614
+ for (path in paths) {
615
+ try {
616
+ val json = ctx.assets.open(path).bufferedReader().use { it.readText() }
617
+ embeddedManifest = EmbeddedManifest.fromJson(json)
618
+ pulseLog(TAG, "Loaded embedded manifest from $path: ${embeddedManifest?.updateId}")
619
+ return
620
+ } catch (e: Exception) {
621
+ // Try next path
622
+ }
611
623
  }
624
+ pulseLog(TAG, "No embedded manifest found")
612
625
  }
613
626
 
614
627
  private fun seedEmbeddedUpdateIfNeeded() {
@@ -1251,12 +1264,23 @@ object PulseRemoteLoader {
1251
1264
  }
1252
1265
 
1253
1266
  // Download assets and link them to the update
1267
+ val embeddedHashes = PulseController.getInstance().embeddedAssetHashes
1268
+
1254
1269
  for (asset in manifest.assets) {
1255
1270
  val assetHash = asset.hash.lowercase()
1256
1271
  val assetDest = File(directory, "assets/sha256/$assetHash")
1257
1272
 
1258
- // Download if not exists
1259
- if (!assetDest.exists()) {
1273
+ // Skip download if already cached
1274
+ if (assetDest.exists()) {
1275
+ // Asset already in cache, just link it
1276
+ }
1277
+ // Skip download if exists in embedded assets
1278
+ else if (embeddedHashes.contains(assetHash)) {
1279
+ pulseLog(TAG, "Asset ${assetHash.take(16)}... exists in embedded, skipping download")
1280
+ // Don't download - will use embedded fallback at runtime
1281
+ }
1282
+ // Download if not exists anywhere
1283
+ else {
1260
1284
  try {
1261
1285
  downloadFile(asset.url, assetDest, stagingDir)
1262
1286
 
@@ -323,13 +323,18 @@ class PulseAppLauncher(
323
323
  // MARK: - Embedded Manifest
324
324
 
325
325
  private fun loadEmbeddedManifest(): EmbeddedManifest? {
326
- return try {
327
- val json = context.assets.open("embedded-manifest.json").bufferedReader().use { it.readText() }
328
- EmbeddedManifest.fromJson(json)
329
- } catch (e: Exception) {
330
- pulseLogWarn(TAG, "No embedded manifest found: ${e.message}")
331
- null
326
+ // Try pulse/ subdirectory first, then root
327
+ val paths = listOf("pulse/embedded-manifest.json", "embedded-manifest.json")
328
+ for (path in paths) {
329
+ try {
330
+ val json = context.assets.open(path).bufferedReader().use { it.readText() }
331
+ return EmbeddedManifest.fromJson(json)
332
+ } catch (e: Exception) {
333
+ // Try next path
334
+ }
332
335
  }
336
+ pulseLogWarn(TAG, "No embedded manifest found")
337
+ return null
333
338
  }
334
339
 
335
340
  // MARK: - Crypto
@@ -62,7 +62,8 @@ public final class PulseController {
62
62
 
63
63
  // Embedded manifest cache
64
64
  private var embeddedManifest: PulseEmbeddedManifest?
65
- private var embeddedAssetHashes: [String: URL] = [:]
65
+ /// Embedded asset hashes map (hash -> file URL) - internal for download optimization
66
+ internal var embeddedAssetHashes: [String: URL] = [:]
66
67
  private var embeddedBundleHash: String?
67
68
 
68
69
  // MARK: - Native Config (from Info.plist, like expo-updates)
@@ -560,7 +561,9 @@ public final class PulseController {
560
561
  }
561
562
 
562
563
  private func loadEmbeddedManifest() {
563
- guard let url = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json"),
564
+ // Try pulse/ subdirectory first, then root
565
+ guard let url = Bundle.main.url(forResource: "embedded-manifest", withExtension: "json", subdirectory: "pulse")
566
+ ?? Bundle.main.url(forResource: "embedded-manifest", withExtension: "json"),
564
567
  let data = try? Data(contentsOf: url),
565
568
  let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
566
569
  pulseLog("No embedded manifest found")
@@ -1134,6 +1137,14 @@ final class PulseRemoteLoader {
1134
1137
  continue
1135
1138
  }
1136
1139
 
1140
+ // Already exists in embedded assets? Skip download and use embedded
1141
+ if PulseController.shared.embeddedAssetHashes[assetHash] != nil {
1142
+ pulseLog("Asset \(assetHash.prefix(16))... exists in embedded, skipping download")
1143
+ storeAssetInDatabase(asset: asset, hash: assetHash, database: database, updateId: updateId)
1144
+ group.leave()
1145
+ continue
1146
+ }
1147
+
1137
1148
  guard let url = URL(string: asset.url) else {
1138
1149
  group.leave()
1139
1150
  continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulse-updates",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",