@hot-updater/react-native 0.23.1 → 0.24.0
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/java/com/hotupdater/BundleFileStorageService.kt +393 -49
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +204 -0
- package/android/src/main/java/com/hotupdater/HotUpdater.kt +48 -36
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +134 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +168 -95
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +15 -3
- package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +17 -12
- package/android/src/newarch/HotUpdaterModule.kt +88 -23
- package/android/src/oldarch/HotUpdaterModule.kt +89 -22
- package/android/src/oldarch/HotUpdaterSpec.kt +6 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +401 -77
- package/ios/HotUpdater/Internal/BundleMetadata.swift +177 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +213 -47
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +96 -25
- package/ios/HotUpdater/Internal/SignatureVerifier.swift +35 -29
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +2 -2
- package/ios/HotUpdater/Public/HotUpdater.h +8 -2
- package/lib/commonjs/checkForUpdate.js +31 -28
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/error.js +45 -1
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchUpdateInfo.js +7 -45
- package/lib/commonjs/fetchUpdateInfo.js.map +1 -1
- package/lib/commonjs/index.js +237 -208
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +103 -3
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/wrap.js +39 -1
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/checkForUpdate.js +32 -26
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/error.js +45 -0
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchUpdateInfo.js +7 -45
- package/lib/module/fetchUpdateInfo.js.map +1 -1
- package/lib/module/index.js +238 -203
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +87 -2
- package/lib/module/native.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/wrap.js +40 -2
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +11 -13
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +120 -0
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +3 -5
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +35 -41
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +58 -2
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +62 -0
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +76 -5
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +11 -13
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +120 -0
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchUpdateInfo.d.ts +3 -5
- package/lib/typescript/module/fetchUpdateInfo.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +35 -41
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +58 -2
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +62 -0
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +76 -5
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/package.json +7 -7
- package/plugin/build/withHotUpdater.js +3 -3
- package/src/checkForUpdate.ts +51 -40
- package/src/error.ts +153 -0
- package/src/fetchUpdateInfo.ts +10 -58
- package/src/index.ts +283 -206
- package/src/native.ts +88 -2
- package/src/specs/NativeHotUpdater.ts +63 -0
- package/src/wrap.tsx +131 -9
- package/android/src/main/java/com/hotupdater/HotUpdaterFactory.kt +0 -52
- package/ios/HotUpdater/Internal/HotUpdaterFactory.swift +0 -24
- package/lib/commonjs/runUpdateProcess.js +0 -69
- package/lib/commonjs/runUpdateProcess.js.map +0 -1
- package/lib/module/runUpdateProcess.js +0 -64
- package/lib/module/runUpdateProcess.js.map +0 -1
- package/lib/typescript/commonjs/runUpdateProcess.d.ts +0 -49
- package/lib/typescript/commonjs/runUpdateProcess.d.ts.map +0 -1
- package/lib/typescript/module/runUpdateProcess.d.ts +0 -49
- package/lib/typescript/module/runUpdateProcess.d.ts.map +0 -1
- package/src/runUpdateProcess.ts +0 -80
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import org.json.JSONArray
|
|
5
|
+
import org.json.JSONObject
|
|
6
|
+
import java.io.File
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Bundle metadata for managing stable/staging bundles and verification state
|
|
10
|
+
*/
|
|
11
|
+
data class BundleMetadata(
|
|
12
|
+
val schema: String = SCHEMA_VERSION,
|
|
13
|
+
val stableBundleId: String? = null,
|
|
14
|
+
val stagingBundleId: String? = null,
|
|
15
|
+
val verificationPending: Boolean = false,
|
|
16
|
+
val verificationAttemptedAt: Long? = null,
|
|
17
|
+
val stagingExecutionCount: Int? = null,
|
|
18
|
+
val updatedAt: Long = System.currentTimeMillis(),
|
|
19
|
+
) {
|
|
20
|
+
companion object {
|
|
21
|
+
private const val TAG = "BundleMetadata"
|
|
22
|
+
const val SCHEMA_VERSION = "metadata-v1"
|
|
23
|
+
const val METADATA_FILENAME = "metadata.json"
|
|
24
|
+
|
|
25
|
+
fun fromJson(json: JSONObject): BundleMetadata =
|
|
26
|
+
BundleMetadata(
|
|
27
|
+
schema = json.optString("schema", SCHEMA_VERSION),
|
|
28
|
+
stableBundleId = json.optString("stableBundleId", null)?.takeIf { it.isNotEmpty() },
|
|
29
|
+
stagingBundleId = json.optString("stagingBundleId", null)?.takeIf { it.isNotEmpty() },
|
|
30
|
+
verificationPending = json.optBoolean("verificationPending", false),
|
|
31
|
+
verificationAttemptedAt =
|
|
32
|
+
if (json.has("verificationAttemptedAt") && !json.isNull("verificationAttemptedAt")) {
|
|
33
|
+
json.getLong("verificationAttemptedAt")
|
|
34
|
+
} else {
|
|
35
|
+
null
|
|
36
|
+
},
|
|
37
|
+
stagingExecutionCount =
|
|
38
|
+
if (json.has("stagingExecutionCount") && !json.isNull("stagingExecutionCount")) {
|
|
39
|
+
json.getInt("stagingExecutionCount")
|
|
40
|
+
} else {
|
|
41
|
+
null
|
|
42
|
+
},
|
|
43
|
+
updatedAt = json.optLong("updatedAt", System.currentTimeMillis()),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
fun loadFromFile(file: File): BundleMetadata? {
|
|
47
|
+
return try {
|
|
48
|
+
if (!file.exists()) {
|
|
49
|
+
Log.d(TAG, "Metadata file does not exist: ${file.absolutePath}")
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
val jsonString = file.readText()
|
|
53
|
+
val json = JSONObject(jsonString)
|
|
54
|
+
fromJson(json)
|
|
55
|
+
} catch (e: Exception) {
|
|
56
|
+
Log.e(TAG, "Failed to load metadata from file", e)
|
|
57
|
+
null
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun toJson(): JSONObject =
|
|
63
|
+
JSONObject().apply {
|
|
64
|
+
put("schema", schema)
|
|
65
|
+
put("stableBundleId", stableBundleId ?: JSONObject.NULL)
|
|
66
|
+
put("stagingBundleId", stagingBundleId ?: JSONObject.NULL)
|
|
67
|
+
put("verificationPending", verificationPending)
|
|
68
|
+
put("verificationAttemptedAt", verificationAttemptedAt ?: JSONObject.NULL)
|
|
69
|
+
put("stagingExecutionCount", stagingExecutionCount ?: JSONObject.NULL)
|
|
70
|
+
put("updatedAt", updatedAt)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fun saveToFile(file: File): Boolean =
|
|
74
|
+
try {
|
|
75
|
+
file.parentFile?.mkdirs()
|
|
76
|
+
file.writeText(toJson().toString(2))
|
|
77
|
+
Log.d(TAG, "Saved metadata to file: ${file.absolutePath}")
|
|
78
|
+
true
|
|
79
|
+
} catch (e: Exception) {
|
|
80
|
+
Log.e(TAG, "Failed to save metadata to file", e)
|
|
81
|
+
false
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Entry for a crashed bundle in history
|
|
87
|
+
*/
|
|
88
|
+
data class CrashedBundleEntry(
|
|
89
|
+
val bundleId: String,
|
|
90
|
+
val crashedAt: Long,
|
|
91
|
+
val crashCount: Int = 1,
|
|
92
|
+
) {
|
|
93
|
+
companion object {
|
|
94
|
+
fun fromJson(json: JSONObject): CrashedBundleEntry =
|
|
95
|
+
CrashedBundleEntry(
|
|
96
|
+
bundleId = json.getString("bundleId"),
|
|
97
|
+
crashedAt = json.getLong("crashedAt"),
|
|
98
|
+
crashCount = json.optInt("crashCount", 1),
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fun toJson(): JSONObject =
|
|
103
|
+
JSONObject().apply {
|
|
104
|
+
put("bundleId", bundleId)
|
|
105
|
+
put("crashedAt", crashedAt)
|
|
106
|
+
put("crashCount", crashCount)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* History of crashed bundles
|
|
112
|
+
*/
|
|
113
|
+
data class CrashedHistory(
|
|
114
|
+
val bundles: MutableList<CrashedBundleEntry> = mutableListOf(),
|
|
115
|
+
val maxHistorySize: Int = DEFAULT_MAX_HISTORY_SIZE,
|
|
116
|
+
) {
|
|
117
|
+
companion object {
|
|
118
|
+
private const val TAG = "CrashedHistory"
|
|
119
|
+
const val DEFAULT_MAX_HISTORY_SIZE = 10
|
|
120
|
+
const val CRASHED_HISTORY_FILENAME = "crashed-history.json"
|
|
121
|
+
|
|
122
|
+
fun fromJson(json: JSONObject): CrashedHistory {
|
|
123
|
+
val bundlesArray = json.optJSONArray("bundles") ?: JSONArray()
|
|
124
|
+
val bundles = mutableListOf<CrashedBundleEntry>()
|
|
125
|
+
for (i in 0 until bundlesArray.length()) {
|
|
126
|
+
bundles.add(CrashedBundleEntry.fromJson(bundlesArray.getJSONObject(i)))
|
|
127
|
+
}
|
|
128
|
+
return CrashedHistory(
|
|
129
|
+
bundles = bundles,
|
|
130
|
+
maxHistorySize = json.optInt("maxHistorySize", DEFAULT_MAX_HISTORY_SIZE),
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fun loadFromFile(file: File): CrashedHistory {
|
|
135
|
+
return try {
|
|
136
|
+
if (!file.exists()) {
|
|
137
|
+
Log.d(TAG, "Crashed history file does not exist, returning empty history")
|
|
138
|
+
return CrashedHistory()
|
|
139
|
+
}
|
|
140
|
+
val jsonString = file.readText()
|
|
141
|
+
val json = JSONObject(jsonString)
|
|
142
|
+
fromJson(json)
|
|
143
|
+
} catch (e: Exception) {
|
|
144
|
+
Log.e(TAG, "Failed to load crashed history from file", e)
|
|
145
|
+
CrashedHistory()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fun toJson(): JSONObject =
|
|
151
|
+
JSONObject().apply {
|
|
152
|
+
val bundlesArray = JSONArray()
|
|
153
|
+
bundles.forEach { bundlesArray.put(it.toJson()) }
|
|
154
|
+
put("bundles", bundlesArray)
|
|
155
|
+
put("maxHistorySize", maxHistorySize)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
fun saveToFile(file: File): Boolean =
|
|
159
|
+
try {
|
|
160
|
+
file.parentFile?.mkdirs()
|
|
161
|
+
file.writeText(toJson().toString(2))
|
|
162
|
+
Log.d(TAG, "Saved crashed history to file: ${file.absolutePath}")
|
|
163
|
+
true
|
|
164
|
+
} catch (e: Exception) {
|
|
165
|
+
Log.e(TAG, "Failed to save crashed history to file", e)
|
|
166
|
+
false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fun contains(bundleId: String): Boolean = bundles.any { it.bundleId == bundleId }
|
|
170
|
+
|
|
171
|
+
fun addEntry(bundleId: String) {
|
|
172
|
+
val existingIndex = bundles.indexOfFirst { it.bundleId == bundleId }
|
|
173
|
+
if (existingIndex >= 0) {
|
|
174
|
+
// Update existing entry
|
|
175
|
+
val existing = bundles[existingIndex]
|
|
176
|
+
bundles[existingIndex] =
|
|
177
|
+
existing.copy(
|
|
178
|
+
crashedAt = System.currentTimeMillis(),
|
|
179
|
+
crashCount = existing.crashCount + 1,
|
|
180
|
+
)
|
|
181
|
+
} else {
|
|
182
|
+
// Add new entry
|
|
183
|
+
bundles.add(
|
|
184
|
+
CrashedBundleEntry(
|
|
185
|
+
bundleId = bundleId,
|
|
186
|
+
crashedAt = System.currentTimeMillis(),
|
|
187
|
+
crashCount = 1,
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Trim to max size (keep most recent)
|
|
193
|
+
if (bundles.size > maxHistorySize) {
|
|
194
|
+
bundles.sortBy { it.crashedAt }
|
|
195
|
+
while (bundles.size > maxHistorySize) {
|
|
196
|
+
bundles.removeAt(0)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
fun clear() {
|
|
202
|
+
bundles.clear()
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -6,52 +6,41 @@ import com.facebook.react.bridge.ReactApplicationContext
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Main React Native package for HotUpdater
|
|
9
|
+
* Provides static utility methods and a default singleton instance
|
|
9
10
|
*/
|
|
10
11
|
class HotUpdater {
|
|
11
12
|
companion object {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @param context Application context
|
|
15
|
-
* @return App version name or null if not available
|
|
16
|
-
*/
|
|
17
|
-
fun getAppVersion(context: Context): String? = HotUpdaterFactory.getInstance(context).getAppVersion()
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Generates a bundle ID based on build timestamp
|
|
21
|
-
* @param context Application context
|
|
22
|
-
* @return The minimum bundle ID string
|
|
23
|
-
*/
|
|
24
|
-
fun getMinBundleId(context: Context): String = HotUpdaterFactory.getInstance(context).getMinBundleId()
|
|
13
|
+
@Volatile
|
|
14
|
+
private var instance: HotUpdaterImpl? = null
|
|
25
15
|
|
|
26
16
|
/**
|
|
27
|
-
* Gets the
|
|
17
|
+
* Gets or creates the singleton instance
|
|
18
|
+
* Thread-safe double-checked locking
|
|
28
19
|
* @param context Application context
|
|
29
|
-
* @return The
|
|
20
|
+
* @return The singleton HotUpdaterImpl instance
|
|
30
21
|
*/
|
|
31
|
-
fun
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*/
|
|
38
|
-
fun getChannel(context: Context): String? = HotUpdaterFactory.getInstance(context).getChannel()
|
|
22
|
+
fun getInstance(context: Context): HotUpdaterImpl =
|
|
23
|
+
instance ?: synchronized(this) {
|
|
24
|
+
instance ?: HotUpdaterImpl(context.applicationContext).also {
|
|
25
|
+
instance = it
|
|
26
|
+
}
|
|
27
|
+
}
|
|
39
28
|
|
|
40
29
|
/**
|
|
41
|
-
* Gets the path
|
|
30
|
+
* Gets the JS bundle file path using the default singleton instance
|
|
42
31
|
* @param context Application context
|
|
43
32
|
* @return The path to the bundle file
|
|
44
33
|
*/
|
|
45
|
-
fun getJSBundleFile(context: Context): String =
|
|
34
|
+
fun getJSBundleFile(context: Context): String = getInstance(context).getJSBundleFile()
|
|
46
35
|
|
|
47
36
|
/**
|
|
48
|
-
* Updates the bundle
|
|
37
|
+
* Updates the bundle using the default singleton instance
|
|
49
38
|
* @param context Application context
|
|
50
39
|
* @param bundleId ID of the bundle to update
|
|
51
40
|
* @param fileUrl URL of the bundle file to download (or null to reset)
|
|
52
41
|
* @param fileHash Combined hash string for verification (sig:<signature> or <hex_hash>)
|
|
53
42
|
* @param progressCallback Callback for download progress updates
|
|
54
|
-
* @
|
|
43
|
+
* @throws HotUpdaterException if the update fails
|
|
55
44
|
*/
|
|
56
45
|
suspend fun updateBundle(
|
|
57
46
|
context: Context,
|
|
@@ -59,23 +48,46 @@ class HotUpdater {
|
|
|
59
48
|
fileUrl: String?,
|
|
60
49
|
fileHash: String?,
|
|
61
50
|
progressCallback: (Double) -> Unit,
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fileUrl,
|
|
66
|
-
fileHash,
|
|
67
|
-
progressCallback,
|
|
68
|
-
)
|
|
51
|
+
) {
|
|
52
|
+
getInstance(context).updateBundle(bundleId, fileUrl, fileHash, progressCallback)
|
|
53
|
+
}
|
|
69
54
|
|
|
70
55
|
/**
|
|
71
|
-
* Reloads the React Native application
|
|
56
|
+
* Reloads the React Native application using the default singleton instance
|
|
72
57
|
* @param context Application context
|
|
73
58
|
*/
|
|
74
59
|
suspend fun reload(context: Context) {
|
|
75
60
|
val currentActivity = getCurrentActivity(context)
|
|
76
|
-
|
|
61
|
+
getInstance(context).reload(currentActivity)
|
|
77
62
|
}
|
|
78
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Gets the app version - delegates to HotUpdaterImpl static method
|
|
66
|
+
* @param context Application context
|
|
67
|
+
* @return App version name or null if not available
|
|
68
|
+
*/
|
|
69
|
+
fun getAppVersion(context: Context): String? = HotUpdaterImpl.getAppVersion(context)
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets the minimum bundle ID - delegates to HotUpdaterImpl static method
|
|
73
|
+
* @return The minimum bundle ID string
|
|
74
|
+
*/
|
|
75
|
+
fun getMinBundleId(): String = HotUpdaterImpl.getMinBundleId()
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets the current fingerprint hash - delegates to HotUpdaterImpl static method
|
|
79
|
+
* @param context Application context
|
|
80
|
+
* @return The fingerprint hash or null if not set
|
|
81
|
+
*/
|
|
82
|
+
fun getFingerprintHash(context: Context): String? = HotUpdaterImpl.getFingerprintHash(context)
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets the current update channel - delegates to HotUpdaterImpl static method
|
|
86
|
+
* @param context Application context
|
|
87
|
+
* @return The channel name or null if not set
|
|
88
|
+
*/
|
|
89
|
+
fun getChannel(context: Context): String = HotUpdaterImpl.getChannel(context)
|
|
90
|
+
|
|
79
91
|
/**
|
|
80
92
|
* Gets the current activity from ReactApplicationContext
|
|
81
93
|
* @param context Context that might be a ReactApplicationContext
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Exception class for Hot Updater errors
|
|
5
|
+
* Matches error codes defined in packages/react-native/src/errors.ts
|
|
6
|
+
*/
|
|
7
|
+
class HotUpdaterException(
|
|
8
|
+
val code: String,
|
|
9
|
+
message: String,
|
|
10
|
+
cause: Throwable? = null,
|
|
11
|
+
) : Exception(message, cause) {
|
|
12
|
+
companion object {
|
|
13
|
+
// Parameter validation errors
|
|
14
|
+
fun missingBundleId() =
|
|
15
|
+
HotUpdaterException(
|
|
16
|
+
"MISSING_BUNDLE_ID",
|
|
17
|
+
"Missing or empty 'bundleId'",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
fun invalidFileUrl() =
|
|
21
|
+
HotUpdaterException(
|
|
22
|
+
"INVALID_FILE_URL",
|
|
23
|
+
"Invalid 'fileUrl' provided",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// Bundle storage errors
|
|
27
|
+
fun directoryCreationFailed() =
|
|
28
|
+
HotUpdaterException(
|
|
29
|
+
"DIRECTORY_CREATION_FAILED",
|
|
30
|
+
"Failed to create bundle directory",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
fun downloadFailed(cause: Throwable? = null) =
|
|
34
|
+
HotUpdaterException(
|
|
35
|
+
"DOWNLOAD_FAILED",
|
|
36
|
+
"Failed to download bundle",
|
|
37
|
+
cause,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
fun incompleteDownload(
|
|
41
|
+
expectedSize: Long,
|
|
42
|
+
actualSize: Long,
|
|
43
|
+
) = HotUpdaterException(
|
|
44
|
+
"INCOMPLETE_DOWNLOAD",
|
|
45
|
+
"Download incomplete: received $actualSize bytes, expected $expectedSize bytes",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
fun extractionFormatError(cause: Throwable? = null) =
|
|
49
|
+
HotUpdaterException(
|
|
50
|
+
"EXTRACTION_FORMAT_ERROR",
|
|
51
|
+
"Invalid or corrupted bundle archive format",
|
|
52
|
+
cause,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
fun invalidBundle() =
|
|
56
|
+
HotUpdaterException(
|
|
57
|
+
"INVALID_BUNDLE",
|
|
58
|
+
"Bundle missing required platform files (index.ios.bundle or index.android.bundle)",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
fun insufficientDiskSpace(
|
|
62
|
+
required: Long,
|
|
63
|
+
available: Long,
|
|
64
|
+
) = HotUpdaterException(
|
|
65
|
+
"INSUFFICIENT_DISK_SPACE",
|
|
66
|
+
"Insufficient disk space: need $required bytes, available $available bytes",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
fun signatureVerificationFailed(cause: Throwable? = null) =
|
|
70
|
+
HotUpdaterException(
|
|
71
|
+
"SIGNATURE_VERIFICATION_FAILED",
|
|
72
|
+
"Bundle signature verification failed",
|
|
73
|
+
cause,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
fun moveOperationFailed() =
|
|
77
|
+
HotUpdaterException(
|
|
78
|
+
"MOVE_OPERATION_FAILED",
|
|
79
|
+
"Failed to move bundle files",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
fun bundleInCrashedHistory(bundleId: String) =
|
|
83
|
+
HotUpdaterException(
|
|
84
|
+
"BUNDLE_IN_CRASHED_HISTORY",
|
|
85
|
+
"Bundle '$bundleId' is in crashed history and cannot be applied",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
// Signature verification errors
|
|
89
|
+
fun publicKeyNotConfigured() =
|
|
90
|
+
HotUpdaterException(
|
|
91
|
+
"PUBLIC_KEY_NOT_CONFIGURED",
|
|
92
|
+
"Public key not configured for signature verification",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
fun invalidPublicKeyFormat() =
|
|
96
|
+
HotUpdaterException(
|
|
97
|
+
"INVALID_PUBLIC_KEY_FORMAT",
|
|
98
|
+
"Invalid public key format",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
fun fileHashMismatch() =
|
|
102
|
+
HotUpdaterException(
|
|
103
|
+
"FILE_HASH_MISMATCH",
|
|
104
|
+
"File hash verification failed",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
fun fileReadFailed() =
|
|
108
|
+
HotUpdaterException(
|
|
109
|
+
"FILE_READ_FAILED",
|
|
110
|
+
"Failed to read file for verification",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
fun unsignedNotAllowed() =
|
|
114
|
+
HotUpdaterException(
|
|
115
|
+
"UNSIGNED_NOT_ALLOWED",
|
|
116
|
+
"Unsigned bundles are not allowed",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
fun securityFrameworkError(cause: Throwable? = null) =
|
|
120
|
+
HotUpdaterException(
|
|
121
|
+
"SECURITY_FRAMEWORK_ERROR",
|
|
122
|
+
"Security framework error occurred",
|
|
123
|
+
cause,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
// Internal errors
|
|
127
|
+
fun unknownError(cause: Throwable? = null) =
|
|
128
|
+
HotUpdaterException(
|
|
129
|
+
"UNKNOWN_ERROR",
|
|
130
|
+
"An unknown error occurred",
|
|
131
|
+
cause,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|