@onekeyfe/react-native-bundle-update 3.0.95 → 3.0.96
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/src/main/java/com/margelo/nitro/reactnativebundleupdate/BundleStorageSchemaPolicy.kt +22 -0
- package/android/src/main/java/com/margelo/nitro/reactnativebundleupdate/ReactNativeBundleUpdate.kt +33 -4
- package/ios/BundleStorageSchemaPolicy.swift +22 -0
- package/ios/ReactNativeBundleUpdate.swift +22 -1
- package/package.json +1 -1
package/android/src/main/java/com/margelo/nitro/reactnativebundleupdate/BundleStorageSchemaPolicy.kt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativebundleupdate
|
|
2
|
+
|
|
3
|
+
internal object BundleStorageSchemaPolicy {
|
|
4
|
+
const val METADATA_KEY = "storageSchemaVersion"
|
|
5
|
+
const val SUPPORTED_VERSION = "mmkv-v1"
|
|
6
|
+
const val LEDGER_PREFS_NAME = "onekey_native_storage_migration"
|
|
7
|
+
private val ledgerKeys = listOf("app-storage-v1", "jotai-storage-v1")
|
|
8
|
+
|
|
9
|
+
fun isCompatible(
|
|
10
|
+
declaredVersion: String?,
|
|
11
|
+
readMigrationLedger: (String) -> String?,
|
|
12
|
+
): Boolean {
|
|
13
|
+
if (!declaredVersion.isNullOrEmpty()) {
|
|
14
|
+
return declaredVersion == SUPPORTED_VERSION
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Old apps do not write this ledger and may keep using unmarked bundles.
|
|
18
|
+
// Any non-empty state (including an interrupted migration or reset)
|
|
19
|
+
// forbids returning to the retained AsyncStorage database.
|
|
20
|
+
return ledgerKeys.none { key -> !readMigrationLedger(key).isNullOrEmpty() }
|
|
21
|
+
}
|
|
22
|
+
}
|
package/android/src/main/java/com/margelo/nitro/reactnativebundleupdate/ReactNativeBundleUpdate.kt
CHANGED
|
@@ -428,7 +428,8 @@ object BundleUpdateStoreAndroid {
|
|
|
428
428
|
return key == METADATA_REQUIRES_BACKGROUND_BUNDLE_KEY ||
|
|
429
429
|
key == METADATA_BACKGROUND_PROTOCOL_VERSION_KEY ||
|
|
430
430
|
key == METADATA_REQUIRES_COMMON_BUNDLE_KEY ||
|
|
431
|
-
key == METADATA_BUNDLE_FORMAT_KEY
|
|
431
|
+
key == METADATA_BUNDLE_FORMAT_KEY ||
|
|
432
|
+
key == BundleStorageSchemaPolicy.METADATA_KEY
|
|
432
433
|
}
|
|
433
434
|
|
|
434
435
|
private fun getFileMetadataEntries(metadata: Map<String, String>): Map<String, String> {
|
|
@@ -458,6 +459,17 @@ object BundleUpdateStoreAndroid {
|
|
|
458
459
|
return metadata[METADATA_BACKGROUND_PROTOCOL_VERSION_KEY] ?: ""
|
|
459
460
|
}
|
|
460
461
|
|
|
462
|
+
private fun validateStorageSchemaCompatibility(context: Context, metadata: Map<String, String>): Boolean {
|
|
463
|
+
val compatible = BundleStorageSchemaPolicy.isCompatible(metadata[BundleStorageSchemaPolicy.METADATA_KEY]) { key ->
|
|
464
|
+
context.getSharedPreferences(BundleStorageSchemaPolicy.LEDGER_PREFS_NAME, Context.MODE_PRIVATE)
|
|
465
|
+
.getString(key, null)
|
|
466
|
+
}
|
|
467
|
+
if (!compatible) {
|
|
468
|
+
OneKeyLog.error("BundleUpdate", "Bundle storage schema is incompatible with the native migration state")
|
|
469
|
+
}
|
|
470
|
+
return compatible
|
|
471
|
+
}
|
|
472
|
+
|
|
461
473
|
fun readMetadataFileSha256(signature: String?): String? {
|
|
462
474
|
if (signature.isNullOrEmpty()) return null
|
|
463
475
|
|
|
@@ -635,6 +647,17 @@ object BundleUpdateStoreAndroid {
|
|
|
635
647
|
}
|
|
636
648
|
}
|
|
637
649
|
|
|
650
|
+
fun validateBundlePairCompatibility(
|
|
651
|
+
context: Context,
|
|
652
|
+
bundleDir: String,
|
|
653
|
+
metadata: Map<String, String>,
|
|
654
|
+
): Boolean {
|
|
655
|
+
return validateStorageSchemaCompatibility(context, metadata) &&
|
|
656
|
+
validateBundlePairCompatibility(bundleDir, metadata)
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Retain the original layout-validator signature for existing native callers.
|
|
660
|
+
// Startup and extracted-bundle validation use the context overload.
|
|
638
661
|
fun validateBundlePairCompatibility(bundleDir: String, metadata: Map<String, String>): Boolean {
|
|
639
662
|
val mainBundleFile = File(bundleDir, MAIN_JS_BUNDLE_FILE_NAME)
|
|
640
663
|
if (!mainBundleFile.exists()) {
|
|
@@ -706,6 +729,12 @@ object BundleUpdateStoreAndroid {
|
|
|
706
729
|
// startup.
|
|
707
730
|
cachedValidatedBundleInfo?.let { cached ->
|
|
708
731
|
if (cached.currentBundleVersion == currentBundleVersion) {
|
|
732
|
+
// Migration state can change while the native bundle cache survives
|
|
733
|
+
// a JS runtime reload. Recheck the ledger without repeating hashes.
|
|
734
|
+
if (!validateStorageSchemaCompatibility(context, cached.metadata)) {
|
|
735
|
+
invalidateValidatedBundleInfoCache()
|
|
736
|
+
return null
|
|
737
|
+
}
|
|
709
738
|
return cached
|
|
710
739
|
}
|
|
711
740
|
}
|
|
@@ -774,7 +803,7 @@ object BundleUpdateStoreAndroid {
|
|
|
774
803
|
return null
|
|
775
804
|
}
|
|
776
805
|
|
|
777
|
-
if (!validateBundlePairCompatibility(bundleDir, metadata)) {
|
|
806
|
+
if (!validateBundlePairCompatibility(context, bundleDir, metadata)) {
|
|
778
807
|
return null
|
|
779
808
|
}
|
|
780
809
|
|
|
@@ -1727,7 +1756,7 @@ class ReactNativeBundleUpdate : HybridReactNativeBundleUpdateSpec() {
|
|
|
1727
1756
|
BundleUpdateStoreAndroid.deleteDir(destinationDir)
|
|
1728
1757
|
throw Exception("Extracted files verification against metadata failed")
|
|
1729
1758
|
}
|
|
1730
|
-
if (!BundleUpdateStoreAndroid.validateBundlePairCompatibility(destination, metadata)) {
|
|
1759
|
+
if (!BundleUpdateStoreAndroid.validateBundlePairCompatibility(context, destination, metadata)) {
|
|
1731
1760
|
OneKeyLog.error("BundleUpdate", "verifyBundleASC: bundle pair compatibility check failed")
|
|
1732
1761
|
BundleUpdateStoreAndroid.deleteDir(destinationDir)
|
|
1733
1762
|
throw Exception("Bundle pair compatibility check failed")
|
|
@@ -2308,7 +2337,7 @@ n2DMz6gqk326W6SFynYtvuiXo7wG4Cmn3SuIU8xfv9rJqunpZGYchMd7nZektmEJ
|
|
|
2308
2337
|
OneKeyLog.error("BundleUpdate", "verifyExtractedBundle: file integrity check failed")
|
|
2309
2338
|
throw Exception("File integrity check failed")
|
|
2310
2339
|
}
|
|
2311
|
-
if (!BundleUpdateStoreAndroid.validateBundlePairCompatibility(bundlePath.absolutePath, metadata)) {
|
|
2340
|
+
if (!BundleUpdateStoreAndroid.validateBundlePairCompatibility(context, bundlePath.absolutePath, metadata)) {
|
|
2312
2341
|
OneKeyLog.error("BundleUpdate", "verifyExtractedBundle: bundle pair compatibility check failed")
|
|
2313
2342
|
throw Exception("Bundle pair compatibility check failed")
|
|
2314
2343
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
enum BundleStorageSchemaPolicy {
|
|
2
|
+
static let metadataKey = "storageSchemaVersion"
|
|
3
|
+
static let supportedVersion = "mmkv-v1"
|
|
4
|
+
static let ledgerPrefix = "onekey_native_storage_migration_"
|
|
5
|
+
private static let ledgerKeys = ["app-storage-v1", "jotai-storage-v1"]
|
|
6
|
+
|
|
7
|
+
static func isCompatible(
|
|
8
|
+
declaredVersion: String?,
|
|
9
|
+
readMigrationLedger: (String) -> String?
|
|
10
|
+
) -> Bool {
|
|
11
|
+
if let declaredVersion, !declaredVersion.isEmpty {
|
|
12
|
+
return declaredVersion == supportedVersion
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Old apps do not write this ledger and may keep using unmarked bundles.
|
|
16
|
+
// Any non-empty state (including an interrupted migration or reset)
|
|
17
|
+
// forbids returning to the retained AsyncStorage database.
|
|
18
|
+
return !ledgerKeys.contains { key in
|
|
19
|
+
!(readMigrationLedger(ledgerPrefix + key) ?? "").isEmpty
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -439,7 +439,8 @@ public class BundleUpdateStore: NSObject {
|
|
|
439
439
|
key == metadataRequiresBackgroundBundleKey ||
|
|
440
440
|
key == metadataBackgroundProtocolVersionKey ||
|
|
441
441
|
key == metadataRequiresCommonBundleKey ||
|
|
442
|
-
key == metadataBundleFormatKey
|
|
442
|
+
key == metadataBundleFormatKey ||
|
|
443
|
+
key == BundleStorageSchemaPolicy.metadataKey
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
private static func metadataStringValue(
|
|
@@ -474,6 +475,17 @@ public class BundleUpdateStore: NSObject {
|
|
|
474
475
|
return false
|
|
475
476
|
}
|
|
476
477
|
|
|
478
|
+
private static func validateStorageSchemaCompatibility(_ metadata: [String: Any]) -> Bool {
|
|
479
|
+
let declaredVersion = metadataStringValue(metadata, key: BundleStorageSchemaPolicy.metadataKey)
|
|
480
|
+
let compatible = BundleStorageSchemaPolicy.isCompatible(declaredVersion: declaredVersion) { key in
|
|
481
|
+
UserDefaults.standard.string(forKey: key)
|
|
482
|
+
}
|
|
483
|
+
if !compatible {
|
|
484
|
+
OneKeyLog.error("BundleUpdate", "Bundle storage schema is incompatible with the native migration state")
|
|
485
|
+
}
|
|
486
|
+
return compatible
|
|
487
|
+
}
|
|
488
|
+
|
|
477
489
|
private static func fileMetadataEntries(
|
|
478
490
|
from metadata: [String: Any],
|
|
479
491
|
) -> [String: String] {
|
|
@@ -567,6 +579,9 @@ public class BundleUpdateStore: NSObject {
|
|
|
567
579
|
bundleDirPath: String,
|
|
568
580
|
metadata: [String: Any],
|
|
569
581
|
) -> Bool {
|
|
582
|
+
if !validateStorageSchemaCompatibility(metadata) {
|
|
583
|
+
return false
|
|
584
|
+
}
|
|
570
585
|
let mainBundlePath = (bundleDirPath as NSString)
|
|
571
586
|
.appendingPathComponent(mainBundleEntryFileName)
|
|
572
587
|
guard FileManager.default.fileExists(atPath: mainBundlePath) else {
|
|
@@ -647,6 +662,12 @@ public class BundleUpdateStore: NSObject {
|
|
|
647
662
|
cachedValidatedBundleInfoLock.lock()
|
|
648
663
|
if let cached = cachedValidatedBundleInfo, cached.currentBundleVersion == currentBundleVer {
|
|
649
664
|
cachedValidatedBundleInfoLock.unlock()
|
|
665
|
+
// Migration state can change while the native bundle cache survives
|
|
666
|
+
// a JS runtime reload. Recheck the ledger without repeating hashes.
|
|
667
|
+
if !validateStorageSchemaCompatibility(cached.metadata) {
|
|
668
|
+
invalidateValidatedBundleInfoCache()
|
|
669
|
+
return nil
|
|
670
|
+
}
|
|
650
671
|
return cached
|
|
651
672
|
}
|
|
652
673
|
cachedValidatedBundleInfoLock.unlock()
|