react-native-ota-hot-update 2.4.0 → 2.4.1
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/README.md +48 -0
- package/android/generated/java/com/otahotupdate/NativeOtaHotUpdateSpec.java +4 -0
- package/android/generated/jni/RNOtaHotUpdateSpec-generated.cpp +6 -0
- package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI-generated.cpp +9 -0
- package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI.h +9 -0
- package/android/src/main/java/com/otahotupdate/OtaHotUpdateModule.kt +50 -0
- package/android/src/oldarch/OtaHotUpdateSpec.kt +1 -0
- package/ios/OtaHotUpdate.mm +85 -42
- package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec-generated.mm +7 -0
- package/ios/generated/RNOtaHotUpdateSpec/RNOtaHotUpdateSpec.h +5 -0
- package/ios/generated/RNOtaHotUpdateSpecJSI-generated.cpp +9 -0
- package/ios/generated/RNOtaHotUpdateSpecJSI.h +9 -0
- package/lib/commonjs/NativeOtaHotUpdate.js.map +1 -1
- package/lib/commonjs/gits/helper/fs.js +28 -2
- package/lib/commonjs/gits/helper/fs.js.map +1 -1
- package/lib/module/NativeOtaHotUpdate.js.map +1 -1
- package/lib/module/gits/helper/fs.js +28 -2
- package/lib/module/gits/helper/fs.js.map +1 -1
- package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts +1 -0
- package/lib/typescript/commonjs/src/NativeOtaHotUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/gits/helper/fs.d.ts.map +1 -1
- package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts +1 -0
- package/lib/typescript/module/src/NativeOtaHotUpdate.d.ts.map +1 -1
- package/lib/typescript/module/src/gits/helper/fs.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeOtaHotUpdate.ts +1 -0
- package/src/gits/helper/fs.ts +27 -3
package/README.md
CHANGED
|
@@ -251,3 +251,51 @@ Using Strapi, you can build a tailored admin panel to manage React Native hot up
|
|
|
251
251
|
|
|
252
252
|
### Sponsor this project
|
|
253
253
|
https://paypal.me/vantuan88291
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Architecture / Sequence Diagram
|
|
258
|
+
|
|
259
|
+
> Paste the content below into [https://sequencediagram.org/](https://sequencediagram.org/) to render the flow diagram.
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
title react-native-ota-hot-update - Main Flow
|
|
263
|
+
|
|
264
|
+
actor Developer
|
|
265
|
+
participant "Your Server\n(or Git Repo)" as Server
|
|
266
|
+
participant "RN App" as App
|
|
267
|
+
participant "Native Module\n(iOS / Android)" as Native
|
|
268
|
+
participant "Local Storage\n& File System" as Local
|
|
269
|
+
|
|
270
|
+
== Download & Install Update ==
|
|
271
|
+
|
|
272
|
+
Developer->Server: Upload bundle.zip + update.json
|
|
273
|
+
App->Server: Fetch update.json → check new version available
|
|
274
|
+
App->Server: Download bundle.zip
|
|
275
|
+
Server-->App: bundle.zip saved to local path
|
|
276
|
+
App->Native: setupBundlePath(zipPath, version)
|
|
277
|
+
Native->Local: Unzip → output_v{version}_{timestamp}/bundle
|
|
278
|
+
Native->Local: Save bundle PATH to SharedPrefs / UserDefaults
|
|
279
|
+
Native-->App: success
|
|
280
|
+
|
|
281
|
+
alt restartAfterInstall = true
|
|
282
|
+
App->Native: restart()
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
== App Startup - Which Bundle to Load? ==
|
|
286
|
+
|
|
287
|
+
App->Native: getBundle() / bundleJS()
|
|
288
|
+
Native->Local: Read saved PATH
|
|
289
|
+
alt PATH exists AND file on disk AND app version matches
|
|
290
|
+
Native-->App: Use OTA bundle path
|
|
291
|
+
else
|
|
292
|
+
Native-->App: Use default bundle (built-in)
|
|
293
|
+
end
|
|
294
|
+
App->App: Load JS Bundle
|
|
295
|
+
|
|
296
|
+
== Crash Auto-Rollback ==
|
|
297
|
+
|
|
298
|
+
App->App: Crash within 2s of startup
|
|
299
|
+
Native->Local: Restore previous bundle PATH
|
|
300
|
+
Native->App: Restart app with safe bundle
|
|
301
|
+
```
|
|
@@ -80,4 +80,8 @@ public abstract class NativeOtaHotUpdateSpec extends ReactContextBaseJavaModule
|
|
|
80
80
|
@ReactMethod
|
|
81
81
|
@DoNotStrip
|
|
82
82
|
public abstract void clearAllBundles(double a, Promise promise);
|
|
83
|
+
|
|
84
|
+
@ReactMethod
|
|
85
|
+
@DoNotStrip
|
|
86
|
+
public abstract void writeFile(String path, String base64Content, String encoding, Promise promise);
|
|
83
87
|
}
|
|
@@ -72,6 +72,11 @@ static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_clearAllBun
|
|
|
72
72
|
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "clearAllBundles", "(DLcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_writeFile(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
76
|
+
static jmethodID cachedMethodId = nullptr;
|
|
77
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "writeFile", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
NativeOtaHotUpdateSpecJSI::NativeOtaHotUpdateSpecJSI(const JavaTurboModule::InitParams ¶ms)
|
|
76
81
|
: JavaTurboModule(params) {
|
|
77
82
|
methodMap_["setupBundlePath"] = MethodMetadata {5, __hostFunction_NativeOtaHotUpdateSpecJSI_setupBundlePath};
|
|
@@ -86,6 +91,7 @@ NativeOtaHotUpdateSpecJSI::NativeOtaHotUpdateSpecJSI(const JavaTurboModule::Init
|
|
|
86
91
|
methodMap_["getBundleList"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_getBundleList};
|
|
87
92
|
methodMap_["deleteBundleById"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_deleteBundleById};
|
|
88
93
|
methodMap_["clearAllBundles"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_clearAllBundles};
|
|
94
|
+
methodMap_["writeFile"] = MethodMetadata {3, __hostFunction_NativeOtaHotUpdateSpecJSI_writeFile};
|
|
89
95
|
}
|
|
90
96
|
|
|
91
97
|
std::shared_ptr<TurboModule> RNOtaHotUpdateSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) {
|
|
@@ -87,6 +87,14 @@ static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_clearAllBundles(js
|
|
|
87
87
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
+
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_writeFile(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
91
|
+
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->writeFile(
|
|
92
|
+
rt,
|
|
93
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt),
|
|
94
|
+
count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asString(rt),
|
|
95
|
+
count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asString(rt)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
90
98
|
|
|
91
99
|
NativeOtaHotUpdateCxxSpecJSI::NativeOtaHotUpdateCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
92
100
|
: TurboModule("OtaHotUpdate", jsInvoker) {
|
|
@@ -102,6 +110,7 @@ NativeOtaHotUpdateCxxSpecJSI::NativeOtaHotUpdateCxxSpecJSI(std::shared_ptr<CallI
|
|
|
102
110
|
methodMap_["getBundleList"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getBundleList};
|
|
103
111
|
methodMap_["deleteBundleById"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_deleteBundleById};
|
|
104
112
|
methodMap_["clearAllBundles"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_clearAllBundles};
|
|
113
|
+
methodMap_["writeFile"] = MethodMetadata {3, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_writeFile};
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
|
package/android/generated/jni/react/renderer/components/RNOtaHotUpdateSpec/RNOtaHotUpdateSpecJSI.h
CHANGED
|
@@ -32,6 +32,7 @@ public:
|
|
|
32
32
|
virtual jsi::Value getBundleList(jsi::Runtime &rt, double a) = 0;
|
|
33
33
|
virtual jsi::Value deleteBundleById(jsi::Runtime &rt, jsi::String id) = 0;
|
|
34
34
|
virtual jsi::Value clearAllBundles(jsi::Runtime &rt, double a) = 0;
|
|
35
|
+
virtual jsi::Value writeFile(jsi::Runtime &rt, jsi::String path, jsi::String base64Content, jsi::String encoding) = 0;
|
|
35
36
|
|
|
36
37
|
};
|
|
37
38
|
|
|
@@ -154,6 +155,14 @@ private:
|
|
|
154
155
|
return bridging::callFromJs<jsi::Value>(
|
|
155
156
|
rt, &T::clearAllBundles, jsInvoker_, instance_, std::move(a));
|
|
156
157
|
}
|
|
158
|
+
jsi::Value writeFile(jsi::Runtime &rt, jsi::String path, jsi::String base64Content, jsi::String encoding) override {
|
|
159
|
+
static_assert(
|
|
160
|
+
bridging::getParameterCount(&T::writeFile) == 4,
|
|
161
|
+
"Expected writeFile(...) to have 4 parameters");
|
|
162
|
+
|
|
163
|
+
return bridging::callFromJs<jsi::Value>(
|
|
164
|
+
rt, &T::writeFile, jsInvoker_, instance_, std::move(path), std::move(base64Content), std::move(encoding));
|
|
165
|
+
}
|
|
157
166
|
|
|
158
167
|
private:
|
|
159
168
|
friend class NativeOtaHotUpdateCxxSpec;
|
|
@@ -22,7 +22,12 @@ import kotlinx.coroutines.SupervisorJob
|
|
|
22
22
|
import kotlinx.coroutines.cancel
|
|
23
23
|
import kotlinx.coroutines.launch
|
|
24
24
|
import kotlinx.coroutines.withContext
|
|
25
|
+
import android.util.Base64
|
|
26
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
27
|
+
import java.util.concurrent.Executors
|
|
25
28
|
import java.io.File
|
|
29
|
+
import java.io.FileOutputStream
|
|
30
|
+
import java.io.IOException
|
|
26
31
|
import org.json.JSONArray
|
|
27
32
|
import org.json.JSONObject
|
|
28
33
|
|
|
@@ -37,6 +42,7 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
37
42
|
OtaHotUpdateSpec(context) {
|
|
38
43
|
private val utils: Utils = Utils(context)
|
|
39
44
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
|
45
|
+
private val fileWriterExecutor = Executors.newSingleThreadExecutor()
|
|
40
46
|
|
|
41
47
|
override fun getName(): String {
|
|
42
48
|
return NAME
|
|
@@ -45,6 +51,7 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
45
51
|
override fun invalidate() {
|
|
46
52
|
super.invalidate()
|
|
47
53
|
scope.cancel()
|
|
54
|
+
fileWriterExecutor.shutdown()
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
private fun loadBundleHistory(): List<BundleVersion> {
|
|
@@ -509,6 +516,49 @@ class OtaHotUpdateModule internal constructor(context: ReactApplicationContext)
|
|
|
509
516
|
}
|
|
510
517
|
}
|
|
511
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Write file with base64 content on native thread
|
|
521
|
+
* This runs on a background thread, not blocking JS thread
|
|
522
|
+
*/
|
|
523
|
+
@ReactMethod
|
|
524
|
+
override fun writeFile(path: String?, base64Content: String?, encoding: String?, promise: Promise) {
|
|
525
|
+
if (path == null || base64Content == null) {
|
|
526
|
+
promise.reject("INVALID_ARG", "Path and base64Content are required", null)
|
|
527
|
+
return
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
fileWriterExecutor.execute {
|
|
531
|
+
try {
|
|
532
|
+
// Decode base64 to bytes
|
|
533
|
+
val bytes = Base64.decode(base64Content, Base64.DEFAULT)
|
|
534
|
+
|
|
535
|
+
// Ensure parent directory exists
|
|
536
|
+
val file = File(path)
|
|
537
|
+
val parentDir = file.parentFile
|
|
538
|
+
if (parentDir != null && !parentDir.exists()) {
|
|
539
|
+
parentDir.mkdirs()
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Write file on background thread
|
|
543
|
+
FileOutputStream(file).use { fos ->
|
|
544
|
+
fos.write(bytes)
|
|
545
|
+
fos.flush()
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Resolve on UI thread (React Native requirement)
|
|
549
|
+
UiThreadUtil.runOnUiThread {
|
|
550
|
+
promise.resolve(true)
|
|
551
|
+
}
|
|
552
|
+
} catch (e: IOException) {
|
|
553
|
+
UiThreadUtil.runOnUiThread {
|
|
554
|
+
promise.reject("WRITE_ERROR", "Failed to write file: ${e.message}", e)
|
|
555
|
+
}
|
|
556
|
+
} catch (e: Exception) {
|
|
557
|
+
UiThreadUtil.runOnUiThread {
|
|
558
|
+
promise.reject("WRITE_ERROR", "Unexpected error: ${e.message}", e)
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
512
562
|
}
|
|
513
563
|
|
|
514
564
|
companion object {
|
|
@@ -19,4 +19,5 @@ abstract class OtaHotUpdateSpec internal constructor(context: ReactApplicationCo
|
|
|
19
19
|
abstract fun getBundleList(a: Double, promise: Promise)
|
|
20
20
|
abstract fun deleteBundleById(id: String, promise: Promise)
|
|
21
21
|
abstract fun clearAllBundles(a: Double, promise: Promise)
|
|
22
|
+
abstract fun writeFile(path: String?, base64Content: String?, encoding: String?, promise: Promise)
|
|
22
23
|
}
|
package/ios/OtaHotUpdate.mm
CHANGED
|
@@ -14,6 +14,9 @@ static BOOL isBeginning = YES;
|
|
|
14
14
|
@implementation OtaHotUpdate
|
|
15
15
|
RCT_EXPORT_MODULE()
|
|
16
16
|
|
|
17
|
+
+ (BOOL)requiresMainQueueSetup {
|
|
18
|
+
return NO;
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
- (instancetype)init {
|
|
19
22
|
self = [super init];
|
|
@@ -237,7 +240,7 @@ void OTAExceptionHandler(NSException *exception) {
|
|
|
237
240
|
[dateFormatter setDateFormat:@"yyyy_MM_dd_HH_mm"];
|
|
238
241
|
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
|
239
242
|
NSString *timestamp = [dateFormatter stringFromDate:[NSDate date]];
|
|
240
|
-
NSString *folderName = version != nil
|
|
243
|
+
NSString *folderName = version != nil
|
|
241
244
|
? [NSString stringWithFormat:@"output_v%@_%@", version, timestamp]
|
|
242
245
|
: [NSString stringWithFormat:@"output_%@", timestamp];
|
|
243
246
|
NSString *newFolderPath = [directoryPath stringByAppendingPathComponent:folderName];
|
|
@@ -303,7 +306,7 @@ RCT_EXPORT_METHOD(setupBundlePath:(NSString *)path extension:(NSString *)extensi
|
|
|
303
306
|
if (extractedFilePath) {
|
|
304
307
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
305
308
|
NSString *oldPath = [defaults stringForKey:@"PATH"];
|
|
306
|
-
|
|
309
|
+
|
|
307
310
|
// If version is provided, save to history system
|
|
308
311
|
if (version != nil) {
|
|
309
312
|
// Default maxVersions to 2 if not provided (backward compatible)
|
|
@@ -314,7 +317,7 @@ RCT_EXPORT_METHOD(setupBundlePath:(NSString *)path extension:(NSString *)extensi
|
|
|
314
317
|
// No version (e.g., Git update) - just set path, no history
|
|
315
318
|
[defaults setObject:extractedFilePath forKey:@"PATH"];
|
|
316
319
|
}
|
|
317
|
-
|
|
320
|
+
|
|
318
321
|
[defaults setObject:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"] forKey:@"VERSION_NAME"];
|
|
319
322
|
[defaults synchronize];
|
|
320
323
|
isBeginning = YES;
|
|
@@ -331,10 +334,10 @@ RCT_EXPORT_METHOD(deleteBundle:(double)i
|
|
|
331
334
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
332
335
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
333
336
|
NSString *currentPath = [defaults stringForKey:@"PATH"];
|
|
334
|
-
|
|
337
|
+
|
|
335
338
|
// Delete current bundle from file system
|
|
336
339
|
BOOL isDeleted = [OtaHotUpdate removeBundleIfNeeded:@"PATH"];
|
|
337
|
-
|
|
340
|
+
|
|
338
341
|
// Remove current bundle from history if exists
|
|
339
342
|
if (currentPath && currentPath.length > 0) {
|
|
340
343
|
NSArray *history = [self loadBundleHistory];
|
|
@@ -346,12 +349,12 @@ RCT_EXPORT_METHOD(deleteBundle:(double)i
|
|
|
346
349
|
}
|
|
347
350
|
[self saveBundleHistory:updatedHistory];
|
|
348
351
|
}
|
|
349
|
-
|
|
352
|
+
|
|
350
353
|
// Clear paths and version
|
|
351
354
|
[defaults removeObjectForKey:@"PATH"];
|
|
352
355
|
[defaults setObject:@"0" forKey:@"VERSION"];
|
|
353
356
|
[defaults synchronize];
|
|
354
|
-
|
|
357
|
+
|
|
355
358
|
resolve(@(isDeleted));
|
|
356
359
|
}
|
|
357
360
|
// Expose deleteBundle method to JavaScript
|
|
@@ -360,7 +363,7 @@ RCT_EXPORT_METHOD(rollbackToPreviousBundle:(double)i
|
|
|
360
363
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
361
364
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
362
365
|
NSString *currentPath = [defaults stringForKey:@"PATH"];
|
|
363
|
-
|
|
366
|
+
|
|
364
367
|
// Use history to find previous version
|
|
365
368
|
NSArray *history = [self loadBundleHistory];
|
|
366
369
|
if (history.count > 0 && currentPath && currentPath.length > 0) {
|
|
@@ -372,7 +375,7 @@ RCT_EXPORT_METHOD(rollbackToPreviousBundle:(double)i
|
|
|
372
375
|
break;
|
|
373
376
|
}
|
|
374
377
|
}
|
|
375
|
-
|
|
378
|
+
|
|
376
379
|
if (currentBundle) {
|
|
377
380
|
// Find previous version (older than current, max version)
|
|
378
381
|
NSDictionary *previousBundle = nil;
|
|
@@ -385,7 +388,7 @@ RCT_EXPORT_METHOD(rollbackToPreviousBundle:(double)i
|
|
|
385
388
|
}
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
|
-
|
|
391
|
+
|
|
389
392
|
if (previousBundle && [OtaHotUpdate isFilePathValid:previousBundle[@"path"]]) {
|
|
390
393
|
// Rollback to previous bundle from history
|
|
391
394
|
BOOL isDeleted = [OtaHotUpdate removeBundleIfNeeded:@"PATH"];
|
|
@@ -399,7 +402,7 @@ RCT_EXPORT_METHOD(rollbackToPreviousBundle:(double)i
|
|
|
399
402
|
}
|
|
400
403
|
}
|
|
401
404
|
}
|
|
402
|
-
|
|
405
|
+
|
|
403
406
|
resolve(@(NO));
|
|
404
407
|
}
|
|
405
408
|
|
|
@@ -478,7 +481,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
478
481
|
- (NSArray *)loadBundleHistory {
|
|
479
482
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
480
483
|
NSString *historyJson = [defaults stringForKey:@"BUNDLE_HISTORY"];
|
|
481
|
-
|
|
484
|
+
|
|
482
485
|
// If history exists, load it
|
|
483
486
|
if (historyJson && historyJson.length > 0) {
|
|
484
487
|
NSData *data = [historyJson dataUsingEncoding:NSUTF8StringEncoding];
|
|
@@ -488,20 +491,20 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
488
491
|
return history;
|
|
489
492
|
}
|
|
490
493
|
}
|
|
491
|
-
|
|
494
|
+
|
|
492
495
|
// Migration: If history is empty but PATH exists, migrate from old system
|
|
493
496
|
NSString *currentPath = [defaults stringForKey:@"PATH"];
|
|
494
497
|
NSString *currentVersion = [defaults stringForKey:@"VERSION"];
|
|
495
498
|
NSString *previousPath = [defaults stringForKey:@"OLD_PATH"];
|
|
496
499
|
NSString *previousVersion = [defaults stringForKey:@"PREVIOUS_VERSION"];
|
|
497
|
-
|
|
500
|
+
|
|
498
501
|
if (!currentPath || currentPath.length == 0) {
|
|
499
502
|
return @[];
|
|
500
503
|
}
|
|
501
|
-
|
|
504
|
+
|
|
502
505
|
// Migrate current bundle
|
|
503
506
|
NSMutableArray *migratedHistory = [NSMutableArray array];
|
|
504
|
-
|
|
507
|
+
|
|
505
508
|
// Add current bundle if has version
|
|
506
509
|
if (currentVersion && currentVersion.length > 0) {
|
|
507
510
|
NSInteger version = [currentVersion integerValue];
|
|
@@ -510,7 +513,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
510
513
|
NSDictionary *attributes = [fileManager attributesOfItemAtPath:currentPath error:nil];
|
|
511
514
|
NSDate *modificationDate = attributes[NSFileModificationDate];
|
|
512
515
|
long long timestamp = modificationDate ? (long long)([modificationDate timeIntervalSince1970] * 1000) : (long long)([[NSDate date] timeIntervalSince1970] * 1000);
|
|
513
|
-
|
|
516
|
+
|
|
514
517
|
NSMutableDictionary *bundle = [NSMutableDictionary dictionary];
|
|
515
518
|
bundle[@"version"] = @(version);
|
|
516
519
|
bundle[@"path"] = currentPath;
|
|
@@ -519,7 +522,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
519
522
|
[migratedHistory addObject:bundle];
|
|
520
523
|
}
|
|
521
524
|
}
|
|
522
|
-
|
|
525
|
+
|
|
523
526
|
// Add previous bundle if exists
|
|
524
527
|
if (previousPath && previousPath.length > 0 && previousVersion && previousVersion.length > 0) {
|
|
525
528
|
NSInteger version = [previousVersion integerValue];
|
|
@@ -528,7 +531,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
528
531
|
NSDictionary *attributes = [fileManager attributesOfItemAtPath:previousPath error:nil];
|
|
529
532
|
NSDate *modificationDate = attributes[NSFileModificationDate];
|
|
530
533
|
long long timestamp = modificationDate ? (long long)([modificationDate timeIntervalSince1970] * 1000) : (long long)([[NSDate date] timeIntervalSince1970] * 1000);
|
|
531
|
-
|
|
534
|
+
|
|
532
535
|
NSMutableDictionary *bundle = [NSMutableDictionary dictionary];
|
|
533
536
|
bundle[@"version"] = @(version);
|
|
534
537
|
bundle[@"path"] = previousPath;
|
|
@@ -537,7 +540,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
537
540
|
[migratedHistory addObject:bundle];
|
|
538
541
|
}
|
|
539
542
|
}
|
|
540
|
-
|
|
543
|
+
|
|
541
544
|
// Save migrated history if any
|
|
542
545
|
if (migratedHistory.count > 0) {
|
|
543
546
|
// Sort by version descending
|
|
@@ -551,7 +554,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
551
554
|
[self saveBundleHistory:sortedHistory];
|
|
552
555
|
return sortedHistory;
|
|
553
556
|
}
|
|
554
|
-
|
|
557
|
+
|
|
555
558
|
return @[];
|
|
556
559
|
}
|
|
557
560
|
|
|
@@ -573,7 +576,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
573
576
|
- (void)saveBundleVersion:(NSString *)path version:(NSInteger)version maxVersions:(NSInteger)maxVersions metadata:(NSString *)metadata {
|
|
574
577
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
575
578
|
NSArray *history = [self loadBundleHistory];
|
|
576
|
-
|
|
579
|
+
|
|
577
580
|
// Create new bundle entry
|
|
578
581
|
NSMutableDictionary *newBundle = [NSMutableDictionary dictionary];
|
|
579
582
|
newBundle[@"version"] = @(version);
|
|
@@ -582,11 +585,11 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
582
585
|
if (metadata) {
|
|
583
586
|
newBundle[@"metadata"] = metadata;
|
|
584
587
|
}
|
|
585
|
-
|
|
588
|
+
|
|
586
589
|
// Combine with existing history
|
|
587
590
|
NSMutableArray *updatedHistory = [NSMutableArray arrayWithObject:newBundle];
|
|
588
591
|
[updatedHistory addObjectsFromArray:history];
|
|
589
|
-
|
|
592
|
+
|
|
590
593
|
// Sort by version descending and remove duplicates
|
|
591
594
|
[updatedHistory sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
|
|
592
595
|
NSInteger v1 = [obj1[@"version"] integerValue];
|
|
@@ -595,7 +598,7 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
595
598
|
if (v1 < v2) return NSOrderedDescending;
|
|
596
599
|
return NSOrderedSame;
|
|
597
600
|
}];
|
|
598
|
-
|
|
601
|
+
|
|
599
602
|
// Remove duplicates by version
|
|
600
603
|
NSMutableArray *uniqueHistory = [NSMutableArray array];
|
|
601
604
|
NSMutableSet *seenVersions = [NSMutableSet set];
|
|
@@ -606,25 +609,25 @@ RCT_EXPORT_METHOD(setExactBundlePath:(NSString *)path
|
|
|
606
609
|
[uniqueHistory addObject:bundle];
|
|
607
610
|
}
|
|
608
611
|
}
|
|
609
|
-
|
|
612
|
+
|
|
610
613
|
// Keep only maxVersions most recent
|
|
611
614
|
NSArray *finalHistory = [uniqueHistory subarrayWithRange:NSMakeRange(0, MIN(maxVersions, uniqueHistory.count))];
|
|
612
|
-
|
|
615
|
+
|
|
613
616
|
// Delete old versions beyond limit
|
|
614
617
|
NSMutableSet *versionsToKeep = [NSMutableSet set];
|
|
615
618
|
for (NSDictionary *bundle in finalHistory) {
|
|
616
619
|
[versionsToKeep addObject:bundle[@"version"]];
|
|
617
620
|
}
|
|
618
|
-
|
|
621
|
+
|
|
619
622
|
for (NSDictionary *bundle in uniqueHistory) {
|
|
620
623
|
if (![versionsToKeep containsObject:bundle[@"version"]]) {
|
|
621
624
|
[OtaHotUpdate deleteBundleAtPath:bundle[@"path"]];
|
|
622
625
|
}
|
|
623
626
|
}
|
|
624
|
-
|
|
627
|
+
|
|
625
628
|
// Save updated history
|
|
626
629
|
[self saveBundleHistory:finalHistory];
|
|
627
|
-
|
|
630
|
+
|
|
628
631
|
// Set current path and version
|
|
629
632
|
[defaults setObject:path forKey:@"PATH"];
|
|
630
633
|
[defaults setObject:[NSString stringWithFormat:@"%ld", (long)version] forKey:@"VERSION"];
|
|
@@ -649,7 +652,7 @@ RCT_EXPORT_METHOD(getBundleList:(double)a
|
|
|
649
652
|
NSArray *history = [self loadBundleHistory];
|
|
650
653
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
651
654
|
NSString *activePath = [defaults stringForKey:@"PATH"];
|
|
652
|
-
|
|
655
|
+
|
|
653
656
|
NSMutableArray *bundleList = [NSMutableArray array];
|
|
654
657
|
for (NSDictionary *bundle in history) {
|
|
655
658
|
NSString *path = bundle[@"path"];
|
|
@@ -669,7 +672,7 @@ RCT_EXPORT_METHOD(getBundleList:(double)a
|
|
|
669
672
|
}
|
|
670
673
|
[bundleList addObject:bundleInfo];
|
|
671
674
|
}
|
|
672
|
-
|
|
675
|
+
|
|
673
676
|
NSError *error = nil;
|
|
674
677
|
NSData *data = [NSJSONSerialization dataWithJSONObject:bundleList options:0 error:&error];
|
|
675
678
|
if (!error && data) {
|
|
@@ -686,7 +689,7 @@ RCT_EXPORT_METHOD(deleteBundleById:(NSString *)id
|
|
|
686
689
|
NSArray *history = [self loadBundleHistory];
|
|
687
690
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
688
691
|
NSString *activePath = [defaults stringForKey:@"PATH"];
|
|
689
|
-
|
|
692
|
+
|
|
690
693
|
NSDictionary *bundleToDelete = nil;
|
|
691
694
|
for (NSDictionary *bundle in history) {
|
|
692
695
|
NSString *folderName = [self extractFolderName:bundle[@"path"]];
|
|
@@ -695,12 +698,12 @@ RCT_EXPORT_METHOD(deleteBundleById:(NSString *)id
|
|
|
695
698
|
break;
|
|
696
699
|
}
|
|
697
700
|
}
|
|
698
|
-
|
|
701
|
+
|
|
699
702
|
if (!bundleToDelete) {
|
|
700
703
|
resolve(@(NO));
|
|
701
704
|
return;
|
|
702
705
|
}
|
|
703
|
-
|
|
706
|
+
|
|
704
707
|
// If deleting active bundle, rollback to oldest remaining bundle or clear
|
|
705
708
|
if ([bundleToDelete[@"path"] isEqualToString:activePath]) {
|
|
706
709
|
NSMutableArray *remainingBundles = [NSMutableArray array];
|
|
@@ -725,10 +728,10 @@ RCT_EXPORT_METHOD(deleteBundleById:(NSString *)id
|
|
|
725
728
|
}
|
|
726
729
|
[defaults synchronize];
|
|
727
730
|
}
|
|
728
|
-
|
|
731
|
+
|
|
729
732
|
// Delete bundle folder
|
|
730
733
|
BOOL isDeleted = [OtaHotUpdate deleteBundleAtPath:bundleToDelete[@"path"]];
|
|
731
|
-
|
|
734
|
+
|
|
732
735
|
// Remove from history
|
|
733
736
|
NSMutableArray *updatedHistory = [NSMutableArray array];
|
|
734
737
|
for (NSDictionary *bundle in history) {
|
|
@@ -737,7 +740,7 @@ RCT_EXPORT_METHOD(deleteBundleById:(NSString *)id
|
|
|
737
740
|
}
|
|
738
741
|
}
|
|
739
742
|
[self saveBundleHistory:updatedHistory];
|
|
740
|
-
|
|
743
|
+
|
|
741
744
|
resolve(@(isDeleted));
|
|
742
745
|
}
|
|
743
746
|
|
|
@@ -746,23 +749,63 @@ RCT_EXPORT_METHOD(clearAllBundles:(double)a
|
|
|
746
749
|
reject:(RCTPromiseRejectBlock)reject) {
|
|
747
750
|
NSArray *history = [self loadBundleHistory];
|
|
748
751
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
749
|
-
|
|
752
|
+
|
|
750
753
|
// Delete all bundle folders
|
|
751
754
|
for (NSDictionary *bundle in history) {
|
|
752
755
|
[OtaHotUpdate deleteBundleAtPath:bundle[@"path"]];
|
|
753
756
|
}
|
|
754
|
-
|
|
757
|
+
|
|
755
758
|
// Clear history
|
|
756
759
|
[self saveBundleHistory:@[]];
|
|
757
|
-
|
|
760
|
+
|
|
758
761
|
// Clear current path and version
|
|
759
762
|
[defaults removeObjectForKey:@"PATH"];
|
|
760
763
|
[defaults removeObjectForKey:@"VERSION"];
|
|
761
764
|
[defaults synchronize];
|
|
762
|
-
|
|
765
|
+
|
|
763
766
|
resolve(@(YES));
|
|
764
767
|
}
|
|
765
768
|
|
|
769
|
+
RCT_EXPORT_METHOD(writeFile:(NSString *)path
|
|
770
|
+
base64Content:(NSString *)base64Content
|
|
771
|
+
encoding:(NSString *)encoding
|
|
772
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
773
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
774
|
+
// Run on background queue to avoid blocking JS thread
|
|
775
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
776
|
+
@try {
|
|
777
|
+
// Decode base64 to NSData
|
|
778
|
+
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Content options:0];
|
|
779
|
+
if (!data) {
|
|
780
|
+
reject(@"DECODE_ERROR", @"Failed to decode base64 content", nil);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Ensure parent directory exists
|
|
785
|
+
NSString *parentDir = [path stringByDeletingLastPathComponent];
|
|
786
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
787
|
+
if (![fileManager fileExistsAtPath:parentDir]) {
|
|
788
|
+
NSError *error;
|
|
789
|
+
if (![fileManager createDirectoryAtPath:parentDir withIntermediateDirectories:YES attributes:nil error:&error]) {
|
|
790
|
+
reject(@"CREATE_DIR_ERROR", [NSString stringWithFormat:@"Failed to create directory: %@", error.localizedDescription], error);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Write file on background thread
|
|
796
|
+
NSError *error;
|
|
797
|
+
if (![data writeToFile:path options:NSDataWritingAtomic error:&error]) {
|
|
798
|
+
reject(@"WRITE_ERROR", [NSString stringWithFormat:@"Failed to write file: %@", error.localizedDescription], error);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
resolve(@(YES));
|
|
803
|
+
} @catch (NSException *exception) {
|
|
804
|
+
reject(@"WRITE_ERROR", [NSString stringWithFormat:@"Unexpected error: %@", exception.reason], nil);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
|
|
766
809
|
// Don't compile this code when we build for the old architecture.
|
|
767
810
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
768
811
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
@@ -74,6 +74,10 @@ namespace facebook::react {
|
|
|
74
74
|
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "clearAllBundles", @selector(clearAllBundles:resolve:reject:), args, count);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
static facebook::jsi::Value __hostFunction_NativeOtaHotUpdateSpecJSI_writeFile(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
78
|
+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "writeFile", @selector(writeFile:base64Content:encoding:resolve:reject:), args, count);
|
|
79
|
+
}
|
|
80
|
+
|
|
77
81
|
NativeOtaHotUpdateSpecJSI::NativeOtaHotUpdateSpecJSI(const ObjCTurboModule::InitParams ¶ms)
|
|
78
82
|
: ObjCTurboModule(params) {
|
|
79
83
|
|
|
@@ -112,5 +116,8 @@ namespace facebook::react {
|
|
|
112
116
|
|
|
113
117
|
methodMap_["clearAllBundles"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateSpecJSI_clearAllBundles};
|
|
114
118
|
|
|
119
|
+
|
|
120
|
+
methodMap_["writeFile"] = MethodMetadata {3, __hostFunction_NativeOtaHotUpdateSpecJSI_writeFile};
|
|
121
|
+
|
|
115
122
|
}
|
|
116
123
|
} // namespace facebook::react
|
|
@@ -71,6 +71,11 @@
|
|
|
71
71
|
- (void)clearAllBundles:(double)a
|
|
72
72
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
73
73
|
reject:(RCTPromiseRejectBlock)reject;
|
|
74
|
+
- (void)writeFile:(NSString *)path
|
|
75
|
+
base64Content:(NSString *)base64Content
|
|
76
|
+
encoding:(NSString *)encoding
|
|
77
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
78
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
74
79
|
|
|
75
80
|
@end
|
|
76
81
|
|
|
@@ -87,6 +87,14 @@ static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_clearAllBundles(js
|
|
|
87
87
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asNumber()
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
+
static jsi::Value __hostFunction_NativeOtaHotUpdateCxxSpecJSI_writeFile(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
91
|
+
return static_cast<NativeOtaHotUpdateCxxSpecJSI *>(&turboModule)->writeFile(
|
|
92
|
+
rt,
|
|
93
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt),
|
|
94
|
+
count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asString(rt),
|
|
95
|
+
count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asString(rt)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
90
98
|
|
|
91
99
|
NativeOtaHotUpdateCxxSpecJSI::NativeOtaHotUpdateCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
92
100
|
: TurboModule("OtaHotUpdate", jsInvoker) {
|
|
@@ -102,6 +110,7 @@ NativeOtaHotUpdateCxxSpecJSI::NativeOtaHotUpdateCxxSpecJSI(std::shared_ptr<CallI
|
|
|
102
110
|
methodMap_["getBundleList"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_getBundleList};
|
|
103
111
|
methodMap_["deleteBundleById"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_deleteBundleById};
|
|
104
112
|
methodMap_["clearAllBundles"] = MethodMetadata {1, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_clearAllBundles};
|
|
113
|
+
methodMap_["writeFile"] = MethodMetadata {3, __hostFunction_NativeOtaHotUpdateCxxSpecJSI_writeFile};
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
|
|
@@ -32,6 +32,7 @@ public:
|
|
|
32
32
|
virtual jsi::Value getBundleList(jsi::Runtime &rt, double a) = 0;
|
|
33
33
|
virtual jsi::Value deleteBundleById(jsi::Runtime &rt, jsi::String id) = 0;
|
|
34
34
|
virtual jsi::Value clearAllBundles(jsi::Runtime &rt, double a) = 0;
|
|
35
|
+
virtual jsi::Value writeFile(jsi::Runtime &rt, jsi::String path, jsi::String base64Content, jsi::String encoding) = 0;
|
|
35
36
|
|
|
36
37
|
};
|
|
37
38
|
|
|
@@ -154,6 +155,14 @@ private:
|
|
|
154
155
|
return bridging::callFromJs<jsi::Value>(
|
|
155
156
|
rt, &T::clearAllBundles, jsInvoker_, instance_, std::move(a));
|
|
156
157
|
}
|
|
158
|
+
jsi::Value writeFile(jsi::Runtime &rt, jsi::String path, jsi::String base64Content, jsi::String encoding) override {
|
|
159
|
+
static_assert(
|
|
160
|
+
bridging::getParameterCount(&T::writeFile) == 4,
|
|
161
|
+
"Expected writeFile(...) to have 4 parameters");
|
|
162
|
+
|
|
163
|
+
return bridging::callFromJs<jsi::Value>(
|
|
164
|
+
rt, &T::writeFile, jsInvoker_, instance_, std::move(path), std::move(base64Content), std::move(encoding));
|
|
165
|
+
}
|
|
157
166
|
|
|
158
167
|
private:
|
|
159
168
|
friend class NativeOtaHotUpdateCxxSpec;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeOtaHotUpdate.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAmD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_default","exports","default","TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeOtaHotUpdate.ts"],"mappings":";;;;;;AACA,IAAAA,YAAA,GAAAC,OAAA;AAAmD,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAkBpCC,gCAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}
|
|
@@ -16,6 +16,17 @@ let RNFS = {
|
|
|
16
16
|
try {
|
|
17
17
|
RNFS = require('react-native-fs');
|
|
18
18
|
} catch {}
|
|
19
|
+
|
|
20
|
+
// Try to load native OtaHotUpdate module for better performance
|
|
21
|
+
let OtaHotUpdateNative = null;
|
|
22
|
+
try {
|
|
23
|
+
const {
|
|
24
|
+
NativeModules
|
|
25
|
+
} = require('react-native');
|
|
26
|
+
OtaHotUpdateNative = NativeModules.OtaHotUpdate;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Native module not available, will fallback to RNFS
|
|
29
|
+
}
|
|
19
30
|
function Err(name) {
|
|
20
31
|
return class extends Error {
|
|
21
32
|
code = name;
|
|
@@ -83,12 +94,27 @@ const writeFile = async (path, content, opts) => {
|
|
|
83
94
|
encoding = opts.encoding;
|
|
84
95
|
}
|
|
85
96
|
if (typeof content === 'string') {
|
|
97
|
+
// Text file - use RNFS (fast enough for text)
|
|
86
98
|
encoding = encoding || 'utf8';
|
|
99
|
+
await RNFS.writeFile(path, content, encoding);
|
|
87
100
|
} else {
|
|
101
|
+
// Binary file - try native module first for better performance
|
|
88
102
|
encoding = 'base64';
|
|
89
|
-
|
|
103
|
+
const base64Content = _buffer.Buffer.from(content).toString('base64');
|
|
104
|
+
if (OtaHotUpdateNative && OtaHotUpdateNative.writeFile) {
|
|
105
|
+
try {
|
|
106
|
+
// Use native write (runs on native thread, doesn't block JS thread)
|
|
107
|
+
await OtaHotUpdateNative.writeFile(path, base64Content, encoding);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
// Fallback to RNFS if native fails
|
|
110
|
+
console.warn('OtaHotUpdate.writeFile failed, falling back to RNFS:', e);
|
|
111
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
// No native module available - use RNFS
|
|
115
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
116
|
+
}
|
|
90
117
|
}
|
|
91
|
-
await RNFS.writeFile(path, content, encoding);
|
|
92
118
|
};
|
|
93
119
|
exports.writeFile = writeFile;
|
|
94
120
|
const stat = async path => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_buffer","require","RNFS","unlink","console","log","readdir","mkdir","readFile","writeFile","stat","Err","name","Error","code","constructor","args","message","ENOENT","ENOTDIR","path","err","exports","opts","encoding","result","Buffer","from","content","toString","r","isSymbolicLink","lstat","rmdir","readlink","symlink","chmod"],"sourceRoot":"../../../../src","sources":["gits/helper/fs.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAEA,IAAIC,IAAI,GAAG;EACTC,MAAM,EAAEC,OAAO,CAACC,GAAG;EACnBC,OAAO,EAAEF,OAAO,CAACC,GAAG;EACpBE,KAAK,EAAEH,OAAO,CAACC,GAAG;EAClBG,QAAQ,EAAEJ,OAAO,CAACC,GAAG;EACrBI,SAAS,EAAEL,OAAO,CAACC,GAAG;EACtBK,IAAI,EAAEN,OAAO,CAACC;AAChB,CAAC;AACD,IAAI;EACFH,IAAI,GAAGD,OAAO,CAAC,iBAAiB,CAAC;AACnC,CAAC,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"names":["_buffer","require","RNFS","unlink","console","log","readdir","mkdir","readFile","writeFile","stat","OtaHotUpdateNative","NativeModules","OtaHotUpdate","e","Err","name","Error","code","constructor","args","message","ENOENT","ENOTDIR","path","err","exports","opts","encoding","result","Buffer","from","content","base64Content","toString","warn","r","isSymbolicLink","lstat","rmdir","readlink","symlink","chmod"],"sourceRoot":"../../../../src","sources":["gits/helper/fs.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeOtaHotUpdate.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeOtaHotUpdate.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAkBlD,eAAeA,mBAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}
|
|
@@ -12,6 +12,17 @@ let RNFS = {
|
|
|
12
12
|
try {
|
|
13
13
|
RNFS = require('react-native-fs');
|
|
14
14
|
} catch {}
|
|
15
|
+
|
|
16
|
+
// Try to load native OtaHotUpdate module for better performance
|
|
17
|
+
let OtaHotUpdateNative = null;
|
|
18
|
+
try {
|
|
19
|
+
const {
|
|
20
|
+
NativeModules
|
|
21
|
+
} = require('react-native');
|
|
22
|
+
OtaHotUpdateNative = NativeModules.OtaHotUpdate;
|
|
23
|
+
} catch (e) {
|
|
24
|
+
// Native module not available, will fallback to RNFS
|
|
25
|
+
}
|
|
15
26
|
function Err(name) {
|
|
16
27
|
return class extends Error {
|
|
17
28
|
code = name;
|
|
@@ -76,12 +87,27 @@ export const writeFile = async (path, content, opts) => {
|
|
|
76
87
|
encoding = opts.encoding;
|
|
77
88
|
}
|
|
78
89
|
if (typeof content === 'string') {
|
|
90
|
+
// Text file - use RNFS (fast enough for text)
|
|
79
91
|
encoding = encoding || 'utf8';
|
|
92
|
+
await RNFS.writeFile(path, content, encoding);
|
|
80
93
|
} else {
|
|
94
|
+
// Binary file - try native module first for better performance
|
|
81
95
|
encoding = 'base64';
|
|
82
|
-
|
|
96
|
+
const base64Content = Buffer.from(content).toString('base64');
|
|
97
|
+
if (OtaHotUpdateNative && OtaHotUpdateNative.writeFile) {
|
|
98
|
+
try {
|
|
99
|
+
// Use native write (runs on native thread, doesn't block JS thread)
|
|
100
|
+
await OtaHotUpdateNative.writeFile(path, base64Content, encoding);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// Fallback to RNFS if native fails
|
|
103
|
+
console.warn('OtaHotUpdate.writeFile failed, falling back to RNFS:', e);
|
|
104
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
// No native module available - use RNFS
|
|
108
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
109
|
+
}
|
|
83
110
|
}
|
|
84
|
-
await RNFS.writeFile(path, content, encoding);
|
|
85
111
|
};
|
|
86
112
|
export const stat = async path => {
|
|
87
113
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Buffer","RNFS","unlink","console","log","readdir","mkdir","readFile","writeFile","stat","require","Err","name","Error","code","constructor","args","message","ENOENT","ENOTDIR","path","err","opts","encoding","result","from","content","toString","r","isSymbolicLink","lstat","rmdir","readlink","symlink","chmod"],"sourceRoot":"../../../../src","sources":["gits/helper/fs.ts"],"mappings":";;AAAA,SAASA,MAAM,QAAQ,QAAQ;AAE/B,IAAIC,IAAI,GAAG;EACTC,MAAM,EAAEC,OAAO,CAACC,GAAG;EACnBC,OAAO,EAAEF,OAAO,CAACC,GAAG;EACpBE,KAAK,EAAEH,OAAO,CAACC,GAAG;EAClBG,QAAQ,EAAEJ,OAAO,CAACC,GAAG;EACrBI,SAAS,EAAEL,OAAO,CAACC,GAAG;EACtBK,IAAI,EAAEN,OAAO,CAACC;AAChB,CAAC;AACD,IAAI;EACFH,IAAI,GAAGS,OAAO,CAAC,iBAAiB,CAAC;AACnC,CAAC,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"names":["Buffer","RNFS","unlink","console","log","readdir","mkdir","readFile","writeFile","stat","require","OtaHotUpdateNative","NativeModules","OtaHotUpdate","e","Err","name","Error","code","constructor","args","message","ENOENT","ENOTDIR","path","err","opts","encoding","result","from","content","base64Content","toString","warn","r","isSymbolicLink","lstat","rmdir","readlink","symlink","chmod"],"sourceRoot":"../../../../src","sources":["gits/helper/fs.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -12,6 +12,7 @@ export interface Spec extends TurboModule {
|
|
|
12
12
|
getBundleList(a: number): Promise<string>;
|
|
13
13
|
deleteBundleById(id: string): Promise<boolean>;
|
|
14
14
|
clearAllBundles(a: number): Promise<boolean>;
|
|
15
|
+
writeFile(path: string, base64Content: string, encoding: string): Promise<boolean>;
|
|
15
16
|
}
|
|
16
17
|
declare const _default: Spec;
|
|
17
18
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeOtaHotUpdate.d.ts","sourceRoot":"","sources":["../../../../src/NativeOtaHotUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9H,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;IAChB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"NativeOtaHotUpdate.d.ts","sourceRoot":"","sources":["../../../../src/NativeOtaHotUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9H,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;IAChB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;;AAED,wBAAsE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../../src/gits/helper/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../../src/gits/helper/fs.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,OAAO,SAAgB,MAAM,kBAezC,CAAC;AAEF,eAAO,MAAM,KAAK,SAAgB,MAAM,kBAEvC,CAAC;AAEF,eAAO,MAAM,QAAQ,SACb,MAAM,SACL,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,kDAsB1C,CAAC;AACF,eAAO,MAAM,SAAS,SACd,MAAM,WACH,MAAM,GAAG,UAAU,SACrB,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,kBAiC1C,CAAC;AAEF,eAAO,MAAM,IAAI,SAAgB,MAAM,kBAiBtC,CAAC;AAGF,eAAO,MAAM,KAAK,SApBe,MAAM,kBAoBd,CAAC;AAE1B,eAAO,MAAM,MAAM,SAAgB,MAAM,kBAYxC,CAAC;AAGF,eAAO,MAAM,KAAK,SAfiB,MAAM,kBAed,CAAC;AAG5B,eAAO,MAAM,QAAQ,sBAEpB,CAAC;AACF,eAAO,MAAM,OAAO,sBAEnB,CAAC;AAIF,eAAO,MAAM,KAAK,sBAEjB,CAAC"}
|
|
@@ -12,6 +12,7 @@ export interface Spec extends TurboModule {
|
|
|
12
12
|
getBundleList(a: number): Promise<string>;
|
|
13
13
|
deleteBundleById(id: string): Promise<boolean>;
|
|
14
14
|
clearAllBundles(a: number): Promise<boolean>;
|
|
15
|
+
writeFile(path: string, base64Content: string, encoding: string): Promise<boolean>;
|
|
15
16
|
}
|
|
16
17
|
declare const _default: Spec;
|
|
17
18
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeOtaHotUpdate.d.ts","sourceRoot":"","sources":["../../../../src/NativeOtaHotUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9H,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;IAChB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"NativeOtaHotUpdate.d.ts","sourceRoot":"","sources":["../../../../src/NativeOtaHotUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9H,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;IAChB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,wBAAwB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;;AAED,wBAAsE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../../src/gits/helper/fs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../../src/gits/helper/fs.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,OAAO,SAAgB,MAAM,kBAezC,CAAC;AAEF,eAAO,MAAM,KAAK,SAAgB,MAAM,kBAEvC,CAAC;AAEF,eAAO,MAAM,QAAQ,SACb,MAAM,SACL,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,kDAsB1C,CAAC;AACF,eAAO,MAAM,SAAS,SACd,MAAM,WACH,MAAM,GAAG,UAAU,SACrB,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,kBAiC1C,CAAC;AAEF,eAAO,MAAM,IAAI,SAAgB,MAAM,kBAiBtC,CAAC;AAGF,eAAO,MAAM,KAAK,SApBe,MAAM,kBAoBd,CAAC;AAE1B,eAAO,MAAM,MAAM,SAAgB,MAAM,kBAYxC,CAAC;AAGF,eAAO,MAAM,KAAK,SAfiB,MAAM,kBAed,CAAC;AAG5B,eAAO,MAAM,QAAQ,sBAEpB,CAAC;AACF,eAAO,MAAM,OAAO,sBAEnB,CAAC;AAIF,eAAO,MAAM,KAAK,sBAEjB,CAAC"}
|
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ export interface Spec extends TurboModule {
|
|
|
14
14
|
getBundleList(a: number): Promise<string>;
|
|
15
15
|
deleteBundleById(id: string): Promise<boolean>;
|
|
16
16
|
clearAllBundles(a: number): Promise<boolean>;
|
|
17
|
+
writeFile(path: string, base64Content: string, encoding: string): Promise<boolean>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export default TurboModuleRegistry.getEnforcing<Spec>('OtaHotUpdate');
|
package/src/gits/helper/fs.ts
CHANGED
|
@@ -12,6 +12,15 @@ try {
|
|
|
12
12
|
RNFS = require('react-native-fs');
|
|
13
13
|
} catch {}
|
|
14
14
|
|
|
15
|
+
// Try to load native OtaHotUpdate module for better performance
|
|
16
|
+
let OtaHotUpdateNative: any = null;
|
|
17
|
+
try {
|
|
18
|
+
const { NativeModules } = require('react-native');
|
|
19
|
+
OtaHotUpdateNative = NativeModules.OtaHotUpdate;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// Native module not available, will fallback to RNFS
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
function Err(name: string) {
|
|
16
25
|
return class extends Error {
|
|
17
26
|
public code = name;
|
|
@@ -91,13 +100,28 @@ export const writeFile = async (
|
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
if (typeof content === 'string') {
|
|
103
|
+
// Text file - use RNFS (fast enough for text)
|
|
94
104
|
encoding = encoding || 'utf8';
|
|
105
|
+
await RNFS.writeFile(path, content, encoding);
|
|
95
106
|
} else {
|
|
107
|
+
// Binary file - try native module first for better performance
|
|
96
108
|
encoding = 'base64';
|
|
97
|
-
|
|
109
|
+
const base64Content = Buffer.from(content).toString('base64');
|
|
110
|
+
|
|
111
|
+
if (OtaHotUpdateNative && OtaHotUpdateNative.writeFile) {
|
|
112
|
+
try {
|
|
113
|
+
// Use native write (runs on native thread, doesn't block JS thread)
|
|
114
|
+
await OtaHotUpdateNative.writeFile(path, base64Content, encoding);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
// Fallback to RNFS if native fails
|
|
117
|
+
console.warn('OtaHotUpdate.writeFile failed, falling back to RNFS:', e);
|
|
118
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
// No native module available - use RNFS
|
|
122
|
+
await RNFS.writeFile(path, base64Content, encoding);
|
|
123
|
+
}
|
|
98
124
|
}
|
|
99
|
-
|
|
100
|
-
await RNFS.writeFile(path, content as string, encoding);
|
|
101
125
|
};
|
|
102
126
|
|
|
103
127
|
export const stat = async (path: string) => {
|