@onekeyfe/react-native-split-bundle-loader 3.0.180 → 6.7.0-2
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.
|
@@ -114,6 +114,7 @@ class SplitBundleLoaderModule(reactContext: ReactApplicationContext) :
|
|
|
114
114
|
// assets get extracted fresh on first load.
|
|
115
115
|
private const val PREFS_NAME = "split_bundle_loader"
|
|
116
116
|
private const val KEY_LAST_INSTALL_STAMP = "last_install_stamp"
|
|
117
|
+
private const val BUILTIN_BUNDLE_SENTINEL = "__BUILTIN__"
|
|
117
118
|
// Double-checked locking: loser threads must BLOCK until the wipe
|
|
118
119
|
// finishes, not just skip. AtomicBoolean.compareAndSet would let
|
|
119
120
|
// losers race ahead and read half-nuked state.
|
|
@@ -121,6 +122,19 @@ class SplitBundleLoaderModule(reactContext: ReactApplicationContext) :
|
|
|
121
122
|
private val wipeLock = Any()
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
// Pin the OTA bundle path that this React runtime instance booted with so
|
|
126
|
+
// mid-session SharedPreferences mutations (installBundle / switchBundle /
|
|
127
|
+
// resetToBuiltInBundle during the 2500ms pre-restart window) cannot cause
|
|
128
|
+
// subsequent lazy segment loads to jump to a different bundle root.
|
|
129
|
+
@Volatile
|
|
130
|
+
private var pinnedOtaBundlePath: String? = null
|
|
131
|
+
private val pinnedOtaBundleLock = Any()
|
|
132
|
+
|
|
133
|
+
override fun initialize() {
|
|
134
|
+
super.initialize()
|
|
135
|
+
getOtaBundlePath()
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
override fun getName(): String = NAME
|
|
125
139
|
|
|
126
140
|
// -----------------------------------------------------------------------
|
|
@@ -458,6 +472,8 @@ class SplitBundleLoaderModule(reactContext: ReactApplicationContext) :
|
|
|
458
472
|
} else {
|
|
459
473
|
SBLLogger.info("[resolveSeg] rel=$relativePath otaBundlePath=$otaBundlePath parentFile=null")
|
|
460
474
|
}
|
|
475
|
+
SBLLogger.warn("[resolveSeg] rel=$relativePath missing in active OTA bundle root — refusing builtin fallback")
|
|
476
|
+
return null
|
|
461
477
|
} else {
|
|
462
478
|
SBLLogger.info("[resolveSeg] rel=$relativePath otaBundlePath=(empty) — skipping OTA")
|
|
463
479
|
}
|
|
@@ -636,6 +652,22 @@ class SplitBundleLoaderModule(reactContext: ReactApplicationContext) :
|
|
|
636
652
|
}
|
|
637
653
|
|
|
638
654
|
private fun getOtaBundlePath(): String? {
|
|
655
|
+
val existing = pinnedOtaBundlePath
|
|
656
|
+
if (existing != null) {
|
|
657
|
+
return if (existing == BUILTIN_BUNDLE_SENTINEL) null else existing
|
|
658
|
+
}
|
|
659
|
+
synchronized(pinnedOtaBundleLock) {
|
|
660
|
+
val rechecked = pinnedOtaBundlePath
|
|
661
|
+
if (rechecked != null) {
|
|
662
|
+
return if (rechecked == BUILTIN_BUNDLE_SENTINEL) null else rechecked
|
|
663
|
+
}
|
|
664
|
+
val resolved = queryCurrentOtaBundlePath()
|
|
665
|
+
pinnedOtaBundlePath = if (resolved.isNullOrEmpty()) BUILTIN_BUNDLE_SENTINEL else resolved
|
|
666
|
+
return resolved
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private fun queryCurrentOtaBundlePath(): String? {
|
|
639
671
|
return try {
|
|
640
672
|
val bundleUpdateStore = Class.forName(
|
|
641
673
|
"com.margelo.nitro.reactnativebundleupdate.BundleUpdateStoreAndroid"
|
|
@@ -697,7 +729,9 @@ class SplitBundleLoaderModule(reactContext: ReactApplicationContext) :
|
|
|
697
729
|
*/
|
|
698
730
|
private fun verifySha256(path: String, expected: String): Boolean {
|
|
699
731
|
if (expected.isEmpty()) {
|
|
700
|
-
|
|
732
|
+
val otaPath = getOtaBundlePath()
|
|
733
|
+
val otaActive = !otaPath.isNullOrEmpty() && pinnedOtaBundlePath != BUILTIN_BUNDLE_SENTINEL
|
|
734
|
+
return otaActive && !currentBundleRequiresPerSegmentHash()
|
|
701
735
|
}
|
|
702
736
|
return try {
|
|
703
737
|
val md = MessageDigest.getInstance("SHA-256")
|
package/ios/SplitBundleLoader.mm
CHANGED
|
@@ -416,7 +416,33 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
416
416
|
ESegmentEvalErrorTimeout = 6,
|
|
417
417
|
};
|
|
418
418
|
|
|
419
|
-
@
|
|
419
|
+
static NSString *const kSBLBuiltinBundleSentinel = @"__BUILTIN__";
|
|
420
|
+
|
|
421
|
+
@implementation SplitBundleLoader {
|
|
422
|
+
os_unfair_lock _pinnedOtaLock;
|
|
423
|
+
NSString *_Nullable _pinnedOtaBundlePath;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
- (instancetype)init
|
|
427
|
+
{
|
|
428
|
+
if (self = [super init]) {
|
|
429
|
+
_pinnedOtaLock = OS_UNFAIR_LOCK_INIT;
|
|
430
|
+
}
|
|
431
|
+
return self;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
- (nullable NSString *)pinnedOtaBundlePath
|
|
435
|
+
{
|
|
436
|
+
os_unfair_lock_lock(&_pinnedOtaLock);
|
|
437
|
+
NSString *cached = _pinnedOtaBundlePath;
|
|
438
|
+
if (!cached) {
|
|
439
|
+
NSString *resolved = [SplitBundleLoader otaBundlePath];
|
|
440
|
+
cached = (resolved.length > 0) ? [resolved copy] : kSBLBuiltinBundleSentinel;
|
|
441
|
+
_pinnedOtaBundlePath = cached;
|
|
442
|
+
}
|
|
443
|
+
os_unfair_lock_unlock(&_pinnedOtaLock);
|
|
444
|
+
return [cached isEqualToString:kSBLBuiltinBundleSentinel] ? nil : cached;
|
|
445
|
+
}
|
|
420
446
|
|
|
421
447
|
// Reflectively asks bundle-update whether the current bundle's manifest is
|
|
422
448
|
// required to ship per-segment SHA-256s (three-bundle / split-thread format).
|
|
@@ -451,11 +477,21 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
451
477
|
// mismatch or read failure. Streams the file with a fixed-size buffer so a
|
|
452
478
|
// large segment doesn't pull the whole bundle into memory just to hash it.
|
|
453
479
|
+ (BOOL)verifySha256OfFile:(NSString *)path expected:(NSString *)expected
|
|
480
|
+
{
|
|
481
|
+
return [self verifySha256OfFile:path expected:expected otaBundlePath:[self otaBundlePath]];
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
+ (BOOL)verifySha256OfFile:(NSString *)path
|
|
485
|
+
expected:(NSString *)expected
|
|
486
|
+
otaBundlePath:(nullable NSString *)otaPath
|
|
454
487
|
{
|
|
455
488
|
if (expected.length == 0) {
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
489
|
+
// Only allow skipping SHA-256 verification when an OTA bundle IS active
|
|
490
|
+
// AND its manifest is an older format that does not require per-segment hashes.
|
|
491
|
+
// Built-in bundles and three-bundle manifests must always supply a non-empty hash.
|
|
492
|
+
if (otaPath.length == 0 || [otaPath isEqualToString:kSBLBuiltinBundleSentinel]) {
|
|
493
|
+
return NO;
|
|
494
|
+
}
|
|
459
495
|
return ![SplitBundleLoader currentBundleRequiresPerSegmentHash];
|
|
460
496
|
}
|
|
461
497
|
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
|
|
@@ -486,6 +522,7 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
486
522
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
487
523
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
488
524
|
{
|
|
525
|
+
[self pinnedOtaBundlePath];
|
|
489
526
|
[SBLLogger info:@"SplitBundleLoader module initialized"];
|
|
490
527
|
return std::make_shared<facebook::react::NativeSplitBundleLoaderSpecJSI>(params);
|
|
491
528
|
}
|
|
@@ -761,7 +798,7 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
761
798
|
NSString *nativeVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: @"";
|
|
762
799
|
NSString *bundleVersion = @"";
|
|
763
800
|
|
|
764
|
-
NSString *otaPath = [
|
|
801
|
+
NSString *otaPath = [self pinnedOtaBundlePath];
|
|
765
802
|
if (otaPath && [[NSFileManager defaultManager] fileExistsAtPath:otaPath]) {
|
|
766
803
|
sourceKind = @"ota";
|
|
767
804
|
bundleRoot = [otaPath stringByDeletingLastPathComponent];
|
|
@@ -788,28 +825,38 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
788
825
|
/// Resolves a relative segment path to an absolute path, checking OTA then builtin.
|
|
789
826
|
/// Returns nil if the segment file is not found.
|
|
790
827
|
+ (nullable NSString *)resolveAbsolutePath:(NSString *)relativePath
|
|
828
|
+
{
|
|
829
|
+
return [self resolveAbsolutePath:relativePath
|
|
830
|
+
otaBundlePath:[self otaBundlePath]];
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
+ (nullable NSString *)resolveAbsolutePath:(NSString *)relativePath
|
|
834
|
+
otaBundlePath:(nullable NSString *)otaPath
|
|
791
835
|
{
|
|
792
836
|
// 1. Try OTA bundle root first
|
|
793
|
-
NSString *otaPath = [SplitBundleLoader otaBundlePath];
|
|
794
837
|
if (otaPath) {
|
|
795
|
-
//
|
|
796
|
-
NSString *otaRoot = [[otaPath stringByDeletingLastPathComponent] stringByStandardizingPath];
|
|
797
|
-
NSString *
|
|
798
|
-
|
|
838
|
+
// Resolve symlinks and enforce trailing slash boundary so sibling-prefix directories cannot match.
|
|
839
|
+
NSString *otaRoot = [[[otaPath stringByDeletingLastPathComponent] stringByResolvingSymlinksInPath] stringByStandardizingPath];
|
|
840
|
+
NSString *otaRootWithSlash = [otaRoot hasSuffix:@"/"] ? otaRoot : [otaRoot stringByAppendingString:@"/"];
|
|
841
|
+
NSString *candidate = [[[otaRoot stringByAppendingPathComponent:relativePath] stringByResolvingSymlinksInPath] stringByStandardizingPath];
|
|
842
|
+
BOOL otaPrefixOk = [candidate hasPrefix:otaRootWithSlash];
|
|
799
843
|
BOOL otaExists = [[NSFileManager defaultManager] fileExistsAtPath:candidate];
|
|
800
844
|
[SBLLogger info:[NSString stringWithFormat:@"[resolveAbs] rel=%@ ota root=%@ cand=%@ prefixOk=%d exists=%d",
|
|
801
845
|
relativePath, otaRoot, candidate, otaPrefixOk, otaExists]];
|
|
802
846
|
if (otaPrefixOk && otaExists) {
|
|
803
847
|
return candidate;
|
|
804
848
|
}
|
|
849
|
+
[SBLLogger warn:[NSString stringWithFormat:@"[resolveAbs] rel=%@ missing in active OTA bundle root — refusing builtin fallback", relativePath]];
|
|
850
|
+
return nil;
|
|
805
851
|
} else {
|
|
806
852
|
[SBLLogger info:[NSString stringWithFormat:@"[resolveAbs] rel=%@ otaPath=(nil) — skipping OTA", relativePath]];
|
|
807
853
|
}
|
|
808
854
|
|
|
809
855
|
// 2. Fallback to builtin resource path
|
|
810
|
-
NSString *builtinRoot = [[[NSBundle mainBundle] resourcePath] stringByStandardizingPath];
|
|
811
|
-
NSString *
|
|
812
|
-
|
|
856
|
+
NSString *builtinRoot = [[[[NSBundle mainBundle] resourcePath] stringByResolvingSymlinksInPath] stringByStandardizingPath];
|
|
857
|
+
NSString *builtinRootWithSlash = [builtinRoot hasSuffix:@"/"] ? builtinRoot : [builtinRoot stringByAppendingString:@"/"];
|
|
858
|
+
NSString *candidate = [[[builtinRoot stringByAppendingPathComponent:relativePath] stringByResolvingSymlinksInPath] stringByStandardizingPath];
|
|
859
|
+
BOOL builtinPrefixOk = [candidate hasPrefix:builtinRootWithSlash];
|
|
813
860
|
BOOL builtinExists = [[NSFileManager defaultManager] fileExistsAtPath:candidate];
|
|
814
861
|
[SBLLogger info:[NSString stringWithFormat:@"[resolveAbs] rel=%@ builtin root=%@ cand=%@ prefixOk=%d exists=%d",
|
|
815
862
|
relativePath, builtinRoot, candidate, builtinPrefixOk, builtinExists]];
|
|
@@ -836,14 +883,16 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
836
883
|
return;
|
|
837
884
|
}
|
|
838
885
|
|
|
839
|
-
NSString *
|
|
886
|
+
NSString *otaPath = [self pinnedOtaBundlePath];
|
|
887
|
+
NSString *absolutePath = [SplitBundleLoader resolveAbsolutePath:relativePath
|
|
888
|
+
otaBundlePath:otaPath];
|
|
840
889
|
if (!absolutePath) {
|
|
841
890
|
reject(@"SPLIT_BUNDLE_NOT_FOUND",
|
|
842
891
|
[NSString stringWithFormat:@"Segment file not found: %@", relativePath],
|
|
843
892
|
nil);
|
|
844
893
|
return;
|
|
845
894
|
}
|
|
846
|
-
if (![SplitBundleLoader verifySha256OfFile:absolutePath expected:sha256]) {
|
|
895
|
+
if (![SplitBundleLoader verifySha256OfFile:absolutePath expected:sha256 otaBundlePath:otaPath]) {
|
|
847
896
|
reject(@"SPLIT_BUNDLE_SHA256_MISMATCH",
|
|
848
897
|
[NSString stringWithFormat:@"Segment SHA-256 mismatch: %@", relativePath],
|
|
849
898
|
nil);
|
|
@@ -1068,7 +1117,9 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
1068
1117
|
return;
|
|
1069
1118
|
}
|
|
1070
1119
|
|
|
1071
|
-
NSString *
|
|
1120
|
+
NSString *otaPath = [self pinnedOtaBundlePath];
|
|
1121
|
+
NSString *absolutePath = [SplitBundleLoader resolveAbsolutePath:relativePath
|
|
1122
|
+
otaBundlePath:otaPath];
|
|
1072
1123
|
|
|
1073
1124
|
if (!absolutePath) {
|
|
1074
1125
|
reject(@"SPLIT_BUNDLE_NOT_FOUND",
|
|
@@ -1077,7 +1128,7 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
1077
1128
|
return;
|
|
1078
1129
|
}
|
|
1079
1130
|
|
|
1080
|
-
if (![SplitBundleLoader verifySha256OfFile:absolutePath expected:sha256]) {
|
|
1131
|
+
if (![SplitBundleLoader verifySha256OfFile:absolutePath expected:sha256 otaBundlePath:otaPath]) {
|
|
1081
1132
|
reject(@"SPLIT_BUNDLE_SHA256_MISMATCH",
|
|
1082
1133
|
[NSString stringWithFormat:@"Segment SHA-256 mismatch: %@ (key=%@)", relativePath, segmentKey],
|
|
1083
1134
|
nil);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-split-bundle-loader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.7.0-2",
|
|
4
4
|
"description": "Native split JavaScript bundle context and segment loading for OneKey",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|