@hot-updater/react-native 0.33.2 → 0.34.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/HotUpdaterConfig.kt +47 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +4 -0
- package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +14 -2
- package/android/src/newarch/HotUpdater.kt +18 -0
- package/android/src/oldarch/HotUpdater.kt +18 -0
- package/ios/HotUpdater/Internal/HotUpdater.mm +8 -0
- package/ios/HotUpdater/Internal/HotUpdaterConfig.swift +54 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +18 -2
- package/ios/HotUpdater/Internal/SignatureVerifier.swift +3 -0
- package/ios/HotUpdater/Public/HotUpdater.h +9 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/package.json +6 -6
- package/src/sdkVersion.ts +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Thread-safe programmatic configuration holder for HotUpdater.
|
|
5
|
+
*
|
|
6
|
+
* When values are set here, they take priority over manifest metadata and
|
|
7
|
+
* Android string resources. This enables brownfield/AAR setups where the RN
|
|
8
|
+
* module cannot rely on the host app's manifest/strings.xml for configuration.
|
|
9
|
+
*/
|
|
10
|
+
object HotUpdaterConfig {
|
|
11
|
+
@Volatile
|
|
12
|
+
var fingerprintHash: String? = null
|
|
13
|
+
|
|
14
|
+
@Volatile
|
|
15
|
+
var publicKey: String? = null
|
|
16
|
+
|
|
17
|
+
@Volatile
|
|
18
|
+
var channel: String? = null
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* When set, this value is used verbatim as the storage isolation key,
|
|
22
|
+
* bypassing the default `HotUpdaterPrefs_{fingerprint}_{appVersion}_{channel}`
|
|
23
|
+
* composition. Use this to keep the OTA cache stable across host app version
|
|
24
|
+
* bumps (e.g. key only by fingerprint + channel).
|
|
25
|
+
*/
|
|
26
|
+
@Volatile
|
|
27
|
+
var isolationKey: String? = null
|
|
28
|
+
|
|
29
|
+
fun configure(
|
|
30
|
+
fingerprintHash: String? = null,
|
|
31
|
+
publicKey: String? = null,
|
|
32
|
+
channel: String? = null,
|
|
33
|
+
isolationKey: String? = null,
|
|
34
|
+
) {
|
|
35
|
+
this.fingerprintHash = fingerprintHash
|
|
36
|
+
this.publicKey = publicKey
|
|
37
|
+
this.channel = channel
|
|
38
|
+
this.isolationKey = isolationKey
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fun clear() {
|
|
42
|
+
fingerprintHash = null
|
|
43
|
+
publicKey = null
|
|
44
|
+
channel = null
|
|
45
|
+
isolationKey = null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -105,6 +105,10 @@ class HotUpdaterImpl {
|
|
|
105
105
|
* @return The isolation key in format: HotUpdaterPrefs_{fingerprintOrVersion}_{channel}
|
|
106
106
|
*/
|
|
107
107
|
private fun getIsolationKey(context: Context): String {
|
|
108
|
+
// Programmatic override (brownfield/AAR). When set, it is used verbatim
|
|
109
|
+
// so the OTA cache can stay stable across host app version bumps.
|
|
110
|
+
HotUpdaterConfig.isolationKey?.takeIf { it.isNotEmpty() }?.let { return it }
|
|
111
|
+
|
|
108
112
|
val fingerprintHash = getFingerprintHash(context)
|
|
109
113
|
|
|
110
114
|
// Get app version and channel
|
|
@@ -13,9 +13,21 @@ object NativeConfigUtils {
|
|
|
13
13
|
context: Context,
|
|
14
14
|
metaDataKey: String,
|
|
15
15
|
stringResourceName: String,
|
|
16
|
-
): String?
|
|
17
|
-
|
|
16
|
+
): String? {
|
|
17
|
+
// Programmatic overrides take priority over manifest metadata and string
|
|
18
|
+
// resources (brownfield/AAR setups, see HotUpdaterConfig).
|
|
19
|
+
val override =
|
|
20
|
+
when (metaDataKey) {
|
|
21
|
+
FINGERPRINT_HASH_META_DATA_KEY -> HotUpdaterConfig.fingerprintHash
|
|
22
|
+
PUBLIC_KEY_META_DATA_KEY -> HotUpdaterConfig.publicKey
|
|
23
|
+
CHANNEL_META_DATA_KEY -> HotUpdaterConfig.channel
|
|
24
|
+
else -> null
|
|
25
|
+
}
|
|
26
|
+
override?.takeIf { it.isNotEmpty() }?.let { return it }
|
|
27
|
+
|
|
28
|
+
return getManifestMetaDataString(context, metaDataKey)
|
|
18
29
|
?: getStringResource(context, stringResourceName)
|
|
30
|
+
}
|
|
19
31
|
|
|
20
32
|
private fun getManifestMetaDataString(
|
|
21
33
|
context: Context,
|
|
@@ -99,6 +99,24 @@ class HotUpdater {
|
|
|
99
99
|
*/
|
|
100
100
|
fun getChannel(context: Context): String = HotUpdaterImpl.getChannel(context)
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Programmatic configuration for brownfield/AAR setups where manifest
|
|
104
|
+
* metadata or string resources in the host app are not desirable.
|
|
105
|
+
*/
|
|
106
|
+
fun configure(
|
|
107
|
+
fingerprintHash: String? = null,
|
|
108
|
+
publicKey: String? = null,
|
|
109
|
+
channel: String? = null,
|
|
110
|
+
isolationKey: String? = null,
|
|
111
|
+
) {
|
|
112
|
+
HotUpdaterConfig.configure(
|
|
113
|
+
fingerprintHash = fingerprintHash,
|
|
114
|
+
publicKey = publicKey,
|
|
115
|
+
channel = channel,
|
|
116
|
+
isolationKey = isolationKey,
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
/**
|
|
103
121
|
* Sets the ReactHost for brownfield apps (New Architecture).
|
|
104
122
|
* Sets the ReactHost for brownfield apps that don't have ReactApplication.
|
|
@@ -98,6 +98,24 @@ class HotUpdater {
|
|
|
98
98
|
*/
|
|
99
99
|
fun getChannel(context: Context): String = HotUpdaterImpl.getChannel(context)
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Programmatic configuration for brownfield/AAR setups where manifest
|
|
103
|
+
* metadata or string resources in the host app are not desirable.
|
|
104
|
+
*/
|
|
105
|
+
fun configure(
|
|
106
|
+
fingerprintHash: String? = null,
|
|
107
|
+
publicKey: String? = null,
|
|
108
|
+
channel: String? = null,
|
|
109
|
+
isolationKey: String? = null,
|
|
110
|
+
) {
|
|
111
|
+
HotUpdaterConfig.configure(
|
|
112
|
+
fingerprintHash = fingerprintHash,
|
|
113
|
+
publicKey = publicKey,
|
|
114
|
+
channel = channel,
|
|
115
|
+
isolationKey = isolationKey,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
101
119
|
/**
|
|
102
120
|
* Sets the ReactHost for brownfield apps (Old Architecture - No-op).
|
|
103
121
|
* In old architecture, this is a no-op since ReactHost is not available.
|
|
@@ -456,6 +456,14 @@ RCT_EXPORT_MODULE();
|
|
|
456
456
|
return [[HotUpdater sharedImpl] bundleURLWithBundle:bundle];
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
+ (void)configureWithFingerprintHash:(NSString *)fingerprintHash
|
|
460
|
+
publicKey:(NSString *)publicKey
|
|
461
|
+
channel:(NSString *)channel
|
|
462
|
+
isolationKey:(NSString *)isolationKey {
|
|
463
|
+
HotUpdaterConfig *config = [HotUpdaterConfig shared];
|
|
464
|
+
[config configureWithFingerprintHash:fingerprintHash publicKey:publicKey channel:channel isolationKey:isolationKey];
|
|
465
|
+
}
|
|
466
|
+
|
|
459
467
|
#pragma mark - Progress Updates & Event Emitting (Keep in ObjC Wrapper)
|
|
460
468
|
|
|
461
469
|
- (void)handleDownloadProgress:(NSNotification *)notification {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Thread-safe programmatic configuration holder for HotUpdater.
|
|
4
|
+
///
|
|
5
|
+
/// When values are set here, they take priority over `Info.plist` (iOS) and
|
|
6
|
+
/// manifest metadata / string resources (Android). This enables brownfield /
|
|
7
|
+
/// prebuilt-framework setups where the RN module cannot rely on the host app's
|
|
8
|
+
/// `Info.plist` for configuration.
|
|
9
|
+
@objcMembers
|
|
10
|
+
public class HotUpdaterConfig: NSObject {
|
|
11
|
+
public static let shared = HotUpdaterConfig()
|
|
12
|
+
|
|
13
|
+
private let lock = NSLock()
|
|
14
|
+
|
|
15
|
+
private var _fingerprintHash: String?
|
|
16
|
+
private var _publicKey: String?
|
|
17
|
+
private var _channel: String?
|
|
18
|
+
private var _isolationKey: String?
|
|
19
|
+
|
|
20
|
+
public var fingerprintHash: String? {
|
|
21
|
+
get { lock.lock(); defer { lock.unlock() }; return _fingerprintHash }
|
|
22
|
+
set { lock.lock(); defer { lock.unlock() }; _fingerprintHash = newValue }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public var publicKey: String? {
|
|
26
|
+
get { lock.lock(); defer { lock.unlock() }; return _publicKey }
|
|
27
|
+
set { lock.lock(); defer { lock.unlock() }; _publicKey = newValue }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public var channel: String? {
|
|
31
|
+
get { lock.lock(); defer { lock.unlock() }; return _channel }
|
|
32
|
+
set { lock.lock(); defer { lock.unlock() }; _channel = newValue }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// When set, used verbatim as the storage isolation key, bypassing the default
|
|
36
|
+
/// `hotupdater_{fingerprint}_{appVersion}_{channel}_` composition. Use this to keep
|
|
37
|
+
/// the OTA cache stable across host app version bumps (e.g. key only by fingerprint + channel).
|
|
38
|
+
public var isolationKey: String? {
|
|
39
|
+
get { lock.lock(); defer { lock.unlock() }; return _isolationKey }
|
|
40
|
+
set { lock.lock(); defer { lock.unlock() }; _isolationKey = newValue }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc(configureWithFingerprintHash:publicKey:channel:isolationKey:)
|
|
44
|
+
public func configureWithFingerprintHash(_ fingerprintHash: String?, publicKey: String?, channel: String?, isolationKey: String?) {
|
|
45
|
+
self.fingerprintHash = fingerprintHash
|
|
46
|
+
self.publicKey = publicKey
|
|
47
|
+
self.channel = channel
|
|
48
|
+
self.isolationKey = isolationKey
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public func clear() {
|
|
52
|
+
configureWithFingerprintHash(nil, publicKey: nil, channel: nil, isolationKey: nil)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -93,6 +93,9 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
|
|
|
93
93
|
* Returns the app version from main bundle info.
|
|
94
94
|
*/
|
|
95
95
|
public static var appChannel: String {
|
|
96
|
+
if let override = HotUpdaterConfig.shared.channel, !override.isEmpty {
|
|
97
|
+
return override
|
|
98
|
+
}
|
|
96
99
|
return Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_CHANNEL") as? String ?? Self.DEFAULT_CHANNEL
|
|
97
100
|
}
|
|
98
101
|
|
|
@@ -101,8 +104,15 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
|
|
|
101
104
|
* @return The isolation key in format: hotupdater_{fingerprintOrVersion}_{channel}_
|
|
102
105
|
*/
|
|
103
106
|
public static func getIsolationKey() -> String {
|
|
104
|
-
//
|
|
105
|
-
|
|
107
|
+
// Programmatic override (brownfield/XCFramework). When set, it is used verbatim
|
|
108
|
+
// so the OTA cache can stay stable across host app version bumps.
|
|
109
|
+
if let override = HotUpdaterConfig.shared.isolationKey, !override.isEmpty {
|
|
110
|
+
return override
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get fingerprint hash from programmatic override or Info.plist
|
|
114
|
+
let fingerprintHash = HotUpdaterConfig.shared.fingerprintHash
|
|
115
|
+
?? Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_FINGERPRINT_HASH") as? String
|
|
106
116
|
|
|
107
117
|
// Get app version and channel
|
|
108
118
|
let appVersion = self.appVersion ?? "unknown"
|
|
@@ -126,6 +136,9 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
|
|
|
126
136
|
* @return The channel name or nil if not set
|
|
127
137
|
*/
|
|
128
138
|
public func getChannel() -> String {
|
|
139
|
+
if let override = HotUpdaterConfig.shared.channel, !override.isEmpty {
|
|
140
|
+
return override
|
|
141
|
+
}
|
|
129
142
|
if let savedChannel = try? preferences.getItem(forKey: Self.CHANNEL_STORAGE_KEY),
|
|
130
143
|
!savedChannel.isEmpty {
|
|
131
144
|
return savedChannel
|
|
@@ -143,6 +156,9 @@ private func hotUpdaterPerformRecoveryReload() -> Bool {
|
|
|
143
156
|
* @return The fingerprint hash or nil if not set
|
|
144
157
|
*/
|
|
145
158
|
public func getFingerprintHash() -> String? {
|
|
159
|
+
if let override = HotUpdaterConfig.shared.fingerprintHash, !override.isEmpty {
|
|
160
|
+
return override
|
|
161
|
+
}
|
|
146
162
|
return Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_FINGERPRINT_HASH") as? String
|
|
147
163
|
}
|
|
148
164
|
|
|
@@ -106,6 +106,9 @@ public class SignatureVerifier {
|
|
|
106
106
|
* @return Public key PEM string or nil if not configured
|
|
107
107
|
*/
|
|
108
108
|
private static func getPublicKeyFromConfig() -> String? {
|
|
109
|
+
if let override = HotUpdaterConfig.shared.publicKey, !override.isEmpty {
|
|
110
|
+
return override
|
|
111
|
+
}
|
|
109
112
|
guard let publicKeyPEM = Bundle.main.object(forInfoDictionaryKey: "HOT_UPDATER_PUBLIC_KEY") as? String else {
|
|
110
113
|
NSLog("[SignatureVerifier] HOT_UPDATER_PUBLIC_KEY not found in Info.plist")
|
|
111
114
|
return nil
|
|
@@ -35,6 +35,15 @@
|
|
|
35
35
|
*/
|
|
36
36
|
- (NSURL *)bundleURLWithBundle:(NSBundle *)bundle NS_SWIFT_NAME(bundleURL(bundle:));
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Programmatic configuration for brownfield / XCFramework setups where
|
|
40
|
+
* Info.plist (host) or string resources (Android) are not desirable.
|
|
41
|
+
*/
|
|
42
|
+
+ (void)configureWithFingerprintHash:(nullable NSString *)fingerprintHash
|
|
43
|
+
publicKey:(nullable NSString *)publicKey
|
|
44
|
+
channel:(nullable NSString *)channel
|
|
45
|
+
isolationKey:(nullable NSString *)isolationKey;
|
|
46
|
+
|
|
38
47
|
/**
|
|
39
48
|
* 다운로드 진행 상황 업데이트 시간을 추적하는 속성
|
|
40
49
|
*/
|
|
@@ -4,5 +4,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.HOT_UPDATER_SDK_VERSION = void 0;
|
|
7
|
-
const HOT_UPDATER_SDK_VERSION = exports.HOT_UPDATER_SDK_VERSION = "0.
|
|
7
|
+
const HOT_UPDATER_SDK_VERSION = exports.HOT_UPDATER_SDK_VERSION = "0.34.0";
|
|
8
8
|
//# sourceMappingURL=sdkVersion.js.map
|
package/lib/module/sdkVersion.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const HOT_UPDATER_SDK_VERSION = "0.
|
|
1
|
+
export declare const HOT_UPDATER_SDK_VERSION = "0.34.0";
|
|
2
2
|
//# sourceMappingURL=sdkVersion.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const HOT_UPDATER_SDK_VERSION = "0.
|
|
1
|
+
export declare const HOT_UPDATER_SDK_VERSION = "0.34.0";
|
|
2
2
|
//# sourceMappingURL=sdkVersion.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/commonjs/index.js",
|
|
@@ -125,14 +125,14 @@
|
|
|
125
125
|
"react-native": "0.79.1",
|
|
126
126
|
"react-native-builder-bob": "0.41.0",
|
|
127
127
|
"typescript": "^6.0.2",
|
|
128
|
-
"hot-updater": "0.
|
|
128
|
+
"hot-updater": "0.34.0"
|
|
129
129
|
},
|
|
130
130
|
"dependencies": {
|
|
131
131
|
"use-sync-external-store": "1.5.0",
|
|
132
|
-
"@hot-updater/cli-tools": "0.
|
|
133
|
-
"@hot-updater/core": "0.
|
|
134
|
-
"@hot-updater/
|
|
135
|
-
"@hot-updater/
|
|
132
|
+
"@hot-updater/cli-tools": "0.34.0",
|
|
133
|
+
"@hot-updater/plugin-core": "0.34.0",
|
|
134
|
+
"@hot-updater/core": "0.34.0",
|
|
135
|
+
"@hot-updater/js": "0.34.0"
|
|
136
136
|
},
|
|
137
137
|
"scripts": {
|
|
138
138
|
"build": "node ./scripts/sync-sdk-version.mjs run -- bob build && tsc -p plugin/tsconfig.build.json",
|
package/src/sdkVersion.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const HOT_UPDATER_SDK_VERSION = "0.
|
|
1
|
+
export const HOT_UPDATER_SDK_VERSION = "0.34.0";
|