@hot-updater/react-native 0.25.14 → 0.26.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 +42 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +25 -0
- package/android/src/newarch/HotUpdater.kt +4 -1
- package/android/src/newarch/HotUpdaterModule.kt +18 -1
- package/android/src/oldarch/HotUpdater.kt +4 -1
- package/android/src/oldarch/HotUpdaterModule.kt +19 -1
- package/android/src/oldarch/HotUpdaterSpec.kt +2 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +44 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +17 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +44 -2
- package/lib/commonjs/checkForUpdate.js +29 -4
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/index.js +35 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/native.js +88 -21
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/checkForUpdate.js +29 -5
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/index.js +36 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/native.js +81 -16
- package/lib/module/native.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +5 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +26 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +13 -0
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +8 -0
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +5 -0
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +26 -0
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +13 -0
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +8 -0
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/checkForUpdate.ts +63 -3
- package/src/index.ts +41 -0
- package/src/native.ts +103 -16
- package/src/specs/NativeHotUpdater.ts +9 -0
|
@@ -76,6 +76,12 @@ interface BundleStorageService {
|
|
|
76
76
|
* @return Base URL string (e.g., "file:///data/.../bundle-store/abc123") or empty string
|
|
77
77
|
*/
|
|
78
78
|
fun getBaseURL(): String
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Restores the original bundle and clears downloaded bundle state.
|
|
82
|
+
* @return true if the reset was successful
|
|
83
|
+
*/
|
|
84
|
+
suspend fun resetChannel(): Boolean
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
/**
|
|
@@ -829,4 +835,40 @@ class BundleFileStorageService(
|
|
|
829
835
|
""
|
|
830
836
|
}
|
|
831
837
|
}
|
|
838
|
+
|
|
839
|
+
override suspend fun resetChannel(): Boolean =
|
|
840
|
+
withContext(Dispatchers.IO) {
|
|
841
|
+
if (!setBundleURL(null)) {
|
|
842
|
+
return@withContext false
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
val clearedMetadata =
|
|
846
|
+
BundleMetadata(
|
|
847
|
+
isolationKey = isolationKey,
|
|
848
|
+
stableBundleId = null,
|
|
849
|
+
stagingBundleId = null,
|
|
850
|
+
verificationPending = false,
|
|
851
|
+
verificationAttemptedAt = null,
|
|
852
|
+
stagingExecutionCount = null,
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
if (!saveMetadata(clearedMetadata)) {
|
|
856
|
+
return@withContext false
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
getBundleStoreDir().listFiles()?.forEach { file ->
|
|
860
|
+
if (
|
|
861
|
+
file.name == BundleMetadata.METADATA_FILENAME ||
|
|
862
|
+
file.name == CrashedHistory.CRASHED_HISTORY_FILENAME
|
|
863
|
+
) {
|
|
864
|
+
return@forEach
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
if (file.isDirectory) {
|
|
868
|
+
file.deleteRecursively()
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
true
|
|
873
|
+
}
|
|
832
874
|
}
|
|
@@ -61,6 +61,7 @@ class HotUpdaterImpl {
|
|
|
61
61
|
companion object {
|
|
62
62
|
private const val TAG = "HotUpdaterImpl"
|
|
63
63
|
private const val DEFAULT_CHANNEL = "production"
|
|
64
|
+
private const val CHANNEL_STORAGE_KEY = "HotUpdaterChannel"
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Create BundleStorageService with all dependencies
|
|
@@ -242,6 +243,11 @@ class HotUpdaterImpl {
|
|
|
242
243
|
* @return The channel name or null if not set
|
|
243
244
|
*/
|
|
244
245
|
fun getChannel(): String {
|
|
246
|
+
val overriddenChannel = preferences.getItem(CHANNEL_STORAGE_KEY)
|
|
247
|
+
if (!overriddenChannel.isNullOrEmpty()) {
|
|
248
|
+
return overriddenChannel
|
|
249
|
+
}
|
|
250
|
+
|
|
245
251
|
val id = StringResourceUtils.getIdentifier(context, "hot_updater_channel")
|
|
246
252
|
return if (id != 0) {
|
|
247
253
|
context.getString(id).takeIf { it.isNotEmpty() } ?: DEFAULT_CHANNEL
|
|
@@ -250,6 +256,8 @@ class HotUpdaterImpl {
|
|
|
250
256
|
}
|
|
251
257
|
}
|
|
252
258
|
|
|
259
|
+
fun getDefaultChannel(): String = getChannel(context)
|
|
260
|
+
|
|
253
261
|
/**
|
|
254
262
|
* Gets the path to the bundle file
|
|
255
263
|
* @return The path to the bundle file
|
|
@@ -268,9 +276,18 @@ class HotUpdaterImpl {
|
|
|
268
276
|
bundleId: String,
|
|
269
277
|
fileUrl: String?,
|
|
270
278
|
fileHash: String?,
|
|
279
|
+
channel: String?,
|
|
271
280
|
progressCallback: (Double) -> Unit,
|
|
272
281
|
) {
|
|
273
282
|
bundleStorage.updateBundle(bundleId, fileUrl, fileHash, progressCallback)
|
|
283
|
+
|
|
284
|
+
if (!channel.isNullOrEmpty()) {
|
|
285
|
+
if (channel == getDefaultChannel()) {
|
|
286
|
+
preferences.setItem(CHANNEL_STORAGE_KEY, null)
|
|
287
|
+
} else {
|
|
288
|
+
preferences.setItem(CHANNEL_STORAGE_KEY, channel)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
274
291
|
}
|
|
275
292
|
|
|
276
293
|
/**
|
|
@@ -318,4 +335,12 @@ class HotUpdaterImpl {
|
|
|
318
335
|
* @return Base URL string (e.g., "file:///data/.../bundle-store/abc123/") or empty string
|
|
319
336
|
*/
|
|
320
337
|
fun getBaseURL(): String = bundleStorage.getBaseURL()
|
|
338
|
+
|
|
339
|
+
suspend fun resetChannel(): Boolean {
|
|
340
|
+
val success = bundleStorage.resetChannel()
|
|
341
|
+
if (success) {
|
|
342
|
+
preferences.setItem(CHANNEL_STORAGE_KEY, null)
|
|
343
|
+
}
|
|
344
|
+
return success
|
|
345
|
+
}
|
|
321
346
|
}
|
|
@@ -46,9 +46,10 @@ class HotUpdater {
|
|
|
46
46
|
bundleId: String,
|
|
47
47
|
fileUrl: String?,
|
|
48
48
|
fileHash: String?,
|
|
49
|
+
channel: String?,
|
|
49
50
|
progressCallback: (Double) -> Unit,
|
|
50
51
|
) {
|
|
51
|
-
getInstance(context).updateBundle(bundleId, fileUrl, fileHash, progressCallback)
|
|
52
|
+
getInstance(context).updateBundle(bundleId, fileUrl, fileHash, channel, progressCallback)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -102,5 +103,7 @@ class HotUpdater {
|
|
|
102
103
|
fun clearReactHost() {
|
|
103
104
|
ReactHostHolder.clear()
|
|
104
105
|
}
|
|
106
|
+
|
|
107
|
+
suspend fun resetChannel(context: Context): Boolean = getInstance(context).resetChannel()
|
|
105
108
|
}
|
|
106
109
|
}
|
|
@@ -74,6 +74,7 @@ class HotUpdaterModule internal constructor(
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
val fileHash = params.getString("fileHash")
|
|
77
|
+
val channel = params.getString("channel")
|
|
77
78
|
|
|
78
79
|
val impl = getInstance()
|
|
79
80
|
|
|
@@ -81,6 +82,7 @@ class HotUpdaterModule internal constructor(
|
|
|
81
82
|
bundleId,
|
|
82
83
|
fileUrl,
|
|
83
84
|
fileHash,
|
|
85
|
+
channel,
|
|
84
86
|
) { progress ->
|
|
85
87
|
// Post to Main thread for React Native event emission
|
|
86
88
|
Handler(Looper.getMainLooper()).post {
|
|
@@ -112,7 +114,8 @@ class HotUpdaterModule internal constructor(
|
|
|
112
114
|
val constants: MutableMap<String, Any?> = HashMap()
|
|
113
115
|
constants["MIN_BUNDLE_ID"] = HotUpdater.getMinBundleId()
|
|
114
116
|
constants["APP_VERSION"] = HotUpdater.getAppVersion(mReactApplicationContext)
|
|
115
|
-
constants["CHANNEL"] =
|
|
117
|
+
constants["CHANNEL"] = getInstance().getChannel()
|
|
118
|
+
constants["DEFAULT_CHANNEL"] = getInstance().getDefaultChannel()
|
|
116
119
|
constants["FINGERPRINT_HASH"] = HotUpdater.getFingerprintHash(mReactApplicationContext)
|
|
117
120
|
return constants
|
|
118
121
|
}
|
|
@@ -166,6 +169,20 @@ class HotUpdaterModule internal constructor(
|
|
|
166
169
|
return impl.getBaseURL()
|
|
167
170
|
}
|
|
168
171
|
|
|
172
|
+
override fun resetChannel(promise: Promise) {
|
|
173
|
+
moduleScope.launch {
|
|
174
|
+
try {
|
|
175
|
+
val impl = getInstance()
|
|
176
|
+
val success = impl.resetChannel()
|
|
177
|
+
promise.resolve(success)
|
|
178
|
+
} catch (e: HotUpdaterException) {
|
|
179
|
+
promise.reject(e.code, e.message)
|
|
180
|
+
} catch (e: Exception) {
|
|
181
|
+
promise.reject("UNKNOWN_ERROR", e.message ?: "Failed to reset channel")
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
169
186
|
companion object {
|
|
170
187
|
const val NAME = "HotUpdater"
|
|
171
188
|
}
|
|
@@ -45,9 +45,10 @@ class HotUpdater {
|
|
|
45
45
|
bundleId: String,
|
|
46
46
|
fileUrl: String?,
|
|
47
47
|
fileHash: String?,
|
|
48
|
+
channel: String?,
|
|
48
49
|
progressCallback: (Double) -> Unit,
|
|
49
50
|
) {
|
|
50
|
-
getInstance(context).updateBundle(bundleId, fileUrl, fileHash, progressCallback)
|
|
51
|
+
getInstance(context).updateBundle(bundleId, fileUrl, fileHash, channel, progressCallback)
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -100,5 +101,7 @@ class HotUpdater {
|
|
|
100
101
|
fun clearReactHost() {
|
|
101
102
|
// No-op for old architecture
|
|
102
103
|
}
|
|
104
|
+
|
|
105
|
+
suspend fun resetChannel(context: Context): Boolean = getInstance(context).resetChannel()
|
|
103
106
|
}
|
|
104
107
|
}
|
|
@@ -78,6 +78,7 @@ class HotUpdaterModule internal constructor(
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
val fileHash = params.getString("fileHash")
|
|
81
|
+
val channel = params.getString("channel")
|
|
81
82
|
|
|
82
83
|
val impl = getInstance()
|
|
83
84
|
|
|
@@ -85,6 +86,7 @@ class HotUpdaterModule internal constructor(
|
|
|
85
86
|
bundleId,
|
|
86
87
|
fileUrl,
|
|
87
88
|
fileHash,
|
|
89
|
+
channel,
|
|
88
90
|
) { progress ->
|
|
89
91
|
// Post to Main thread for React Native event emission
|
|
90
92
|
Handler(Looper.getMainLooper()).post {
|
|
@@ -130,7 +132,8 @@ class HotUpdaterModule internal constructor(
|
|
|
130
132
|
val constants: MutableMap<String, Any?> = HashMap()
|
|
131
133
|
constants["MIN_BUNDLE_ID"] = HotUpdater.getMinBundleId()
|
|
132
134
|
constants["APP_VERSION"] = HotUpdater.getAppVersion(mReactApplicationContext)
|
|
133
|
-
constants["CHANNEL"] =
|
|
135
|
+
constants["CHANNEL"] = getInstance().getChannel()
|
|
136
|
+
constants["DEFAULT_CHANNEL"] = getInstance().getDefaultChannel()
|
|
134
137
|
constants["FINGERPRINT_HASH"] = HotUpdater.getFingerprintHash(mReactApplicationContext)
|
|
135
138
|
return constants
|
|
136
139
|
}
|
|
@@ -175,6 +178,21 @@ class HotUpdaterModule internal constructor(
|
|
|
175
178
|
return impl.getBaseURL()
|
|
176
179
|
}
|
|
177
180
|
|
|
181
|
+
@ReactMethod
|
|
182
|
+
override fun resetChannel(promise: Promise) {
|
|
183
|
+
moduleScope.launch {
|
|
184
|
+
try {
|
|
185
|
+
val impl = getInstance()
|
|
186
|
+
val success = impl.resetChannel()
|
|
187
|
+
promise.resolve(success)
|
|
188
|
+
} catch (e: HotUpdaterException) {
|
|
189
|
+
promise.reject(e.code, e.message)
|
|
190
|
+
} catch (e: Exception) {
|
|
191
|
+
promise.reject("UNKNOWN_ERROR", e.message ?: "Failed to reset channel")
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
178
196
|
companion object {
|
|
179
197
|
const val NAME = "HotUpdater"
|
|
180
198
|
}
|
|
@@ -118,6 +118,11 @@ public protocol BundleStorageService {
|
|
|
118
118
|
* @return Base URL string (e.g., "file:///data/.../bundle-store/abc123") or empty string
|
|
119
119
|
*/
|
|
120
120
|
func getBaseURL() -> String
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Restores the original bundle and clears downloaded bundle state.
|
|
124
|
+
*/
|
|
125
|
+
func resetChannel() -> Result<Bool, Error>
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
class BundleFileStorageService: BundleStorageService {
|
|
@@ -1225,6 +1230,45 @@ class BundleFileStorageService: BundleStorageService {
|
|
|
1225
1230
|
return ""
|
|
1226
1231
|
}
|
|
1227
1232
|
}
|
|
1233
|
+
|
|
1234
|
+
func resetChannel() -> Result<Bool, Error> {
|
|
1235
|
+
guard case .success = setBundleURL(localPath: nil) else {
|
|
1236
|
+
return .failure(BundleStorageError.unknown(nil))
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
let clearedMetadata = BundleMetadata(
|
|
1240
|
+
isolationKey: isolationKey,
|
|
1241
|
+
stableBundleId: nil,
|
|
1242
|
+
stagingBundleId: nil,
|
|
1243
|
+
verificationPending: false,
|
|
1244
|
+
verificationAttemptedAt: nil,
|
|
1245
|
+
stagingExecutionCount: nil
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
guard saveMetadata(clearedMetadata) else {
|
|
1249
|
+
return .failure(BundleStorageError.unknown(nil))
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
guard case .success(let storeDir) = bundleStoreDir() else {
|
|
1253
|
+
return .failure(BundleStorageError.unknown(nil))
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
do {
|
|
1257
|
+
for item in try fileSystem.contentsOfDirectory(atPath: storeDir) {
|
|
1258
|
+
if item == BundleMetadata.metadataFilename || item == CrashedHistory.crashedHistoryFilename {
|
|
1259
|
+
continue
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
let bundlePath = (storeDir as NSString).appendingPathComponent(item)
|
|
1263
|
+
if fileSystem.fileExists(atPath: bundlePath) {
|
|
1264
|
+
try fileSystem.removeItem(atPath: bundlePath)
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return .success(true)
|
|
1268
|
+
} catch {
|
|
1269
|
+
return .failure(BundleStorageError.moveOperationFailed(error))
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1228
1272
|
}
|
|
1229
1273
|
|
|
1230
1274
|
// Helper to get the associated error from a Result, if it's a failure
|
|
@@ -172,6 +172,7 @@ RCT_EXPORT_MODULE();
|
|
|
172
172
|
@"MIN_BUNDLE_ID": [self getMinBundleId] ?: [NSNull null],
|
|
173
173
|
@"APP_VERSION": [HotUpdaterImpl appVersion] ?: [NSNull null],
|
|
174
174
|
@"CHANNEL": [[HotUpdater sharedImpl] getChannel] ?: [NSNull null],
|
|
175
|
+
@"DEFAULT_CHANNEL": [[HotUpdater sharedImpl] getDefaultChannel] ?: [NSNull null],
|
|
175
176
|
@"FINGERPRINT_HASH": [[HotUpdater sharedImpl] getFingerprintHash] ?: [NSNull null]
|
|
176
177
|
};
|
|
177
178
|
}
|
|
@@ -286,6 +287,9 @@ RCT_EXPORT_MODULE();
|
|
|
286
287
|
if (params.fileHash()) {
|
|
287
288
|
paramDict[@"fileHash"] = params.fileHash();
|
|
288
289
|
}
|
|
290
|
+
if (params.channel()) {
|
|
291
|
+
paramDict[@"channel"] = params.channel();
|
|
292
|
+
}
|
|
289
293
|
|
|
290
294
|
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
291
295
|
[impl updateBundle:paramDict resolver:resolve rejecter:reject];
|
|
@@ -322,6 +326,12 @@ RCT_EXPORT_MODULE();
|
|
|
322
326
|
return baseURL ?: @"";
|
|
323
327
|
}
|
|
324
328
|
|
|
329
|
+
- (void)resetChannel:(RCTPromiseResolveBlock)resolve
|
|
330
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
331
|
+
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
332
|
+
[impl resetChannel:resolve rejecter:reject];
|
|
333
|
+
}
|
|
334
|
+
|
|
325
335
|
- (facebook::react::ModuleConstants<JS::NativeHotUpdater::Constants::Builder>)constantsToExport {
|
|
326
336
|
return [self getConstants];
|
|
327
337
|
}
|
|
@@ -332,6 +342,7 @@ RCT_EXPORT_MODULE();
|
|
|
332
342
|
.MIN_BUNDLE_ID = [self getMinBundleId],
|
|
333
343
|
.APP_VERSION = [HotUpdaterImpl appVersion],
|
|
334
344
|
.CHANNEL = [impl getChannel],
|
|
345
|
+
.DEFAULT_CHANNEL = [impl getDefaultChannel],
|
|
335
346
|
.FINGERPRINT_HASH = [impl getFingerprintHash],
|
|
336
347
|
});
|
|
337
348
|
}
|
|
@@ -398,6 +409,12 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBaseURL) {
|
|
|
398
409
|
return baseURL ?: @"";
|
|
399
410
|
}
|
|
400
411
|
|
|
412
|
+
RCT_EXPORT_METHOD(resetChannel:(RCTPromiseResolveBlock)resolve
|
|
413
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
414
|
+
HotUpdaterImpl *impl = [HotUpdater sharedImpl];
|
|
415
|
+
[impl resetChannel:resolve rejecter:reject];
|
|
416
|
+
}
|
|
417
|
+
|
|
401
418
|
- (NSDictionary *)constantsToExport {
|
|
402
419
|
return [self _buildConstantsDictionary];
|
|
403
420
|
}
|
|
@@ -6,6 +6,7 @@ import React
|
|
|
6
6
|
private let preferences: PreferencesService
|
|
7
7
|
|
|
8
8
|
private static let DEFAULT_CHANNEL = "production"
|
|
9
|
+
private static let CHANNEL_STORAGE_KEY = "HotUpdaterChannel"
|
|
9
10
|
|
|
10
11
|
// MARK: - Initialization
|
|
11
12
|
|
|
@@ -91,7 +92,16 @@ import React
|
|
|
91
92
|
* @return The channel name or nil if not set
|
|
92
93
|
*/
|
|
93
94
|
public func getChannel() -> String {
|
|
94
|
-
|
|
95
|
+
if let savedChannel = try? preferences.getItem(forKey: Self.CHANNEL_STORAGE_KEY),
|
|
96
|
+
!savedChannel.isEmpty {
|
|
97
|
+
return savedChannel
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return Self.appChannel
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public func getDefaultChannel() -> String {
|
|
104
|
+
return Self.appChannel
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
/**
|
|
@@ -157,6 +167,7 @@ import React
|
|
|
157
167
|
|
|
158
168
|
// Extract fileHash if provided
|
|
159
169
|
let fileHash = data["fileHash"] as? String
|
|
170
|
+
let channel = data["channel"] as? String
|
|
160
171
|
|
|
161
172
|
// Extract progress callback if provided
|
|
162
173
|
let progressCallback = data["progressCallback"] as? RCTResponseSenderBlock
|
|
@@ -172,7 +183,7 @@ import React
|
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
185
|
}) { [weak self] result in
|
|
175
|
-
guard self
|
|
186
|
+
guard let self = self else {
|
|
176
187
|
let error = NSError(domain: "HotUpdater", code: 0,
|
|
177
188
|
userInfo: [NSLocalizedDescriptionKey: "Internal error: self deallocated during update"])
|
|
178
189
|
DispatchQueue.main.async {
|
|
@@ -185,6 +196,17 @@ import React
|
|
|
185
196
|
switch result {
|
|
186
197
|
case .success:
|
|
187
198
|
NSLog("[HotUpdaterImpl] Update successful for \(bundleId). Resolving promise.")
|
|
199
|
+
if let channel, !channel.isEmpty {
|
|
200
|
+
do {
|
|
201
|
+
if channel == self.getDefaultChannel() {
|
|
202
|
+
try self.preferences.setItem(nil, forKey: Self.CHANNEL_STORAGE_KEY)
|
|
203
|
+
} else {
|
|
204
|
+
try self.preferences.setItem(channel, forKey: Self.CHANNEL_STORAGE_KEY)
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
NSLog("[HotUpdaterImpl] Failed to persist channel override: \(error)")
|
|
208
|
+
}
|
|
209
|
+
}
|
|
188
210
|
resolve(true)
|
|
189
211
|
case .failure(let error):
|
|
190
212
|
NSLog("[HotUpdaterImpl] Update failed for \(bundleId) - Error: \(error)")
|
|
@@ -281,4 +303,24 @@ import React
|
|
|
281
303
|
public func getBaseURL() -> String {
|
|
282
304
|
return bundleStorage.getBaseURL()
|
|
283
305
|
}
|
|
306
|
+
|
|
307
|
+
@objc
|
|
308
|
+
public func resetChannel(_ resolver: @escaping RCTPromiseResolveBlock,
|
|
309
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
310
|
+
let result = bundleStorage.resetChannel()
|
|
311
|
+
|
|
312
|
+
switch result {
|
|
313
|
+
case .success(let success):
|
|
314
|
+
do {
|
|
315
|
+
try preferences.setItem(nil, forKey: Self.CHANNEL_STORAGE_KEY)
|
|
316
|
+
} catch {
|
|
317
|
+
NSLog("[HotUpdaterImpl] Failed to clear channel override: \(error)")
|
|
318
|
+
}
|
|
319
|
+
resolver(success)
|
|
320
|
+
case .failure(let error):
|
|
321
|
+
let normalizedCode = HotUpdaterImpl.normalizeErrorCode(from: error)
|
|
322
|
+
let nsError = error as NSError
|
|
323
|
+
reject(normalizedCode, nsError.localizedDescription, nsError)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
284
326
|
}
|
|
@@ -7,8 +7,13 @@ exports.checkForUpdate = checkForUpdate;
|
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
var _error = require("./error.js");
|
|
9
9
|
var _native = require("./native.js");
|
|
10
|
+
const NIL_UUID = "00000000-0000-0000-0000-000000000000";
|
|
11
|
+
|
|
10
12
|
// Internal type that includes resolver for use within index.ts
|
|
11
13
|
|
|
14
|
+
const isResetToBuiltInResponse = updateInfo => {
|
|
15
|
+
return updateInfo.status === "ROLLBACK" && updateInfo.id === NIL_UUID && updateInfo.fileUrl === null;
|
|
16
|
+
};
|
|
12
17
|
async function checkForUpdate(options) {
|
|
13
18
|
if (__DEV__) {
|
|
14
19
|
return null;
|
|
@@ -21,11 +26,22 @@ async function checkForUpdate(options) {
|
|
|
21
26
|
const platform = _reactNative.Platform.OS;
|
|
22
27
|
const currentBundleId = (0, _native.getBundleId)();
|
|
23
28
|
const minBundleId = (0, _native.getMinBundleId)();
|
|
24
|
-
const
|
|
29
|
+
const defaultChannel = (0, _native.getDefaultChannel)();
|
|
30
|
+
const isSwitched = (0, _native.isChannelSwitched)();
|
|
31
|
+
const currentChannel = isSwitched ? (0, _native.getChannel)() : defaultChannel;
|
|
32
|
+
const explicitChannel = options.channel || undefined;
|
|
33
|
+
const targetChannel = explicitChannel || currentChannel;
|
|
34
|
+
const isFirstRuntimeChannelSwitchAttempt = !isSwitched && explicitChannel !== undefined && explicitChannel !== defaultChannel;
|
|
35
|
+
const requestBundleId = isFirstRuntimeChannelSwitchAttempt ? minBundleId : currentBundleId;
|
|
25
36
|
if (!currentAppVersion) {
|
|
26
37
|
options.onError?.(new _error.HotUpdaterError("Failed to get app version"));
|
|
27
38
|
return null;
|
|
28
39
|
}
|
|
40
|
+
if (isSwitched && explicitChannel && explicitChannel !== currentChannel) {
|
|
41
|
+
const error = new _error.HotUpdaterError(`Runtime channel is already switched to "${currentChannel}". Call HotUpdater.resetChannel() before checking "${explicitChannel}".`);
|
|
42
|
+
options.onError?.(error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
29
45
|
const fingerprintHash = (0, _native.getFingerprintHash)();
|
|
30
46
|
if (!options.resolver?.checkUpdate) {
|
|
31
47
|
options.onError?.(new _error.HotUpdaterError("Resolver is required but not configured"));
|
|
@@ -36,9 +52,9 @@ async function checkForUpdate(options) {
|
|
|
36
52
|
updateInfo = await options.resolver.checkUpdate({
|
|
37
53
|
platform,
|
|
38
54
|
appVersion: currentAppVersion,
|
|
39
|
-
bundleId:
|
|
55
|
+
bundleId: requestBundleId,
|
|
40
56
|
minBundleId,
|
|
41
|
-
channel,
|
|
57
|
+
channel: targetChannel,
|
|
42
58
|
updateStrategy: options.updateStrategy,
|
|
43
59
|
fingerprintHash,
|
|
44
60
|
requestHeaders: options.requestHeaders,
|
|
@@ -51,14 +67,23 @@ async function checkForUpdate(options) {
|
|
|
51
67
|
if (!updateInfo) {
|
|
52
68
|
return null;
|
|
53
69
|
}
|
|
70
|
+
if (explicitChannel && explicitChannel !== defaultChannel && !isSwitched && updateInfo.status === "ROLLBACK") {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
54
73
|
return {
|
|
55
74
|
...updateInfo,
|
|
56
75
|
updateBundle: async () => {
|
|
76
|
+
if (explicitChannel && isSwitched && isResetToBuiltInResponse(updateInfo)) {
|
|
77
|
+
return (0, _native.resetChannel)();
|
|
78
|
+
}
|
|
79
|
+
const runtimeChannel = updateInfo.fileUrl !== null ? targetChannel : undefined;
|
|
57
80
|
return (0, _native.updateBundle)({
|
|
58
81
|
bundleId: updateInfo.id,
|
|
82
|
+
channel: runtimeChannel,
|
|
59
83
|
fileUrl: updateInfo.fileUrl,
|
|
60
84
|
fileHash: updateInfo.fileHash,
|
|
61
|
-
status: updateInfo.status
|
|
85
|
+
status: updateInfo.status,
|
|
86
|
+
shouldSkipCurrentBundleIdCheck: isFirstRuntimeChannelSwitchAttempt
|
|
62
87
|
});
|
|
63
88
|
}
|
|
64
89
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","_error","_native","checkForUpdate","options","__DEV__","includes","Platform","OS","onError","HotUpdaterError","currentAppVersion","getAppVersion","platform","currentBundleId","getBundleId","minBundleId","getMinBundleId","
|
|
1
|
+
{"version":3,"names":["_reactNative","require","_error","_native","NIL_UUID","isResetToBuiltInResponse","updateInfo","status","id","fileUrl","checkForUpdate","options","__DEV__","includes","Platform","OS","onError","HotUpdaterError","currentAppVersion","getAppVersion","platform","currentBundleId","getBundleId","minBundleId","getMinBundleId","defaultChannel","getDefaultChannel","isSwitched","isChannelSwitched","currentChannel","getChannel","explicitChannel","channel","undefined","targetChannel","isFirstRuntimeChannelSwitchAttempt","requestBundleId","error","fingerprintHash","getFingerprintHash","resolver","checkUpdate","appVersion","bundleId","updateStrategy","requestHeaders","requestTimeout","updateBundle","resetChannel","runtimeChannel","fileHash","shouldSkipCurrentBundleIdCheck"],"sourceRoot":"../../src","sources":["checkForUpdate.ts"],"mappings":"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","ignoreList":[]}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -172,6 +172,25 @@ function createHotUpdaterClient() {
|
|
|
172
172
|
* ```
|
|
173
173
|
*/
|
|
174
174
|
getChannel: _native.getChannel,
|
|
175
|
+
/**
|
|
176
|
+
* Fetches the build-time default channel of the app.
|
|
177
|
+
*
|
|
178
|
+
* This value does not change when a runtime channel override is active.
|
|
179
|
+
*
|
|
180
|
+
* @returns {string} The default release channel embedded in the app
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* const defaultChannel = HotUpdater.getDefaultChannel();
|
|
184
|
+
* console.log(`Default channel: ${defaultChannel}`);
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
getDefaultChannel: _native.getDefaultChannel,
|
|
188
|
+
/**
|
|
189
|
+
* Returns whether the app is currently using a runtime channel override.
|
|
190
|
+
*
|
|
191
|
+
* @returns {boolean} true when a non-default channel has been applied
|
|
192
|
+
*/
|
|
193
|
+
isChannelSwitched: _native.isChannelSwitched,
|
|
175
194
|
/**
|
|
176
195
|
* Adds a listener to HotUpdater events.
|
|
177
196
|
*
|
|
@@ -195,6 +214,7 @@ function createHotUpdaterClient() {
|
|
|
195
214
|
*
|
|
196
215
|
* @param {Object} config - Update check configuration
|
|
197
216
|
* @param {string} config.source - Update server URL
|
|
217
|
+
* @param {string} [config.channel] - Optional channel override for this update check
|
|
198
218
|
* @param {Record<string, string>} [config.requestHeaders] - Request headers
|
|
199
219
|
*
|
|
200
220
|
* @returns {Promise<UpdateInfo | null>} Update information or null if up to date
|
|
@@ -269,6 +289,21 @@ function createHotUpdaterClient() {
|
|
|
269
289
|
ensureGlobalResolver("updateBundle");
|
|
270
290
|
return (0, _native.updateBundle)(params);
|
|
271
291
|
},
|
|
292
|
+
/**
|
|
293
|
+
* Clears the runtime channel override and restores the original bundle.
|
|
294
|
+
*
|
|
295
|
+
* @returns {Promise<boolean>} Resolves with true if reset was successful
|
|
296
|
+
*/
|
|
297
|
+
resetChannel: async () => {
|
|
298
|
+
const ok = await (0, _native.resetChannel)();
|
|
299
|
+
if (ok) {
|
|
300
|
+
_store.hotUpdaterStore.setState({
|
|
301
|
+
isUpdateDownloaded: false,
|
|
302
|
+
progress: 0
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return ok;
|
|
306
|
+
},
|
|
272
307
|
/**
|
|
273
308
|
* Fetches the fingerprint of the app.
|
|
274
309
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_checkForUpdate","require","_DefaultResolver","_native","_store","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_wrap","_types","registerGlobalGetBaseURL","fn","getBaseURL","globalThis","HotUpdaterGetBaseURL","global","createHotUpdaterClient","globalConfig","resolver","ensureGlobalResolver","methodName","Error","wrap","options","normalizedOptions","baseURL","rest","createDefaultResolver","requestHeaders","requestTimeout","reload","isUpdateDownloaded","hotUpdaterStore","getSnapshot","getAppVersion","getBundleId","getMinBundleId","getChannel","addListener","checkForUpdate","config","mergedConfig","updateBundle","params","getFingerprintHash","getCrashHistory","clearCrashHistory","HotUpdater"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA;AAKA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;
|
|
1
|
+
{"version":3,"names":["_checkForUpdate","require","_DefaultResolver","_native","_store","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","_wrap","_types","registerGlobalGetBaseURL","fn","getBaseURL","globalThis","HotUpdaterGetBaseURL","global","createHotUpdaterClient","globalConfig","resolver","ensureGlobalResolver","methodName","Error","wrap","options","normalizedOptions","baseURL","rest","createDefaultResolver","requestHeaders","requestTimeout","reload","isUpdateDownloaded","hotUpdaterStore","getSnapshot","getAppVersion","getBundleId","getMinBundleId","getChannel","getDefaultChannel","isChannelSwitched","addListener","checkForUpdate","config","mergedConfig","updateBundle","params","resetChannel","ok","setState","progress","getFingerprintHash","getCrashHistory","clearCrashHistory","HotUpdater"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA;AAKA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AAiBA,IAAAG,MAAA,GAAAH,OAAA;AAKAI,MAAA,CAAAC,IAAA,CAAAF,MAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,MAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,MAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AAHA,IAAAS,KAAA,GAAAhB,OAAA;AAIA,IAAAiB,MAAA,GAAAjB,OAAA;AAUA;AACA;AACA;AACA;AACA,MAAMkB,wBAAwB,GAAGA,CAAA,KAAM;EACrC,MAAMC,EAAE,GAAGC,kBAAU;;EAErB;EACA,IAAI,OAAOC,UAAU,KAAK,WAAW,EAAE;IACrC,IAAI,CAACA,UAAU,CAACC,oBAAoB,EAAE;MACpCD,UAAU,CAACC,oBAAoB,GAAGH,EAAE;IACtC;EACF;;EAEA;EACA,IAAI,OAAOI,MAAM,KAAK,WAAW,EAAE;IACjC,IAAI,CAACA,MAAM,CAACD,oBAAoB,EAAE;MAChCC,MAAM,CAACD,oBAAoB,GAAGH,EAAE;IAClC;EACF;AACF,CAAC;;AAED;AACAD,wBAAwB,CAAC,CAAC;;AAE1B;AACA;AACA;AACA;AACA,SAASM,sBAAsBA,CAAA,EAAG;EAChC;EACA,MAAMC,YAIL,GAAG;IACFC,QAAQ,EAAE;EACZ,CAAC;EAED,MAAMC,oBAAoB,GAAIC,UAAkB,IAAK;IACnD,IAAI,CAACH,YAAY,CAACC,QAAQ,EAAE;MAC1B,MAAM,IAAIG,KAAK,CACb,gBAAgBD,UAAU,6CAA6C,GACrE,yEAAyE,GACzE,oCAAoC,GACpC,sCAAsC,GACtC,4CAA4C,GAC5C,qCAAqC,GACrC,0BAA0B,GAC1B,gBAAgB,GAChB,+CAA+C,GAC/C,sCAAsC,GACtC,4CAA4C,GAC5C,4BAA4B,GAC5B,gBAAgB,GAChB,iFACJ,CAAC;IACH;IACA,OAAOH,YAAY,CAACC,QAAQ;EAC9B,CAAC;EAED,OAAO;IACL;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACII,IAAI,EAAGC,OAA0B,IAAK;MACpC,IAAIC,iBAAsC;MAE1C,IAAI,SAAS,IAAID,OAAO,IAAIA,OAAO,CAACE,OAAO,EAAE;QAC3C,MAAM;UAAEA,OAAO;UAAE,GAAGC;QAAK,CAAC,GAAGH,OAAO;QACpCC,iBAAiB,GAAG;UAClB,GAAGE,IAAI;UACPR,QAAQ,EAAE,IAAAS,sCAAqB,EAACF,OAAO;QACzC,CAAC;MACH,CAAC,MAAM,IAAI,UAAU,IAAIF,OAAO,IAAIA,OAAO,CAACL,QAAQ,EAAE;QACpDM,iBAAiB,GAAGD,OAAO;MAC7B,CAAC,MAAM;QACL,MAAM,IAAIF,KAAK,CACb,+DAA+D,GAC7D,wDAAwD,GACxD,sCAAsC,GACtC,4CAA4C,GAC5C,qCAAqC,GACrC,0BAA0B,GAC1B,gBAAgB,GAChB,8CAA8C,GAC9C,sCAAsC,GACtC,mBAAmB,GACnB,gEAAgE,GAChE,kEAAkE,GAClE,UAAU,GACV,4BAA4B,GAC5B,gBAAgB,GAChB,iFACJ,CAAC;MACH;MAEAJ,YAAY,CAACC,QAAQ,GAAGM,iBAAiB,CAACN,QAAQ;MAClDD,YAAY,CAACW,cAAc,GAAGL,OAAO,CAACK,cAAc;MACpDX,YAAY,CAACY,cAAc,GAAGN,OAAO,CAACM,cAAc;MAEpD,OAAO,IAAAP,UAAI,EAACE,iBAAiB,CAAC;IAChC,CAAC;IAED;AACJ;AACA;IACIM,MAAM,EAANA,cAAM;IAEN;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,kBAAkB,EAAEA,CAAA,KAAMC,sBAAe,CAACC,WAAW,CAAC,CAAC,CAACF,kBAAkB;IAE1E;AACJ;AACA;IACIG,aAAa,EAAbA,qBAAa;IAEb;AACJ;AACA;IACIC,WAAW,EAAXA,mBAAW;IAEX;AACJ;AACA;IACIC,cAAc,EAAdA,sBAAc;IAEd;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,UAAU,EAAVA,kBAAU;IAEV;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA,yBAAiB;IAEjB;AACJ;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA,yBAAiB;IAEjB;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,WAAW,EAAXA,mBAAW;IAEX;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,cAAc,EAAGC,MAA6B,IAAK;MACjD,MAAMxB,QAAQ,GAAGC,oBAAoB,CAAC,gBAAgB,CAAC;MAEvD,MAAMwB,YAA2C,GAAG;QAClD,GAAGD,MAAM;QACTxB,QAAQ;QACRU,cAAc,EAAE;UACd,GAAGX,YAAY,CAACW,cAAc;UAC9B,GAAGc,MAAM,CAACd;QACZ,CAAC;QACDC,cAAc,EAAEa,MAAM,CAACb,cAAc,IAAIZ,YAAY,CAACY;MACxD,CAAC;MAED,OAAO,IAAAY,8BAAc,EAACE,YAAY,CAAC;IACrC,CAAC;IAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,YAAY,EAAGC,MAAoB,IAAK;MACtC1B,oBAAoB,CAAC,cAAc,CAAC;MACpC,OAAO,IAAAyB,oBAAY,EAACC,MAAM,CAAC;IAC7B,CAAC;IAED;AACJ;AACA;AACA;AACA;IACIC,YAAY,EAAE,MAAAA,CAAA,KAAY;MACxB,MAAMC,EAAE,GAAG,MAAM,IAAAD,oBAAY,EAAC,CAAC;MAC/B,IAAIC,EAAE,EAAE;QACNf,sBAAe,CAACgB,QAAQ,CAAC;UACvBjB,kBAAkB,EAAE,KAAK;UACzBkB,QAAQ,EAAE;QACZ,CAAC,CAAC;MACJ;MACA,OAAOF,EAAE;IACX,CAAC;IAED;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIG,kBAAkB,EAAlBA,0BAAkB;IAElB;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,eAAe,EAAfA,uBAAe;IAEf;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACIC,iBAAiB,EAAjBA;EACF,CAAC;AACH;AAEO,MAAMC,UAAU,GAAAjD,OAAA,CAAAiD,UAAA,GAAGrC,sBAAsB,CAAC,CAAC","ignoreList":[]}
|