@vanikya/ota-react-native 0.1.9 → 0.2.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.
- package/README.md +135 -1
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/otaupdate/OTAUpdateModule.kt +116 -25
- package/app.plugin.js +247 -26
- package/ios/OTAUpdate.swift +29 -0
- package/lib/commonjs/OTAProvider.js +37 -0
- package/lib/commonjs/OTAProvider.js.map +1 -1
- package/lib/commonjs/components/OTADebugPanel.js +426 -0
- package/lib/commonjs/components/OTADebugPanel.js.map +1 -0
- package/lib/commonjs/hooks/useOTAUpdate.js +38 -2
- package/lib/commonjs/hooks/useOTAUpdate.js.map +1 -1
- package/lib/commonjs/index.js +10 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/storage.js +79 -4
- package/lib/commonjs/utils/storage.js.map +1 -1
- package/lib/module/OTAProvider.js +38 -1
- package/lib/module/OTAProvider.js.map +1 -1
- package/lib/module/components/OTADebugPanel.js +418 -0
- package/lib/module/components/OTADebugPanel.js.map +1 -0
- package/lib/module/hooks/useOTAUpdate.js +38 -2
- package/lib/module/hooks/useOTAUpdate.js.map +1 -1
- package/lib/module/index.js +4 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/storage.js +79 -4
- package/lib/module/utils/storage.js.map +1 -1
- package/lib/typescript/OTAProvider.d.ts.map +1 -1
- package/lib/typescript/components/OTADebugPanel.d.ts +18 -0
- package/lib/typescript/components/OTADebugPanel.d.ts.map +1 -0
- package/lib/typescript/hooks/useOTAUpdate.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/storage.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/OTAProvider.tsx +40 -0
- package/src/components/OTADebugPanel.tsx +447 -0
- package/src/hooks/useOTAUpdate.ts +49 -2
- package/src/index.ts +4 -1
- package/src/utils/storage.ts +105 -4
package/app.plugin.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
const { withMainApplication, withAppDelegate
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const { withMainApplication, withAppDelegate } = require('@expo/config-plugins');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OTA Update Expo Config Plugin
|
|
5
|
+
*
|
|
6
|
+
* This plugin modifies the native code to enable OTA bundle loading:
|
|
7
|
+
* - Android: Overrides getJSBundleFile() in MainApplication.kt
|
|
8
|
+
* - iOS: Modifies bundleURL() in AppDelegate.swift
|
|
9
|
+
*
|
|
10
|
+
* The modifications check SharedPreferences (Android) / UserDefaults (iOS)
|
|
11
|
+
* for a pending OTA bundle path and load it instead of the default bundle.
|
|
12
|
+
*/
|
|
4
13
|
|
|
5
14
|
function withOTAUpdateAndroid(config) {
|
|
6
15
|
return withMainApplication(config, (config) => {
|
|
@@ -8,37 +17,152 @@ function withOTAUpdateAndroid(config) {
|
|
|
8
17
|
|
|
9
18
|
// Check if already modified
|
|
10
19
|
if (contents.includes('getJSBundleFile')) {
|
|
20
|
+
console.log('[OTAUpdate] Android: getJSBundleFile already present, skipping');
|
|
11
21
|
return config;
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
// Add imports if not present
|
|
15
25
|
if (!contents.includes('import android.content.SharedPreferences')) {
|
|
26
|
+
// Find the package declaration and add imports after it
|
|
27
|
+
const packageMatch = contents.match(/^package\s+[\w.]+\s*\n/m);
|
|
28
|
+
if (packageMatch) {
|
|
29
|
+
const insertPos = packageMatch.index + packageMatch[0].length;
|
|
30
|
+
const imports = `\nimport android.content.SharedPreferences\nimport java.io.File\n`;
|
|
31
|
+
contents = contents.slice(0, insertPos) + imports + contents.slice(insertPos);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Strategy 1: Look for "override val reactNativeHost" pattern (Expo SDK 50+)
|
|
36
|
+
// This is more reliable than matching getUseDeveloperSupport
|
|
37
|
+
const reactNativeHostPattern = /(override\s+val\s+reactNativeHost\s*:\s*ReactNativeHost\s*=\s*object\s*:\s*DefaultReactNativeHost\s*\(\s*this\s*\)\s*\{)/;
|
|
38
|
+
|
|
39
|
+
if (reactNativeHostPattern.test(contents)) {
|
|
40
|
+
const getJSBundleFileOverride = `
|
|
41
|
+
override fun getJSBundleFile(): String? {
|
|
42
|
+
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
43
|
+
val bundlePath = prefs.getString("BundlePath", null)
|
|
44
|
+
android.util.Log.d("OTAUpdate", "getJSBundleFile called, stored path: $bundlePath")
|
|
45
|
+
if (bundlePath != null) {
|
|
46
|
+
val file = File(bundlePath)
|
|
47
|
+
if (file.exists() && file.canRead()) {
|
|
48
|
+
android.util.Log.d("OTAUpdate", "Loading OTA bundle: $bundlePath (${file.length()} bytes)")
|
|
49
|
+
return bundlePath
|
|
50
|
+
} else {
|
|
51
|
+
android.util.Log.w("OTAUpdate", "OTA bundle not found or not readable: $bundlePath, exists=${file.exists()}")
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
16
58
|
contents = contents.replace(
|
|
17
|
-
|
|
18
|
-
`$1
|
|
59
|
+
reactNativeHostPattern,
|
|
60
|
+
`$1${getJSBundleFileOverride}`
|
|
19
61
|
);
|
|
62
|
+
console.log('[OTAUpdate] Android: Successfully injected getJSBundleFile (pattern 1)');
|
|
63
|
+
config.modResults.contents = contents;
|
|
64
|
+
return config;
|
|
20
65
|
}
|
|
21
66
|
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
const kotlinPattern = /override\s+fun\s+getUseDeveloperSupport\(\):\s+Boolean\s*=\s*BuildConfig\.DEBUG/;
|
|
67
|
+
// Strategy 2: Look for DefaultReactNativeHost with different formatting
|
|
68
|
+
const altPattern = /(object\s*:\s*DefaultReactNativeHost\s*\(\s*this\s*\)\s*\{)/;
|
|
25
69
|
|
|
26
|
-
if (
|
|
70
|
+
if (altPattern.test(contents)) {
|
|
71
|
+
const getJSBundleFileOverride = `
|
|
72
|
+
override fun getJSBundleFile(): String? {
|
|
73
|
+
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
74
|
+
val bundlePath = prefs.getString("BundlePath", null)
|
|
75
|
+
android.util.Log.d("OTAUpdate", "getJSBundleFile called, stored path: $bundlePath")
|
|
76
|
+
if (bundlePath != null) {
|
|
77
|
+
val file = File(bundlePath)
|
|
78
|
+
if (file.exists() && file.canRead()) {
|
|
79
|
+
android.util.Log.d("OTAUpdate", "Loading OTA bundle: $bundlePath (${file.length()} bytes)")
|
|
80
|
+
return bundlePath
|
|
81
|
+
} else {
|
|
82
|
+
android.util.Log.w("OTAUpdate", "OTA bundle not found or not readable: $bundlePath, exists=${file.exists()}")
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
86
|
+
return null
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
27
89
|
contents = contents.replace(
|
|
28
|
-
|
|
29
|
-
`
|
|
90
|
+
altPattern,
|
|
91
|
+
`$1${getJSBundleFileOverride}`
|
|
92
|
+
);
|
|
93
|
+
console.log('[OTAUpdate] Android: Successfully injected getJSBundleFile (pattern 2)');
|
|
94
|
+
config.modResults.contents = contents;
|
|
95
|
+
return config;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Strategy 3: Look for getUseDeveloperSupport with flexible whitespace
|
|
99
|
+
const devSupportPattern = /(override\s+fun\s+getUseDeveloperSupport\s*\(\s*\)\s*[:\s]*Boolean\s*[=\{])/;
|
|
100
|
+
|
|
101
|
+
if (devSupportPattern.test(contents)) {
|
|
102
|
+
const getJSBundleFileOverride = `override fun getJSBundleFile(): String? {
|
|
30
103
|
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
31
104
|
val bundlePath = prefs.getString("BundlePath", null)
|
|
32
|
-
|
|
33
|
-
|
|
105
|
+
android.util.Log.d("OTAUpdate", "getJSBundleFile called, stored path: $bundlePath")
|
|
106
|
+
if (bundlePath != null) {
|
|
107
|
+
val file = File(bundlePath)
|
|
108
|
+
if (file.exists() && file.canRead()) {
|
|
109
|
+
android.util.Log.d("OTAUpdate", "Loading OTA bundle: $bundlePath (${file.length()} bytes)")
|
|
110
|
+
return bundlePath
|
|
111
|
+
} else {
|
|
112
|
+
android.util.Log.w("OTAUpdate", "OTA bundle not found or not readable: $bundlePath, exists=${file.exists()}")
|
|
113
|
+
}
|
|
34
114
|
}
|
|
115
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
35
116
|
return null
|
|
36
117
|
}
|
|
37
118
|
|
|
38
|
-
|
|
119
|
+
`;
|
|
120
|
+
contents = contents.replace(
|
|
121
|
+
devSupportPattern,
|
|
122
|
+
`${getJSBundleFileOverride}$1`
|
|
39
123
|
);
|
|
124
|
+
console.log('[OTAUpdate] Android: Successfully injected getJSBundleFile (pattern 3)');
|
|
125
|
+
config.modResults.contents = contents;
|
|
126
|
+
return config;
|
|
40
127
|
}
|
|
41
128
|
|
|
129
|
+
// Strategy 4: Look for ReactNativeHost in any form
|
|
130
|
+
const genericPattern = /(ReactNativeHost\s*[\(\{])/;
|
|
131
|
+
|
|
132
|
+
if (genericPattern.test(contents)) {
|
|
133
|
+
// Find the opening brace after ReactNativeHost and insert after it
|
|
134
|
+
const match = contents.match(/ReactNativeHost[^{]*\{/);
|
|
135
|
+
if (match) {
|
|
136
|
+
const insertPos = match.index + match[0].length;
|
|
137
|
+
const getJSBundleFileOverride = `
|
|
138
|
+
override fun getJSBundleFile(): String? {
|
|
139
|
+
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
140
|
+
val bundlePath = prefs.getString("BundlePath", null)
|
|
141
|
+
android.util.Log.d("OTAUpdate", "getJSBundleFile called, stored path: $bundlePath")
|
|
142
|
+
if (bundlePath != null) {
|
|
143
|
+
val file = File(bundlePath)
|
|
144
|
+
if (file.exists() && file.canRead()) {
|
|
145
|
+
android.util.Log.d("OTAUpdate", "Loading OTA bundle: $bundlePath (${file.length()} bytes)")
|
|
146
|
+
return bundlePath
|
|
147
|
+
} else {
|
|
148
|
+
android.util.Log.w("OTAUpdate", "OTA bundle not found or not readable: $bundlePath, exists=${file.exists()}")
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
152
|
+
return null
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
contents = contents.slice(0, insertPos) + getJSBundleFileOverride + contents.slice(insertPos);
|
|
156
|
+
console.log('[OTAUpdate] Android: Successfully injected getJSBundleFile (pattern 4)');
|
|
157
|
+
config.modResults.contents = contents;
|
|
158
|
+
return config;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.warn('[OTAUpdate] Android: Could not find suitable injection point for getJSBundleFile');
|
|
163
|
+
console.warn('[OTAUpdate] Android: Please manually add getJSBundleFile override to MainApplication.kt');
|
|
164
|
+
console.warn('[OTAUpdate] Android: See https://vanikya.github.io/ota-update/ for manual setup instructions');
|
|
165
|
+
|
|
42
166
|
config.modResults.contents = contents;
|
|
43
167
|
return config;
|
|
44
168
|
});
|
|
@@ -50,45 +174,138 @@ function withOTAUpdateIOS(config) {
|
|
|
50
174
|
|
|
51
175
|
// Check if already modified
|
|
52
176
|
if (contents.includes('OTAUpdateBundlePath')) {
|
|
177
|
+
console.log('[OTAUpdate] iOS: OTAUpdateBundlePath already present, skipping');
|
|
53
178
|
return config;
|
|
54
179
|
}
|
|
55
180
|
|
|
56
181
|
// For Swift AppDelegate
|
|
57
182
|
if (config.modResults.language === 'swift') {
|
|
58
|
-
//
|
|
183
|
+
// Helper function to get OTA bundle URL
|
|
59
184
|
const helperFunction = `
|
|
60
185
|
// OTA Update: Check for downloaded bundle
|
|
61
186
|
private func getOTABundleURL() -> URL? {
|
|
62
|
-
let
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
187
|
+
let bundlePath = UserDefaults.standard.string(forKey: "OTAUpdateBundlePath")
|
|
188
|
+
NSLog("[OTAUpdate] getOTABundleURL called, stored path: %@", bundlePath ?? "nil")
|
|
189
|
+
if let path = bundlePath {
|
|
190
|
+
let fileManager = FileManager.default
|
|
191
|
+
if fileManager.fileExists(atPath: path) {
|
|
192
|
+
if let attrs = try? fileManager.attributesOfItem(atPath: path),
|
|
193
|
+
let size = attrs[.size] as? Int64 {
|
|
194
|
+
NSLog("[OTAUpdate] Loading OTA bundle: %@ (%lld bytes)", path, size)
|
|
195
|
+
}
|
|
196
|
+
return URL(fileURLWithPath: path)
|
|
197
|
+
} else {
|
|
198
|
+
NSLog("[OTAUpdate] OTA bundle not found at path: %@", path)
|
|
67
199
|
}
|
|
68
200
|
}
|
|
201
|
+
NSLog("[OTAUpdate] Loading default bundle")
|
|
69
202
|
return nil
|
|
70
203
|
}
|
|
71
204
|
`;
|
|
72
205
|
|
|
73
|
-
//
|
|
74
|
-
|
|
206
|
+
// Strategy 1: Look for bundleURL() with flexible pattern
|
|
207
|
+
// Match: func bundleURL() -> URL? { ... } with any content inside
|
|
208
|
+
const bundleURLPattern1 = /(func\s+bundleURL\s*\(\s*\)\s*->\s*URL\?\s*\{)([\s\S]*?)(\n\s*\})/;
|
|
209
|
+
|
|
210
|
+
if (bundleURLPattern1.test(contents)) {
|
|
211
|
+
contents = contents.replace(
|
|
212
|
+
bundleURLPattern1,
|
|
213
|
+
(match, funcStart, funcBody, funcEnd) => {
|
|
214
|
+
// Check if it already has OTA check
|
|
215
|
+
if (funcBody.includes('getOTABundleURL')) {
|
|
216
|
+
return match;
|
|
217
|
+
}
|
|
218
|
+
return `${funcStart}
|
|
219
|
+
// OTA Update: Check for downloaded bundle first
|
|
220
|
+
if let otaBundle = getOTABundleURL() {
|
|
221
|
+
return otaBundle
|
|
222
|
+
}
|
|
223
|
+
${funcBody}${funcEnd}${helperFunction}`;
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
console.log('[OTAUpdate] iOS: Successfully modified bundleURL (pattern 1)');
|
|
227
|
+
config.modResults.contents = contents;
|
|
228
|
+
return config;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Strategy 2: Look for sourceURL(for bridge:) pattern (older Expo versions)
|
|
232
|
+
const sourceURLPattern = /(func\s+sourceURL\s*\(\s*for\s+bridge\s*:\s*RCTBridge\s*\)\s*->\s*URL\?\s*\{)([\s\S]*?)(\n\s*\})/;
|
|
75
233
|
|
|
76
|
-
if (
|
|
234
|
+
if (sourceURLPattern.test(contents)) {
|
|
77
235
|
contents = contents.replace(
|
|
78
|
-
|
|
79
|
-
|
|
236
|
+
sourceURLPattern,
|
|
237
|
+
(match, funcStart, funcBody, funcEnd) => {
|
|
238
|
+
if (funcBody.includes('getOTABundleURL')) {
|
|
239
|
+
return match;
|
|
240
|
+
}
|
|
241
|
+
return `${funcStart}
|
|
80
242
|
// OTA Update: Check for downloaded bundle first
|
|
81
243
|
if let otaBundle = getOTABundleURL() {
|
|
82
244
|
return otaBundle
|
|
83
245
|
}
|
|
246
|
+
${funcBody}${funcEnd}${helperFunction}`;
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
console.log('[OTAUpdate] iOS: Successfully modified sourceURL (pattern 2)');
|
|
250
|
+
config.modResults.contents = contents;
|
|
251
|
+
return config;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Strategy 3: Add bundleURL method if it doesn't exist but class exists
|
|
255
|
+
const appDelegateClassPattern = /(class\s+AppDelegate\s*[^{]*\{)/;
|
|
256
|
+
|
|
257
|
+
if (appDelegateClassPattern.test(contents) && !contents.includes('func bundleURL')) {
|
|
258
|
+
const newBundleURLMethod = `
|
|
259
|
+
// OTA Update: Bundle URL with OTA support
|
|
260
|
+
func bundleURL() -> URL? {
|
|
261
|
+
// Check for downloaded OTA bundle first
|
|
262
|
+
if let otaBundle = getOTABundleURL() {
|
|
263
|
+
return otaBundle
|
|
264
|
+
}
|
|
84
265
|
#if DEBUG
|
|
85
266
|
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
86
267
|
#else
|
|
87
268
|
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
88
269
|
#endif
|
|
89
270
|
}
|
|
90
|
-
${helperFunction}
|
|
271
|
+
${helperFunction}
|
|
272
|
+
`;
|
|
273
|
+
contents = contents.replace(
|
|
274
|
+
appDelegateClassPattern,
|
|
275
|
+
`$1${newBundleURLMethod}`
|
|
91
276
|
);
|
|
277
|
+
console.log('[OTAUpdate] iOS: Successfully added bundleURL method (pattern 3)');
|
|
278
|
+
config.modResults.contents = contents;
|
|
279
|
+
return config;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
console.warn('[OTAUpdate] iOS: Could not find suitable injection point');
|
|
283
|
+
console.warn('[OTAUpdate] iOS: Please manually modify AppDelegate.swift');
|
|
284
|
+
console.warn('[OTAUpdate] iOS: See https://vanikya.github.io/ota-update/ for manual setup instructions');
|
|
285
|
+
} else if (config.modResults.language === 'objc' || config.modResults.language === 'objcpp') {
|
|
286
|
+
// Objective-C AppDelegate (older Expo versions)
|
|
287
|
+
const sourceURLPattern = /(- \(NSURL \*\)sourceURLForBridge:\(RCTBridge \*\)bridge\s*\{)([\s\S]*?)(\n\})/;
|
|
288
|
+
|
|
289
|
+
if (sourceURLPattern.test(contents)) {
|
|
290
|
+
const helperCode = `
|
|
291
|
+
// Check for OTA bundle
|
|
292
|
+
NSString *bundlePath = [[NSUserDefaults standardUserDefaults] stringForKey:@"OTAUpdateBundlePath"];
|
|
293
|
+
if (bundlePath && [[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
|
|
294
|
+
return [NSURL fileURLWithPath:bundlePath];
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
contents = contents.replace(
|
|
298
|
+
sourceURLPattern,
|
|
299
|
+
(match, funcStart, funcBody, funcEnd) => {
|
|
300
|
+
if (funcBody.includes('OTAUpdateBundlePath')) {
|
|
301
|
+
return match;
|
|
302
|
+
}
|
|
303
|
+
return `${funcStart}${helperCode}${funcBody}${funcEnd}`;
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
console.log('[OTAUpdate] iOS: Successfully modified sourceURLForBridge (Obj-C)');
|
|
307
|
+
config.modResults.contents = contents;
|
|
308
|
+
return config;
|
|
92
309
|
}
|
|
93
310
|
}
|
|
94
311
|
|
|
@@ -102,3 +319,7 @@ module.exports = function withOTAUpdate(config) {
|
|
|
102
319
|
config = withOTAUpdateIOS(config);
|
|
103
320
|
return config;
|
|
104
321
|
};
|
|
322
|
+
|
|
323
|
+
// Export individual functions for testing
|
|
324
|
+
module.exports.withOTAUpdateAndroid = withOTAUpdateAndroid;
|
|
325
|
+
module.exports.withOTAUpdateIOS = withOTAUpdateIOS;
|
package/ios/OTAUpdate.swift
CHANGED
|
@@ -240,6 +240,35 @@ class OTAUpdate: NSObject {
|
|
|
240
240
|
|
|
241
241
|
@objc
|
|
242
242
|
func applyBundle(_ bundlePath: String, restart: Bool, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
243
|
+
// Validate bundle file exists before storing path
|
|
244
|
+
let fileManager = FileManager.default
|
|
245
|
+
|
|
246
|
+
guard fileManager.fileExists(atPath: bundlePath) else {
|
|
247
|
+
rejecter("APPLY_ERROR", "Bundle file does not exist: \(bundlePath)", nil)
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
guard fileManager.isReadableFile(atPath: bundlePath) else {
|
|
252
|
+
rejecter("APPLY_ERROR", "Bundle file is not readable: \(bundlePath)", nil)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Check file size
|
|
257
|
+
do {
|
|
258
|
+
let attributes = try fileManager.attributesOfItem(atPath: bundlePath)
|
|
259
|
+
let fileSize = attributes[.size] as? Int64 ?? 0
|
|
260
|
+
if fileSize < 100 {
|
|
261
|
+
rejecter("APPLY_ERROR", "Bundle file is too small (likely corrupted): \(fileSize) bytes", nil)
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Log for debugging
|
|
266
|
+
NSLog("[OTAUpdate] Applying bundle: %@ (%lld bytes)", bundlePath, fileSize)
|
|
267
|
+
} catch {
|
|
268
|
+
rejecter("APPLY_ERROR", "Failed to get bundle file attributes: \(error.localizedDescription)", error)
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
243
272
|
// Store the bundle path for next launch
|
|
244
273
|
UserDefaults.standard.set(bundlePath, forKey: "OTAUpdateBundlePath")
|
|
245
274
|
UserDefaults.standard.synchronize()
|
|
@@ -10,6 +10,7 @@ exports.withOTA = withOTA;
|
|
|
10
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
var _reactNative = require("react-native");
|
|
12
12
|
var _useOTAUpdate = require("./hooks/useOTAUpdate");
|
|
13
|
+
var _storage = require("./utils/storage");
|
|
13
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
15
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
15
16
|
// Context type
|
|
@@ -30,6 +31,42 @@ function OTAProvider({
|
|
|
30
31
|
}) {
|
|
31
32
|
const ota = (0, _useOTAUpdate.useOTAUpdate)(config);
|
|
32
33
|
const [handledMandatory, setHandledMandatory] = (0, _react.useState)(false);
|
|
34
|
+
const initRef = (0, _react.useRef)(false);
|
|
35
|
+
|
|
36
|
+
// Startup initialization - check for and clear corrupted bundles
|
|
37
|
+
(0, _react.useEffect)(() => {
|
|
38
|
+
if (initRef.current) return;
|
|
39
|
+
initRef.current = true;
|
|
40
|
+
const initializeOTA = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const storage = new _storage.UpdateStorage();
|
|
43
|
+
|
|
44
|
+
// Check for corrupted bundle and clear it
|
|
45
|
+
const wasCorrupted = await storage.clearCorruptedBundle();
|
|
46
|
+
if (wasCorrupted && __DEV__) {
|
|
47
|
+
console.log('[OTAUpdate] Cleared corrupted bundle on startup');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Also validate that if there's a pending bundle path, the file actually exists
|
|
51
|
+
const metadata = await storage.getMetadata();
|
|
52
|
+
if (metadata && metadata.bundlePath) {
|
|
53
|
+
const bundleExists = await storage.validateBundle(metadata.releaseId);
|
|
54
|
+
if (!bundleExists) {
|
|
55
|
+
if (__DEV__) {
|
|
56
|
+
console.log('[OTAUpdate] Bundle file missing or invalid, clearing metadata');
|
|
57
|
+
}
|
|
58
|
+
await storage.clearMetadata();
|
|
59
|
+
await storage.clearNativePendingBundle();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (__DEV__) {
|
|
64
|
+
console.error('[OTAUpdate] Startup initialization error:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
initializeOTA();
|
|
69
|
+
}, []);
|
|
33
70
|
|
|
34
71
|
// Handle callbacks
|
|
35
72
|
(0, _react.useEffect)(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useOTAUpdate","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","OTAContext","createContext","OTAProvider","children","config","onUpdateAvailable","onUpdateDownloaded","onError","showMandatoryUpdateAlert","mandatoryUpdateAlertTitle","mandatoryUpdateAlertMessage","ota","useOTAUpdate","handledMandatory","setHandledMandatory","useState","useEffect","status","updateInfo","isMandatory","Alert","alert","text","onPress","downloadUpdate","applyUpdate","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_useOTAUpdate","_storage","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","_extends","assign","bind","arguments","length","apply","OTAContext","createContext","OTAProvider","children","config","onUpdateAvailable","onUpdateDownloaded","onError","showMandatoryUpdateAlert","mandatoryUpdateAlertTitle","mandatoryUpdateAlertMessage","ota","useOTAUpdate","handledMandatory","setHandledMandatory","useState","initRef","useRef","useEffect","current","initializeOTA","storage","UpdateStorage","wasCorrupted","clearCorruptedBundle","__DEV__","console","log","metadata","getMetadata","bundlePath","bundleExists","validateBundle","releaseId","clearMetadata","clearNativePendingBundle","error","status","updateInfo","isMandatory","Alert","alert","text","onPress","downloadUpdate","applyUpdate","contextValue","createElement","Provider","value","useOTA","context","useContext","Error","withOTA","Component","WithOTA","props","UpdateBanner","renderAvailable","renderDownloading","renderReady","Fragment","downloadProgress","percentage"],"sourceRoot":"../../src","sources":["OTAProvider.tsx"],"mappings":"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","ignoreList":[]}
|