@onekeyfe/react-native-background-thread 3.0.20 → 3.0.22
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.
|
@@ -95,6 +95,49 @@ static NSURL *resolveMainBundleResourceURL(NSString *resourceName)
|
|
|
95
95
|
withExtension:extension.length > 0 ? extension : nil];
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/// Reflectively query BundleUpdateStore for an OTA-installed bundle path.
|
|
99
|
+
/// Mirrors the cross-framework reflection pattern used by SplitBundleLoader
|
|
100
|
+
/// (we can't import the Swift module directly because its umbrella header
|
|
101
|
+
/// pulls in C++/Nitro headers that break the Clang dependency scanner).
|
|
102
|
+
/// Returns nil when the selector is absent (older bundle-update package) or
|
|
103
|
+
/// when no OTA is currently active.
|
|
104
|
+
static NSString *resolveOtaBundlePath(NSString *selectorName)
|
|
105
|
+
{
|
|
106
|
+
Class cls = NSClassFromString(@"ReactNativeBundleUpdate.BundleUpdateStore");
|
|
107
|
+
if (!cls) return nil;
|
|
108
|
+
SEL sel = NSSelectorFromString(selectorName);
|
|
109
|
+
if (![cls respondsToSelector:sel]) return nil;
|
|
110
|
+
NSMethodSignature *sig = [cls methodSignatureForSelector:sel];
|
|
111
|
+
if (!sig || strcmp(sig.methodReturnType, @encode(id)) != 0) return nil;
|
|
112
|
+
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
|
|
113
|
+
inv.target = cls;
|
|
114
|
+
inv.selector = sel;
|
|
115
|
+
[inv invoke];
|
|
116
|
+
__unsafe_unretained id raw = nil;
|
|
117
|
+
[inv getReturnValue:&raw];
|
|
118
|
+
if (![raw isKindOfClass:[NSString class]]) return nil;
|
|
119
|
+
NSString *result = (NSString *)raw;
|
|
120
|
+
if (result.length == 0) return nil;
|
|
121
|
+
if ([result hasPrefix:@"file://"]) {
|
|
122
|
+
result = [[NSURL URLWithString:result] path];
|
|
123
|
+
}
|
|
124
|
+
if (![[NSFileManager defaultManager] fileExistsAtPath:result]) return nil;
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// True when an OTA-installed main bundle is currently active. Used to
|
|
129
|
+
/// prevent falling back to IPA built-in common/background bundles when the
|
|
130
|
+
/// foreground main runtime has already loaded an OTA main: a mixed
|
|
131
|
+
/// OTA-main + IPA-built-in pair would moduleId-mismatch and crash on first
|
|
132
|
+
/// require(). Without this guard, package skew (split-bundle-loader/
|
|
133
|
+
/// background-thread upgraded but bundle-update still on a version that
|
|
134
|
+
/// doesn't expose currentBundleCommonJSBundle) would silently reintroduce
|
|
135
|
+
/// the very crash this patch was added to fix.
|
|
136
|
+
static BOOL isOtaMainBundleActive(void)
|
|
137
|
+
{
|
|
138
|
+
return resolveOtaBundlePath(@"currentBundleMainJSBundle") != nil;
|
|
139
|
+
}
|
|
140
|
+
|
|
98
141
|
static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
99
142
|
{
|
|
100
143
|
if (jsBundleSourceNS.length == 0) {
|
|
@@ -120,6 +163,21 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
|
120
163
|
RCTInstance *_rctInstance;
|
|
121
164
|
std::string _origin;
|
|
122
165
|
std::string _jsBundleSource;
|
|
166
|
+
// YES when `bundleURL` observed an active OTA main bundle on its most
|
|
167
|
+
// recent invocation, regardless of whether OTA common actually resolved.
|
|
168
|
+
// Captures the invariant "this delegate is locked to OTA territory; IPA
|
|
169
|
+
// built-in fallbacks for the matching common/background bundle are
|
|
170
|
+
// unsafe", covering both:
|
|
171
|
+
// 1. OTA common was resolved (loaded). IPA built-in background would
|
|
172
|
+
// moduleId-mismatch the OTA common.
|
|
173
|
+
// 2. OTA common was unresolvable but OTA main was active (`bundleURL`
|
|
174
|
+
// returned nil). hostDidStart shouldn't fire in this case under
|
|
175
|
+
// normal RCTHost lifecycle, but if it ever does, we must not let
|
|
176
|
+
// `resolveBackgroundEntryBundlePath` happily fall back to IPA bg.
|
|
177
|
+
// `resolveBackgroundEntryBundlePath` and `hostDidStart` consult the flag
|
|
178
|
+
// to distinguish those fatal cases from the legitimate "this build was
|
|
179
|
+
// never split" case (no OTA, no background bundled — warn and continue).
|
|
180
|
+
BOOL _otaActiveAtBundleResolve;
|
|
123
181
|
}
|
|
124
182
|
|
|
125
183
|
- (void)cleanupResources;
|
|
@@ -136,6 +194,7 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
|
136
194
|
if (self = [super init]) {
|
|
137
195
|
_hasOnMessageHandler = NO;
|
|
138
196
|
_hasOnErrorHandler = NO;
|
|
197
|
+
_otaActiveAtBundleResolve = NO;
|
|
139
198
|
self.dependencyProvider = [[RCTAppDependencyProvider alloc] init];
|
|
140
199
|
}
|
|
141
200
|
return self;
|
|
@@ -170,6 +229,10 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
|
170
229
|
|
|
171
230
|
- (NSURL *)bundleURL
|
|
172
231
|
{
|
|
232
|
+
// Reset on every call so a re-entry (e.g. host restart) can't carry over
|
|
233
|
+
// a stale OTA assertion from a previous load.
|
|
234
|
+
_otaActiveAtBundleResolve = NO;
|
|
235
|
+
|
|
173
236
|
// When _jsBundleSource is set (dev mode or explicit override), use it as-is.
|
|
174
237
|
// This is a single full bundle (not split), so DON'T use common+entry strategy.
|
|
175
238
|
if (!_jsBundleSource.empty()) {
|
|
@@ -183,16 +246,64 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
|
183
246
|
}
|
|
184
247
|
|
|
185
248
|
// Default: load common bundle (shared polyfills + modules).
|
|
186
|
-
//
|
|
187
|
-
|
|
249
|
+
// Prefer OTA-installed common.bundle so a three-bundle OTA update is
|
|
250
|
+
// actually picked up by the background runtime; fall back to the IPA
|
|
251
|
+
// built-in common.bundle when no OTA is active. Without this, OTA
|
|
252
|
+
// would push a new common.bundle to disk but the background runtime
|
|
253
|
+
// would keep loading the stale built-in copy and crash on
|
|
254
|
+
// moduleId mismatch with the OTA-loaded background.bundle.
|
|
255
|
+
NSString *otaCommonPath = resolveOtaBundlePath(@"currentBundleCommonJSBundle");
|
|
256
|
+
if (otaCommonPath) {
|
|
257
|
+
[BTLogger info:[NSString stringWithFormat:@"BackgroundRuntime: using OTA common bundle at %@", otaCommonPath]];
|
|
258
|
+
// OTA common resolved implies OTA main is active; the matching OTA
|
|
259
|
+
// background MUST be used (IPA bg would moduleId-mismatch).
|
|
260
|
+
_otaActiveAtBundleResolve = YES;
|
|
261
|
+
return [NSURL fileURLWithPath:otaCommonPath];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Mixed-state guard: if OTA main is loaded but OTA common is unresolvable,
|
|
265
|
+
// refusing the IPA fallback is safer than crashing on moduleId mismatch.
|
|
266
|
+
// Returning nil aborts the background runtime; the foreground main runtime
|
|
267
|
+
// would have crashed anyway, so this just makes the failure mode loud.
|
|
268
|
+
if (isOtaMainBundleActive()) {
|
|
269
|
+
// Set the flag here too so the invariant ("OTA was active when bundleURL
|
|
270
|
+
// ran") holds regardless of whether OTA common resolved. Under normal
|
|
271
|
+
// RCTHost lifecycle hostDidStart won't run after we return nil, but if
|
|
272
|
+
// it ever does (host retry, lifecycle bug, future refactor), the flag
|
|
273
|
+
// ensures the same fatal-abort branch is taken.
|
|
274
|
+
_otaActiveAtBundleResolve = YES;
|
|
275
|
+
[BTLogger error:@"BackgroundRuntime: OTA main is active but OTA common bundle is unresolvable — refusing IPA fallback to avoid moduleId mismatch crash"];
|
|
276
|
+
return nil;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
NSURL *commonURL = resolveMainBundleResourceURL(@"common.bundle");
|
|
188
280
|
if (commonURL) {
|
|
189
281
|
return commonURL;
|
|
190
282
|
}
|
|
191
|
-
return [[NSBundle mainBundle] URLForResource:@"common" withExtension:@"
|
|
283
|
+
return [[NSBundle mainBundle] URLForResource:@"common" withExtension:@"bundle"];
|
|
192
284
|
}
|
|
193
285
|
|
|
194
286
|
- (NSString *)resolveBackgroundEntryBundlePath
|
|
195
287
|
{
|
|
288
|
+
// Prefer OTA-installed background.bundle; fall back to IPA built-in.
|
|
289
|
+
NSString *otaBackgroundPath = resolveOtaBundlePath(@"currentBundleBackgroundJSBundle");
|
|
290
|
+
if (otaBackgroundPath) {
|
|
291
|
+
[BTLogger info:[NSString stringWithFormat:@"BackgroundRuntime: using OTA background bundle at %@", otaBackgroundPath]];
|
|
292
|
+
return otaBackgroundPath;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Mixed-state guard: bundleURL set _otaActiveAtBundleResolve when it
|
|
296
|
+
// observed an active OTA main on its most recent invocation (whether or
|
|
297
|
+
// not OTA common itself resolved). The IPA built-in background.bundle was
|
|
298
|
+
// built against the IPA common.bundle, so its moduleIds won't line up
|
|
299
|
+
// with whatever OTA bundle the foreground runtime is using — IPA fallback
|
|
300
|
+
// would crash on first require(). Return nil; hostDidStart consults the
|
|
301
|
+
// same flag and aborts loudly instead of continuing with a broken runtime.
|
|
302
|
+
if (_otaActiveAtBundleResolve) {
|
|
303
|
+
[BTLogger error:@"BackgroundRuntime: OTA main is active but OTA background is unresolvable — refusing IPA fallback to avoid moduleId mismatch crash"];
|
|
304
|
+
return nil;
|
|
305
|
+
}
|
|
306
|
+
|
|
196
307
|
NSURL *url = resolveMainBundleResourceURL(@"background.bundle");
|
|
197
308
|
return url.path;
|
|
198
309
|
}
|
|
@@ -228,6 +339,22 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
|
|
|
228
339
|
bgBundleSourceURL = bgBundlePath.lastPathComponent ?: @"background.bundle";
|
|
229
340
|
[BTLogger info:[NSString stringWithFormat:@"Background entry bundle loaded from %@ (%lu bytes)",
|
|
230
341
|
bgBundlePath, (unsigned long)bgBundleData.length]];
|
|
342
|
+
} else if (_otaActiveAtBundleResolve) {
|
|
343
|
+
// Fatal: bundleURL committed to OTA territory (loaded OTA common, or
|
|
344
|
+
// detected OTA main and refused IPA fallback) but the matching OTA
|
|
345
|
+
// background couldn't be resolved. Setting up SharedStore / SharedRPC
|
|
346
|
+
// and calling __setupBackgroundRPCHandler against a runtime with no
|
|
347
|
+
// entry bundle would leave RPC silently broken; falling back to IPA
|
|
348
|
+
// bg would moduleId-mismatch and crash. Abort loudly.
|
|
349
|
+
//
|
|
350
|
+
// Clear _rctInstance before returning so registerSegmentWithId (and
|
|
351
|
+
// any other downstream method that gates on `_rctInstance != nil`)
|
|
352
|
+
// doesn't operate on a half-initialized runtime where SharedStore /
|
|
353
|
+
// SharedRPC / error handler / __setupBackgroundRPCHandler were all
|
|
354
|
+
// skipped.
|
|
355
|
+
[BTLogger error:@"BackgroundRuntime: aborting hostDidStart — OTA bundle is loaded but OTA background bundle is unresolvable"];
|
|
356
|
+
_rctInstance = nil;
|
|
357
|
+
return;
|
|
231
358
|
} else {
|
|
232
359
|
[BTLogger warn:@"Background entry bundle not found, __setupBackgroundRPCHandler may not be defined"];
|
|
233
360
|
}
|
|
@@ -109,7 +109,7 @@ static NSString *const MODULE_DEBUG_URL = @"http://localhost:8082/apps/mobile/ba
|
|
|
109
109
|
|
|
110
110
|
// Only set jsBundleSource for debug HTTP URLs or explicit OTA overrides.
|
|
111
111
|
// Leaving the default release name ("background.bundle") unset lets the
|
|
112
|
-
// delegate fall back to split-bundle mode (common.
|
|
112
|
+
// delegate fall back to split-bundle mode (common.bundle + entry).
|
|
113
113
|
if (![entryURL isEqualToString:@"background.bundle"]) {
|
|
114
114
|
[self.reactNativeFactoryDelegate setJsBundleSource:std::string([entryURL UTF8String])];
|
|
115
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-background-thread",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.22",
|
|
4
4
|
"description": "react-native-background-thread",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"typescript": "^5.9.2"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
+
"@onekeyfe/react-native-bundle-update": ">=3.0.22",
|
|
87
88
|
"react": "*",
|
|
88
89
|
"react-native": "*"
|
|
89
90
|
},
|