@vanikya/ota-react-native 0.2.0 → 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/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/otaupdate/OTAUpdateModule.kt +116 -25
- package/app.plugin.js +53 -12
- 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
|
@@ -2,6 +2,8 @@ package com.otaupdate
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.content.SharedPreferences
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
import android.os.Looper
|
|
5
7
|
import android.util.Base64
|
|
6
8
|
import com.facebook.react.bridge.*
|
|
7
9
|
import java.io.File
|
|
@@ -10,6 +12,7 @@ import java.io.InputStream
|
|
|
10
12
|
import java.net.HttpURLConnection
|
|
11
13
|
import java.net.URL
|
|
12
14
|
import java.security.MessageDigest
|
|
15
|
+
import java.util.concurrent.Executors
|
|
13
16
|
|
|
14
17
|
class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
15
18
|
|
|
@@ -17,6 +20,12 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
17
20
|
reactContext.getSharedPreferences("OTAUpdate", Context.MODE_PRIVATE)
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
// Use a thread pool for background operations instead of raw threads
|
|
24
|
+
private val executor = Executors.newFixedThreadPool(2)
|
|
25
|
+
|
|
26
|
+
// Handler to post results back to the main thread
|
|
27
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
28
|
+
|
|
20
29
|
override fun getName(): String = "OTAUpdate"
|
|
21
30
|
|
|
22
31
|
// File System Operations
|
|
@@ -100,25 +109,33 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
100
109
|
// This is critical for large bundles (5MB+)
|
|
101
110
|
@ReactMethod
|
|
102
111
|
fun downloadFile(urlString: String, destPath: String, promise: Promise) {
|
|
103
|
-
|
|
112
|
+
executor.execute {
|
|
104
113
|
var connection: HttpURLConnection? = null
|
|
105
114
|
var inputStream: InputStream? = null
|
|
106
115
|
var outputStream: FileOutputStream? = null
|
|
107
116
|
|
|
108
117
|
try {
|
|
118
|
+
android.util.Log.d("OTAUpdate", "Starting download from: $urlString to: $destPath")
|
|
119
|
+
|
|
109
120
|
val url = URL(urlString)
|
|
110
121
|
connection = url.openConnection() as HttpURLConnection
|
|
111
122
|
connection.connectTimeout = 30000
|
|
112
|
-
connection.readTimeout =
|
|
123
|
+
connection.readTimeout = 120000 // Increased read timeout for large files
|
|
113
124
|
connection.requestMethod = "GET"
|
|
125
|
+
connection.setRequestProperty("Accept-Encoding", "identity") // Disable compression for reliable streaming
|
|
114
126
|
connection.connect()
|
|
115
127
|
|
|
116
128
|
val responseCode = connection.responseCode
|
|
117
129
|
if (responseCode != HttpURLConnection.HTTP_OK) {
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
val errorMsg = "Download failed with status $responseCode"
|
|
131
|
+
android.util.Log.e("OTAUpdate", errorMsg)
|
|
132
|
+
mainHandler.post { promise.reject("DOWNLOAD_ERROR", errorMsg) }
|
|
133
|
+
return@execute
|
|
120
134
|
}
|
|
121
135
|
|
|
136
|
+
val contentLength = connection.contentLengthLong
|
|
137
|
+
android.util.Log.d("OTAUpdate", "Content-Length: $contentLength bytes")
|
|
138
|
+
|
|
122
139
|
// Ensure parent directory exists
|
|
123
140
|
val destFile = File(destPath)
|
|
124
141
|
destFile.parentFile?.mkdirs()
|
|
@@ -137,22 +154,36 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
137
154
|
|
|
138
155
|
outputStream.flush()
|
|
139
156
|
|
|
157
|
+
// Verify written file size
|
|
158
|
+
val writtenSize = destFile.length()
|
|
159
|
+
android.util.Log.d("OTAUpdate", "Download complete: $totalBytesRead bytes read, $writtenSize bytes written")
|
|
160
|
+
|
|
161
|
+
if (contentLength > 0 && writtenSize != contentLength) {
|
|
162
|
+
val errorMsg = "File size mismatch: expected $contentLength, got $writtenSize"
|
|
163
|
+
android.util.Log.e("OTAUpdate", errorMsg)
|
|
164
|
+
destFile.delete()
|
|
165
|
+
mainHandler.post { promise.reject("DOWNLOAD_ERROR", errorMsg) }
|
|
166
|
+
return@execute
|
|
167
|
+
}
|
|
168
|
+
|
|
140
169
|
val result = Arguments.createMap()
|
|
141
170
|
result.putDouble("fileSize", totalBytesRead.toDouble())
|
|
142
|
-
promise
|
|
171
|
+
// Resolve promise on main thread to avoid React Native bridge issues
|
|
172
|
+
mainHandler.post { promise.resolve(result) }
|
|
143
173
|
|
|
144
174
|
} catch (e: Exception) {
|
|
145
|
-
|
|
175
|
+
android.util.Log.e("OTAUpdate", "Download failed: ${e.message}", e)
|
|
176
|
+
mainHandler.post { promise.reject("DOWNLOAD_ERROR", "Failed to download file: ${e.message}", e) }
|
|
146
177
|
} finally {
|
|
147
178
|
try {
|
|
148
179
|
inputStream?.close()
|
|
149
180
|
outputStream?.close()
|
|
150
181
|
connection?.disconnect()
|
|
151
182
|
} catch (e: Exception) {
|
|
152
|
-
|
|
183
|
+
android.util.Log.w("OTAUpdate", "Error during cleanup: ${e.message}")
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
|
-
}
|
|
186
|
+
}
|
|
156
187
|
}
|
|
157
188
|
|
|
158
189
|
// Cryptography
|
|
@@ -174,14 +205,16 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
174
205
|
// Critical for large bundles (5MB+)
|
|
175
206
|
@ReactMethod
|
|
176
207
|
fun calculateSHA256FromFile(filePath: String, promise: Promise) {
|
|
177
|
-
|
|
208
|
+
executor.execute {
|
|
178
209
|
try {
|
|
179
210
|
val file = File(filePath)
|
|
180
211
|
if (!file.exists()) {
|
|
181
|
-
promise.reject("FILE_ERROR", "File not found: $filePath")
|
|
182
|
-
return@
|
|
212
|
+
mainHandler.post { promise.reject("FILE_ERROR", "File not found: $filePath") }
|
|
213
|
+
return@execute
|
|
183
214
|
}
|
|
184
215
|
|
|
216
|
+
android.util.Log.d("OTAUpdate", "Calculating hash for: $filePath (${file.length()} bytes)")
|
|
217
|
+
|
|
185
218
|
val digest = MessageDigest.getInstance("SHA-256")
|
|
186
219
|
val buffer = ByteArray(8192) // 8KB buffer
|
|
187
220
|
var bytesRead: Int
|
|
@@ -194,11 +227,14 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
194
227
|
|
|
195
228
|
val hash = digest.digest()
|
|
196
229
|
val hexString = hash.joinToString("") { "%02x".format(it) }
|
|
197
|
-
|
|
230
|
+
android.util.Log.d("OTAUpdate", "Hash calculated: $hexString")
|
|
231
|
+
// Resolve promise on main thread
|
|
232
|
+
mainHandler.post { promise.resolve(hexString) }
|
|
198
233
|
} catch (e: Exception) {
|
|
199
|
-
|
|
234
|
+
android.util.Log.e("OTAUpdate", "Hash calculation failed: ${e.message}", e)
|
|
235
|
+
mainHandler.post { promise.reject("HASH_ERROR", "Failed to calculate hash: ${e.message}", e) }
|
|
200
236
|
}
|
|
201
|
-
}
|
|
237
|
+
}
|
|
202
238
|
}
|
|
203
239
|
|
|
204
240
|
@ReactMethod
|
|
@@ -229,21 +265,69 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
229
265
|
@ReactMethod
|
|
230
266
|
fun applyBundle(bundlePath: String, restart: Boolean, promise: Promise) {
|
|
231
267
|
try {
|
|
268
|
+
// Validate bundle file exists before storing path
|
|
269
|
+
val bundleFile = File(bundlePath)
|
|
270
|
+
if (!bundleFile.exists()) {
|
|
271
|
+
promise.reject("APPLY_ERROR", "Bundle file does not exist: $bundlePath")
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
if (!bundleFile.canRead()) {
|
|
275
|
+
promise.reject("APPLY_ERROR", "Bundle file is not readable: $bundlePath")
|
|
276
|
+
return
|
|
277
|
+
}
|
|
278
|
+
if (bundleFile.length() < 100) {
|
|
279
|
+
promise.reject("APPLY_ERROR", "Bundle file is too small (likely corrupted): ${bundleFile.length()} bytes")
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Log for debugging
|
|
284
|
+
android.util.Log.d("OTAUpdate", "Applying bundle: $bundlePath (${bundleFile.length()} bytes)")
|
|
285
|
+
|
|
232
286
|
// Store the bundle path for next launch
|
|
233
|
-
|
|
287
|
+
// CRITICAL: Use commit() instead of apply() to ensure synchronous write
|
|
288
|
+
// This prevents a race condition where the app kills before the write completes
|
|
289
|
+
val success = prefs.edit().putString("BundlePath", bundlePath).commit()
|
|
290
|
+
if (!success) {
|
|
291
|
+
android.util.Log.e("OTAUpdate", "Failed to save bundle path to SharedPreferences")
|
|
292
|
+
promise.reject("APPLY_ERROR", "Failed to save bundle path")
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
android.util.Log.d("OTAUpdate", "Bundle path saved to SharedPreferences: $bundlePath")
|
|
296
|
+
|
|
297
|
+
// Verify the path was actually saved
|
|
298
|
+
val savedPath = prefs.getString("BundlePath", null)
|
|
299
|
+
if (savedPath != bundlePath) {
|
|
300
|
+
android.util.Log.e("OTAUpdate", "Bundle path verification failed: expected $bundlePath, got $savedPath")
|
|
301
|
+
promise.reject("APPLY_ERROR", "Bundle path verification failed")
|
|
302
|
+
return
|
|
303
|
+
}
|
|
234
304
|
|
|
235
305
|
if (restart) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
306
|
+
android.util.Log.d("OTAUpdate", "Restarting app to apply bundle...")
|
|
307
|
+
|
|
308
|
+
// Resolve promise before restarting so JS knows it succeeded
|
|
309
|
+
promise.resolve(null)
|
|
310
|
+
|
|
311
|
+
// Give a small delay to ensure the promise is sent back to JS
|
|
312
|
+
mainHandler.postDelayed({
|
|
313
|
+
// Restart the app
|
|
314
|
+
val context = reactApplicationContext
|
|
315
|
+
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
316
|
+
intent?.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
317
|
+
intent?.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
318
|
+
intent?.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
319
|
+
context.startActivity(intent)
|
|
320
|
+
|
|
321
|
+
// Small delay before killing to allow activity to start
|
|
322
|
+
mainHandler.postDelayed({
|
|
323
|
+
android.os.Process.killProcess(android.os.Process.myPid())
|
|
324
|
+
}, 100)
|
|
325
|
+
}, 100)
|
|
326
|
+
} else {
|
|
327
|
+
promise.resolve(null)
|
|
243
328
|
}
|
|
244
|
-
|
|
245
|
-
promise.resolve(null)
|
|
246
329
|
} catch (e: Exception) {
|
|
330
|
+
android.util.Log.e("OTAUpdate", "Failed to apply bundle: ${e.message}", e)
|
|
247
331
|
promise.reject("APPLY_ERROR", "Failed to apply bundle: ${e.message}", e)
|
|
248
332
|
}
|
|
249
333
|
}
|
|
@@ -256,7 +340,8 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
256
340
|
|
|
257
341
|
@ReactMethod
|
|
258
342
|
fun clearPendingBundle(promise: Promise) {
|
|
259
|
-
prefs.edit().remove("BundlePath").
|
|
343
|
+
prefs.edit().remove("BundlePath").commit()
|
|
344
|
+
android.util.Log.d("OTAUpdate", "Pending bundle cleared")
|
|
260
345
|
promise.resolve(null)
|
|
261
346
|
}
|
|
262
347
|
|
|
@@ -273,6 +358,12 @@ class OTAUpdateModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
|
|
|
273
358
|
return data
|
|
274
359
|
}
|
|
275
360
|
|
|
361
|
+
// Cleanup executor when module is destroyed
|
|
362
|
+
override fun onCatalystInstanceDestroy() {
|
|
363
|
+
super.onCatalystInstanceDestroy()
|
|
364
|
+
executor.shutdown()
|
|
365
|
+
}
|
|
366
|
+
|
|
276
367
|
companion object {
|
|
277
368
|
const val NAME = "OTAUpdate"
|
|
278
369
|
}
|
package/app.plugin.js
CHANGED
|
@@ -41,9 +41,17 @@ function withOTAUpdateAndroid(config) {
|
|
|
41
41
|
override fun getJSBundleFile(): String? {
|
|
42
42
|
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
43
43
|
val bundlePath = prefs.getString("BundlePath", null)
|
|
44
|
-
|
|
45
|
-
|
|
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
|
+
}
|
|
46
53
|
}
|
|
54
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
47
55
|
return null
|
|
48
56
|
}
|
|
49
57
|
`;
|
|
@@ -64,9 +72,17 @@ function withOTAUpdateAndroid(config) {
|
|
|
64
72
|
override fun getJSBundleFile(): String? {
|
|
65
73
|
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
66
74
|
val bundlePath = prefs.getString("BundlePath", null)
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
}
|
|
69
84
|
}
|
|
85
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
70
86
|
return null
|
|
71
87
|
}
|
|
72
88
|
`;
|
|
@@ -86,9 +102,17 @@ function withOTAUpdateAndroid(config) {
|
|
|
86
102
|
const getJSBundleFileOverride = `override fun getJSBundleFile(): String? {
|
|
87
103
|
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
88
104
|
val bundlePath = prefs.getString("BundlePath", null)
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}
|
|
91
114
|
}
|
|
115
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
92
116
|
return null
|
|
93
117
|
}
|
|
94
118
|
|
|
@@ -114,9 +138,17 @@ function withOTAUpdateAndroid(config) {
|
|
|
114
138
|
override fun getJSBundleFile(): String? {
|
|
115
139
|
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
116
140
|
val bundlePath = prefs.getString("BundlePath", null)
|
|
117
|
-
|
|
118
|
-
|
|
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
|
+
}
|
|
119
150
|
}
|
|
151
|
+
android.util.Log.d("OTAUpdate", "Loading default bundle")
|
|
120
152
|
return null
|
|
121
153
|
}
|
|
122
154
|
`;
|
|
@@ -152,12 +184,21 @@ function withOTAUpdateIOS(config) {
|
|
|
152
184
|
const helperFunction = `
|
|
153
185
|
// OTA Update: Check for downloaded bundle
|
|
154
186
|
private func getOTABundleURL() -> URL? {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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)
|
|
159
199
|
}
|
|
160
200
|
}
|
|
201
|
+
NSLog("[OTAUpdate] Loading default bundle")
|
|
161
202
|
return nil
|
|
162
203
|
}
|
|
163
204
|
`;
|
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":[]}
|