@onekeyfe/react-native-background-thread 1.1.51 → 1.1.52

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.
@@ -108,6 +108,88 @@ class BackgroundThreadManager private constructor() {
108
108
  return null
109
109
  }
110
110
 
111
+ /**
112
+ * Creates a JSBundleLoader that loads two bundles sequentially from Android assets:
113
+ * first the common bundle (polyfills + shared modules), then the
114
+ * entry-specific bundle (entry-only modules + require(entryId)).
115
+ */
116
+ private fun createSequentialAssetBundleLoader(
117
+ appContext: android.content.Context,
118
+ commonAssetName: String,
119
+ entryAssetName: String
120
+ ): JSBundleLoader {
121
+ return object : JSBundleLoader() {
122
+ override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
123
+ // Step 1: Load common bundle (polyfills + shared modules)
124
+ delegate.loadScriptFromAssets(appContext.assets, "assets://$commonAssetName", false)
125
+ BTLogger.info("Common bundle loaded from assets: $commonAssetName")
126
+
127
+ // Step 2: Load entry-specific bundle
128
+ delegate.loadScriptFromAssets(appContext.assets, "assets://$entryAssetName", false)
129
+ BTLogger.info("Entry bundle loaded from assets: $entryAssetName")
130
+
131
+ return "assets://$entryAssetName"
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Creates a JSBundleLoader that loads two bundles sequentially from local files:
138
+ * first the common bundle, then the entry-specific bundle.
139
+ */
140
+ private fun createSequentialFileBundleLoader(
141
+ commonPath: String,
142
+ entryPath: String,
143
+ entrySourceURL: String
144
+ ): JSBundleLoader {
145
+ return object : JSBundleLoader() {
146
+ override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
147
+ // Step 1: Load common bundle (polyfills + shared modules)
148
+ val commonFile = File(commonPath)
149
+ if (!commonFile.exists()) {
150
+ BTLogger.error("Common bundle file does not exist: $commonPath")
151
+ throw RuntimeException("Common bundle file does not exist: $commonPath")
152
+ }
153
+ delegate.loadScriptFromFile(commonFile.absolutePath, "common.bundle", false)
154
+ BTLogger.info("Common bundle loaded from file: $commonPath")
155
+
156
+ // Step 2: Load entry-specific bundle
157
+ val entryFile = File(entryPath)
158
+ if (!entryFile.exists()) {
159
+ BTLogger.error("Entry bundle file does not exist: $entryPath")
160
+ throw RuntimeException("Entry bundle file does not exist: $entryPath")
161
+ }
162
+ delegate.loadScriptFromFile(entryFile.absolutePath, entrySourceURL, false)
163
+ BTLogger.info("Entry bundle loaded from file: $entryPath")
164
+
165
+ return entrySourceURL
166
+ }
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Check if common.bundle exists in the Android assets directory.
172
+ */
173
+ private fun hasCommonBundleInAssets(appContext: android.content.Context): Boolean {
174
+ return try {
175
+ appContext.assets.open("common.bundle").close()
176
+ true
177
+ } catch (e: Exception) {
178
+ false
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Resolve the common bundle path for OTA (file-based) loading.
184
+ * Looks for common.bundle in the same directory as the entry bundle.
185
+ */
186
+ private fun resolveCommonBundlePath(entryBundlePath: String): String? {
187
+ val entryFile = File(entryBundlePath)
188
+ val parentDir = entryFile.parentFile ?: return null
189
+ val commonFile = File(parentDir, "common.bundle")
190
+ return if (commonFile.exists()) commonFile.absolutePath else null
191
+ }
192
+
111
193
  private fun createDownloadedBundleLoader(appContext: android.content.Context, entryURL: String): JSBundleLoader {
112
194
  return object : JSBundleLoader() {
113
195
  override fun loadScript(delegate: com.facebook.react.bridge.JSBundleLoaderDelegate): String {
@@ -163,10 +245,43 @@ class BackgroundThreadManager private constructor() {
163
245
  val localBundlePath = resolveLocalBundlePath(entryURL)
164
246
  val bundleLoader =
165
247
  when {
248
+ // Debug mode: remote URL — use single bundle (Metro dev server)
166
249
  isRemoteBundleUrl(entryURL) -> createDownloadedBundleLoader(appContext, entryURL)
167
- localBundlePath != null -> createLocalFileBundleLoader(localBundlePath, entryURL)
168
- entryURL.startsWith("assets://") -> JSBundleLoader.createAssetLoader(appContext, entryURL, true)
169
- else -> JSBundleLoader.createAssetLoader(appContext, "assets://$entryURL", true)
250
+
251
+ // OTA / local file path — try sequential loading with common bundle
252
+ localBundlePath != null -> {
253
+ val commonPath = resolveCommonBundlePath(localBundlePath)
254
+ if (commonPath != null) {
255
+ BTLogger.info("Using sequential file bundle loader: common=$commonPath, entry=$localBundlePath")
256
+ createSequentialFileBundleLoader(commonPath, localBundlePath, entryURL)
257
+ } else {
258
+ BTLogger.info("No common bundle found for OTA path, using single bundle: $localBundlePath")
259
+ createLocalFileBundleLoader(localBundlePath, entryURL)
260
+ }
261
+ }
262
+
263
+ // Assets-based loading — try sequential loading with common.bundle in assets
264
+ entryURL.startsWith("assets://") -> {
265
+ val entryAssetName = entryURL.removePrefix("assets://")
266
+ if (hasCommonBundleInAssets(appContext)) {
267
+ BTLogger.info("Using sequential asset bundle loader: common=common.bundle, entry=$entryAssetName")
268
+ createSequentialAssetBundleLoader(appContext, "common.bundle", entryAssetName)
269
+ } else {
270
+ BTLogger.info("No common.bundle in assets, using single bundle: $entryURL")
271
+ JSBundleLoader.createAssetLoader(appContext, entryURL, true)
272
+ }
273
+ }
274
+
275
+ // Bare filename (e.g. "background.bundle") — treat as asset
276
+ else -> {
277
+ if (hasCommonBundleInAssets(appContext)) {
278
+ BTLogger.info("Using sequential asset bundle loader: common=common.bundle, entry=$entryURL")
279
+ createSequentialAssetBundleLoader(appContext, "common.bundle", entryURL)
280
+ } else {
281
+ BTLogger.info("No common.bundle in assets, using single bundle: assets://$entryURL")
282
+ JSBundleLoader.createAssetLoader(appContext, "assets://$entryURL", true)
283
+ }
284
+ }
170
285
  }
171
286
 
172
287
  val delegate = DefaultReactHostDelegate(
@@ -170,6 +170,8 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
170
170
 
171
171
  - (NSURL *)bundleURL
172
172
  {
173
+ // When _jsBundleSource is set (dev mode or explicit override), use it as-is.
174
+ // This is a single full bundle (not split), so DON'T use common+entry strategy.
173
175
  if (!_jsBundleSource.empty()) {
174
176
  NSString *jsBundleSourceNS = [NSString stringWithUTF8String:_jsBundleSource.c_str()];
175
177
  NSURL *resolvedURL = resolveBundleSourceURL(jsBundleSourceNS);
@@ -180,11 +182,19 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
180
182
  [BTLogger warn:[NSString stringWithFormat:@"Unable to resolve custom jsBundleSource=%@", jsBundleSourceNS]];
181
183
  }
182
184
 
183
- NSURL *defaultBundleURL = resolveMainBundleResourceURL(@"background.bundle");
184
- if (defaultBundleURL) {
185
- return defaultBundleURL;
185
+ // Default: load common bundle (shared polyfills + modules).
186
+ // The background entry bundle is loaded later in hostDidStart:.
187
+ NSURL *commonURL = resolveMainBundleResourceURL(@"common.jsbundle");
188
+ if (commonURL) {
189
+ return commonURL;
186
190
  }
187
- return [[NSBundle mainBundle] URLForResource:@"background" withExtension:@"bundle"];
191
+ return [[NSBundle mainBundle] URLForResource:@"common" withExtension:@"jsbundle"];
192
+ }
193
+
194
+ - (NSString *)resolveBackgroundEntryBundlePath
195
+ {
196
+ NSURL *url = resolveMainBundleResourceURL(@"background.bundle");
197
+ return url.path;
188
198
  }
189
199
 
190
200
  - (void)hostDidStart:(RCTHost *)host
@@ -203,6 +213,26 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
203
213
  return;
204
214
  }
205
215
 
216
+ // When _jsBundleSource is set, the bundle loaded in bundleURL was already
217
+ // a full single bundle (dev mode / explicit override), so skip entry loading.
218
+ BOOL isSplitBundle = _jsBundleSource.empty();
219
+
220
+ // Read the background entry bundle data before entering the executor block
221
+ // (only needed in split-bundle mode).
222
+ NSData *bgBundleData = nil;
223
+ NSString *bgBundleSourceURL = nil;
224
+ if (isSplitBundle) {
225
+ NSString *bgBundlePath = [self resolveBackgroundEntryBundlePath];
226
+ if (bgBundlePath) {
227
+ bgBundleData = [NSData dataWithContentsOfFile:bgBundlePath];
228
+ bgBundleSourceURL = bgBundlePath.lastPathComponent ?: @"background.bundle";
229
+ [BTLogger info:[NSString stringWithFormat:@"Background entry bundle loaded from %@ (%lu bytes)",
230
+ bgBundlePath, (unsigned long)bgBundleData.length]];
231
+ } else {
232
+ [BTLogger warn:@"Background entry bundle not found, __setupBackgroundRPCHandler may not be defined"];
233
+ }
234
+ }
235
+
206
236
  [_rctInstance callFunctionOnBufferedRuntimeExecutor:[=](jsi::Runtime &runtime) {
207
237
  [self setupErrorHandler:runtime];
208
238
 
@@ -218,6 +248,17 @@ static NSURL *resolveBundleSourceURL(NSString *jsBundleSourceNS)
218
248
  };
219
249
  SharedRPC::install(runtime, std::move(bgExecutor), "background");
220
250
  [BTLogger info:@"SharedStore and SharedRPC installed in background runtime"];
251
+
252
+ // In split-bundle mode, evaluate the background entry bundle now.
253
+ // This must happen BEFORE invokeOptionalGlobalFunction since the entry
254
+ // bundle defines __setupBackgroundRPCHandler.
255
+ if (isSplitBundle && bgBundleData && bgBundleData.length > 0) {
256
+ auto buffer = std::make_shared<jsi::StringBuffer>(
257
+ std::string(static_cast<const char *>(bgBundleData.bytes), bgBundleData.length));
258
+ runtime.evaluateJavaScript(std::move(buffer), [bgBundleSourceURL UTF8String]);
259
+ [BTLogger info:@"Background entry bundle evaluated in runtime"];
260
+ }
261
+
221
262
  invokeOptionalGlobalFunction(runtime, "__setupBackgroundRPCHandler");
222
263
  }];
223
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-background-thread",
3
- "version": "1.1.51",
3
+ "version": "1.1.52",
4
4
  "description": "react-native-background-thread",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",