@hot-updater/react-native 0.36.7 → 1.0.0-rc.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 +357 -6
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +268 -34
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +75 -9
- package/android/src/main/java/com/hotupdater/NativeConfigUtils.kt +2 -16
- package/android/src/main/java/com/hotupdater/ReleaseCatalogCacheService.kt +166 -0
- package/android/src/main/java/com/hotupdater/SignatureVerifier.kt +1 -2
- package/android/src/newarch/HotUpdater.kt +1 -1
- package/android/src/newarch/HotUpdaterModule.kt +116 -0
- package/android/src/oldarch/HotUpdater.kt +1 -1
- package/android/src/oldarch/HotUpdaterModule.kt +128 -0
- package/android/src/oldarch/HotUpdaterSpec.kt +38 -0
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +280 -2
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +57 -2
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +118 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +491 -30
- package/ios/HotUpdater/Internal/BundleMetadata.swift +229 -6
- package/ios/HotUpdater/Internal/HotUpdater.mm +122 -0
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +176 -7
- package/ios/HotUpdater/Internal/ReleaseCatalogCacheService.swift +213 -0
- package/lib/commonjs/catalogCacheNative.js +44 -0
- package/lib/commonjs/catalogCacheNative.js.map +1 -0
- package/lib/commonjs/catalogCacheNative.spec.js +31 -0
- package/lib/commonjs/catalogCacheNative.spec.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +231 -60
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +604 -0
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/commonjs/error.js +8 -1
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/{fetchUpdateInfo.js → fetchJSON.js} +11 -14
- package/lib/commonjs/fetchJSON.js.map +1 -0
- package/lib/commonjs/httpClient.js +153 -0
- package/lib/commonjs/httpClient.js.map +1 -0
- package/lib/commonjs/httpClient.spec.js +102 -0
- package/lib/commonjs/httpClient.spec.js.map +1 -0
- package/lib/commonjs/index.js +48 -71
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +117 -161
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/iosInterop.spec.js +18 -0
- package/lib/commonjs/iosInterop.spec.js.map +1 -0
- package/lib/commonjs/native.js +222 -55
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +226 -12
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js +218 -0
- package/lib/commonjs/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js +133 -0
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js +246 -0
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js +24 -0
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js +117 -0
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +179 -0
- package/lib/commonjs/releaseCatalogCache.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.spec.js +195 -0
- package/lib/commonjs/releaseCatalogCache.spec.js.map +1 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +1 -0
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/types.js +0 -12
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/wrap.js +7 -82
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +203 -56
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/commonjs/wrap.types.js +6 -0
- package/lib/commonjs/wrap.types.js.map +1 -0
- package/lib/module/catalogCacheNative.js +36 -0
- package/lib/module/catalogCacheNative.js.map +1 -0
- package/lib/module/catalogCacheNative.spec.js +31 -0
- package/lib/module/catalogCacheNative.spec.js.map +1 -0
- package/lib/module/checkForUpdate.js +233 -62
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +603 -0
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -0
- package/lib/module/error.js +6 -0
- package/lib/module/error.js.map +1 -1
- package/lib/module/{fetchUpdateInfo.js → fetchJSON.js} +9 -12
- package/lib/module/fetchJSON.js.map +1 -0
- package/lib/module/httpClient.js +148 -0
- package/lib/module/httpClient.js.map +1 -0
- package/lib/module/httpClient.spec.js +102 -0
- package/lib/module/httpClient.spec.js.map +1 -0
- package/lib/module/index.js +49 -66
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +117 -161
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/iosInterop.spec.js +18 -0
- package/lib/module/iosInterop.spec.js.map +1 -0
- package/lib/module/native.js +206 -51
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +226 -12
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js +217 -0
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.js +128 -0
- package/lib/module/notifyAppReadyAnalytics.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js +245 -0
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js +18 -0
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +1 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js +116 -0
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +171 -0
- package/lib/module/releaseCatalogCache.js.map +1 -0
- package/lib/module/releaseCatalogCache.spec.js +195 -0
- package/lib/module/releaseCatalogCache.spec.js.map +1 -0
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +64 -0
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +1 -0
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/types.js +0 -12
- package/lib/module/types.js.map +1 -1
- package/lib/module/wrap.js +6 -81
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +203 -56
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/module/wrap.types.js +4 -0
- package/lib/module/wrap.types.js.map +1 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/commonjs/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +14 -4
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +3 -0
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +6 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +56 -0
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +32 -41
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +83 -18
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -0
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +40 -5
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +0 -118
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.d.ts +2 -191
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +65 -0
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -0
- package/lib/typescript/module/catalogCacheNative.d.ts +4 -0
- package/lib/typescript/module/catalogCacheNative.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +14 -4
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +3 -0
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +6 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +56 -0
- package/lib/typescript/module/httpClient.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +32 -41
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +83 -18
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +13 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +25 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -0
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +40 -5
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +0 -118
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/wrap.d.ts +2 -191
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +65 -0
- package/lib/typescript/module/wrap.types.d.ts.map +1 -0
- package/package.json +6 -24
- package/src/catalogCacheNative.spec.ts +63 -0
- package/src/catalogCacheNative.ts +74 -0
- package/src/checkForUpdate.releaseCatalog.spec.ts +596 -0
- package/src/checkForUpdate.ts +368 -97
- package/src/error.ts +7 -0
- package/src/{fetchUpdateInfo.ts → fetchJSON.ts} +8 -15
- package/src/httpClient.spec.ts +132 -0
- package/src/httpClient.ts +261 -0
- package/src/index.spec.ts +110 -203
- package/src/index.ts +70 -93
- package/src/iosInterop.spec.ts +30 -0
- package/src/native.spec.ts +226 -10
- package/src/native.ts +385 -83
- package/src/notifyAppReadyAnalytics.boundary.spec.ts +234 -0
- package/src/notifyAppReadyAnalytics.spec.ts +243 -0
- package/src/notifyAppReadyAnalytics.test-utils.ts +31 -0
- package/src/notifyAppReadyAnalytics.transport.spec.ts +130 -0
- package/src/notifyAppReadyAnalytics.ts +194 -0
- package/src/releaseCatalogCache.spec.ts +282 -0
- package/src/releaseCatalogCache.ts +341 -0
- package/src/releaseCatalogCacheNativeContract.spec.ts +80 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +51 -5
- package/src/types.ts +0 -140
- package/src/wrap.spec.ts +251 -56
- package/src/wrap.tsx +23 -318
- package/src/wrap.types.ts +78 -0
- package/android/src/main/java/com/hotupdater/StringResourceUtils.kt +0 -45
- package/app.plugin.js +0 -1
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.h +0 -7
- package/ios/HotUpdater/Internal/HotUpdaterCrashHandler.mm +0 -4
- package/lib/commonjs/DefaultResolver.js +0 -50
- package/lib/commonjs/DefaultResolver.js.map +0 -1
- package/lib/commonjs/DefaultResolver.spec.js +0 -144
- package/lib/commonjs/DefaultResolver.spec.js.map +0 -1
- package/lib/commonjs/fetchUpdateInfo.js.map +0 -1
- package/lib/module/DefaultResolver.js +0 -46
- package/lib/module/DefaultResolver.js.map +0 -1
- package/lib/module/DefaultResolver.spec.js +0 -144
- package/lib/module/DefaultResolver.spec.js.map +0 -1
- package/lib/module/fetchUpdateInfo.js.map +0 -1
- package/lib/typescript/commonjs/DefaultResolver.d.ts +0 -11
- package/lib/typescript/commonjs/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/commonjs/fetchUpdateInfo.d.ts.map +0 -1
- package/lib/typescript/module/DefaultResolver.d.ts +0 -11
- package/lib/typescript/module/DefaultResolver.d.ts.map +0 -1
- package/lib/typescript/module/fetchUpdateInfo.d.ts +0 -7
- package/lib/typescript/module/fetchUpdateInfo.d.ts.map +0 -1
- package/plugin/build/transformers.js +0 -314
- package/plugin/build/withHotUpdater.js +0 -228
- package/src/DefaultResolver.spec.ts +0 -193
- package/src/DefaultResolver.ts +0 -63
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
import android.util.AtomicFile
|
|
4
|
+
import java.io.File
|
|
5
|
+
import java.io.FileOutputStream
|
|
6
|
+
import java.nio.charset.StandardCharsets
|
|
7
|
+
import java.security.MessageDigest
|
|
8
|
+
|
|
9
|
+
internal class ReleaseCatalogCacheService(
|
|
10
|
+
private val rootDirectory: File,
|
|
11
|
+
private val maxEntries: Int = DEFAULT_MAX_ENTRIES,
|
|
12
|
+
private val maxEntryBytes: Int = DEFAULT_MAX_ENTRY_BYTES,
|
|
13
|
+
private val maxTotalBytes: Long = DEFAULT_MAX_TOTAL_BYTES,
|
|
14
|
+
private val atomicReader: (File) -> ByteArray? = ::readAtomically,
|
|
15
|
+
private val atomicWriter: (File, ByteArray) -> Boolean = ::writeAtomically,
|
|
16
|
+
private val atomicRemover: (File) -> Boolean = ::removeAtomically,
|
|
17
|
+
) {
|
|
18
|
+
companion object {
|
|
19
|
+
private const val DIGEST_BYTES = 64
|
|
20
|
+
private const val MAX_COMPILED_CATALOG_BYTES = 256 * 1024
|
|
21
|
+
private const val MAX_WIRE_ENVELOPE_BYTES = 4 * 1024
|
|
22
|
+
private const val MAX_ETAG_BYTES = 1024
|
|
23
|
+
private const val CACHE_FORMAT_BYTES = 3
|
|
24
|
+
const val DEFAULT_MAX_ENTRIES = 8
|
|
25
|
+
const val DEFAULT_MAX_ENTRY_BYTES =
|
|
26
|
+
MAX_COMPILED_CATALOG_BYTES + MAX_WIRE_ENVELOPE_BYTES + MAX_ETAG_BYTES + CACHE_FORMAT_BYTES
|
|
27
|
+
const val DEFAULT_MAX_TOTAL_BYTES =
|
|
28
|
+
DEFAULT_MAX_ENTRIES.toLong() * (DEFAULT_MAX_ENTRY_BYTES + DIGEST_BYTES + 1)
|
|
29
|
+
private const val ENTRY_SUFFIX = ".catalog"
|
|
30
|
+
|
|
31
|
+
private fun readAtomically(file: File): ByteArray? =
|
|
32
|
+
try {
|
|
33
|
+
val atomicFile = AtomicFile(file)
|
|
34
|
+
atomicFile.openRead().use { input -> input.readBytes() }
|
|
35
|
+
} catch (_: Exception) {
|
|
36
|
+
null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private fun removeAtomically(file: File): Boolean {
|
|
40
|
+
val atomicFile = AtomicFile(file)
|
|
41
|
+
atomicFile.delete()
|
|
42
|
+
return !file.exists() &&
|
|
43
|
+
!File("${file.path}.bak").exists() &&
|
|
44
|
+
!File("${file.path}.new").exists()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun writeAtomically(
|
|
48
|
+
destination: File,
|
|
49
|
+
bytes: ByteArray,
|
|
50
|
+
): Boolean {
|
|
51
|
+
val atomicFile = AtomicFile(destination)
|
|
52
|
+
var output: FileOutputStream? = null
|
|
53
|
+
return try {
|
|
54
|
+
output = atomicFile.startWrite()
|
|
55
|
+
output.write(bytes)
|
|
56
|
+
output.fd.sync()
|
|
57
|
+
atomicFile.finishWrite(output)
|
|
58
|
+
true
|
|
59
|
+
} catch (_: Exception) {
|
|
60
|
+
output?.let(atomicFile::failWrite)
|
|
61
|
+
false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
init {
|
|
67
|
+
recoverInterruptedWrites()
|
|
68
|
+
trim()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Synchronized
|
|
72
|
+
fun get(partition: String): String? {
|
|
73
|
+
val file = entryFile(partition)
|
|
74
|
+
return try {
|
|
75
|
+
val stored = atomicReader(file) ?: return null
|
|
76
|
+
if (stored.size > maxEntryBytes + DIGEST_BYTES + 1) {
|
|
77
|
+
atomicRemover(file)
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
val separator = stored.indexOf('\n'.code.toByte())
|
|
81
|
+
if (separator != DIGEST_BYTES) {
|
|
82
|
+
atomicRemover(file)
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
val expectedDigest = String(stored, 0, separator, StandardCharsets.US_ASCII)
|
|
86
|
+
val payload = stored.copyOfRange(separator + 1, stored.size)
|
|
87
|
+
if (payload.size > maxEntryBytes || sha256(payload) != expectedDigest) {
|
|
88
|
+
atomicRemover(file)
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
file.setLastModified(System.currentTimeMillis())
|
|
93
|
+
String(payload, StandardCharsets.UTF_8)
|
|
94
|
+
} catch (_: Exception) {
|
|
95
|
+
atomicRemover(file)
|
|
96
|
+
null
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Synchronized
|
|
101
|
+
fun set(
|
|
102
|
+
partition: String,
|
|
103
|
+
value: String,
|
|
104
|
+
): Boolean {
|
|
105
|
+
val payload = value.toByteArray(StandardCharsets.UTF_8)
|
|
106
|
+
if (payload.size > maxEntryBytes) return false
|
|
107
|
+
if (!rootDirectory.exists() && !rootDirectory.mkdirs()) return false
|
|
108
|
+
|
|
109
|
+
val destination = entryFile(partition)
|
|
110
|
+
return try {
|
|
111
|
+
val stored =
|
|
112
|
+
sha256(payload).toByteArray(StandardCharsets.US_ASCII) +
|
|
113
|
+
byteArrayOf('\n'.code.toByte()) +
|
|
114
|
+
payload
|
|
115
|
+
if (!atomicWriter(destination, stored)) return false
|
|
116
|
+
destination.setLastModified(System.currentTimeMillis())
|
|
117
|
+
trim()
|
|
118
|
+
true
|
|
119
|
+
} catch (_: Exception) {
|
|
120
|
+
false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Synchronized
|
|
125
|
+
fun remove(partition: String): Boolean = atomicRemover(entryFile(partition))
|
|
126
|
+
|
|
127
|
+
private fun entryFile(partition: String): File =
|
|
128
|
+
File(rootDirectory, "${sha256(partition.toByteArray(StandardCharsets.UTF_8))}$ENTRY_SUFFIX")
|
|
129
|
+
|
|
130
|
+
private fun recoverInterruptedWrites() {
|
|
131
|
+
rootDirectory.listFiles()?.forEach { file ->
|
|
132
|
+
when {
|
|
133
|
+
file.name.endsWith("$ENTRY_SUFFIX.bak") -> {
|
|
134
|
+
val destination = File(file.parentFile, file.name.removeSuffix(".bak"))
|
|
135
|
+
if (atomicReader(destination) == null) atomicRemover(destination)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
file.name.endsWith("$ENTRY_SUFFIX.new") -> {
|
|
139
|
+
val destination = File(file.parentFile, file.name.removeSuffix(".new"))
|
|
140
|
+
if (atomicReader(destination) == null) atomicRemover(destination)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun trim() {
|
|
147
|
+
val entries =
|
|
148
|
+
rootDirectory
|
|
149
|
+
.listFiles { file -> file.isFile && file.name.endsWith(ENTRY_SUFFIX) }
|
|
150
|
+
?.sortedByDescending { it.lastModified() }
|
|
151
|
+
.orEmpty()
|
|
152
|
+
var retainedBytes = 0L
|
|
153
|
+
entries.forEachIndexed { index, file ->
|
|
154
|
+
retainedBytes += file.length()
|
|
155
|
+
if (index >= maxEntries || retainedBytes > maxTotalBytes) {
|
|
156
|
+
atomicRemover(file)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private fun sha256(value: ByteArray): String =
|
|
162
|
+
MessageDigest
|
|
163
|
+
.getInstance("SHA-256")
|
|
164
|
+
.digest(value)
|
|
165
|
+
.joinToString("") { byte -> "%02x".format(byte.toInt() and 0xff) }
|
|
166
|
+
}
|
|
@@ -92,7 +92,7 @@ object SignatureVerifier {
|
|
|
92
92
|
private const val TAG = "SignatureVerifier"
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
* Reads public key from
|
|
95
|
+
* Reads public key from AndroidManifest metadata.
|
|
96
96
|
* @param context Application context
|
|
97
97
|
* @return Public key PEM string or null if not configured
|
|
98
98
|
*/
|
|
@@ -101,7 +101,6 @@ object SignatureVerifier {
|
|
|
101
101
|
NativeConfigUtils.getString(
|
|
102
102
|
context,
|
|
103
103
|
NativeConfigUtils.PUBLIC_KEY_META_DATA_KEY,
|
|
104
|
-
"hot_updater_public_key",
|
|
105
104
|
)
|
|
106
105
|
|
|
107
106
|
if (publicKeyPEM.isNullOrEmpty()) {
|
|
@@ -107,6 +107,27 @@ class HotUpdaterModule internal constructor(
|
|
|
107
107
|
return parsedAssets
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
private fun parseSelection(params: ReadableMap): PersistedSelection? {
|
|
111
|
+
if (!params.hasKey("selection") || params.isNull("selection")) return null
|
|
112
|
+
val selection = params.getMap("selection") ?: return null
|
|
113
|
+
return PersistedSelection(
|
|
114
|
+
kind = selection.getString("kind") ?: return null,
|
|
115
|
+
releaseId = selection.getString("releaseId"),
|
|
116
|
+
bundleId = selection.getString("bundleId") ?: return null,
|
|
117
|
+
catalogId = selection.getString("catalogId"),
|
|
118
|
+
scopeKey = selection.getString("scopeKey"),
|
|
119
|
+
generation =
|
|
120
|
+
if (selection.hasKey("generation") && !selection.isNull("generation")) {
|
|
121
|
+
selection.getDouble("generation").toLong()
|
|
122
|
+
} else {
|
|
123
|
+
null
|
|
124
|
+
},
|
|
125
|
+
catalogHash = selection.getString("catalogHash"),
|
|
126
|
+
channel = selection.getString("channel") ?: "",
|
|
127
|
+
selectionContextHash = selection.getString("selectionContextHash"),
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
override fun reload(promise: Promise) {
|
|
111
132
|
moduleScope.launch {
|
|
112
133
|
try {
|
|
@@ -162,6 +183,7 @@ class HotUpdaterModule internal constructor(
|
|
|
162
183
|
val manifestFileHash = params.getString("manifestFileHash")
|
|
163
184
|
val changedAssets = parseChangedAssets(params)
|
|
164
185
|
val channel = params.getString("channel")
|
|
186
|
+
val selection = parseSelection(params)
|
|
165
187
|
|
|
166
188
|
val impl = getInstance()
|
|
167
189
|
|
|
@@ -173,6 +195,7 @@ class HotUpdaterModule internal constructor(
|
|
|
173
195
|
manifestFileHash,
|
|
174
196
|
changedAssets,
|
|
175
197
|
channel,
|
|
198
|
+
selection,
|
|
176
199
|
) { progress ->
|
|
177
200
|
// Post to Main thread for React Native event emission
|
|
178
201
|
Handler(Looper.getMainLooper()).post {
|
|
@@ -235,6 +258,86 @@ class HotUpdaterModule internal constructor(
|
|
|
235
258
|
}
|
|
236
259
|
}
|
|
237
260
|
|
|
261
|
+
override fun acceptReleaseCatalog(params: ReadableMap): Boolean {
|
|
262
|
+
val catalogId = params.getString("catalogId") ?: return false
|
|
263
|
+
val scopeKey = params.getString("scopeKey") ?: return false
|
|
264
|
+
val catalogHash = params.getString("catalogHash") ?: return false
|
|
265
|
+
val channel = params.getString("channel") ?: return false
|
|
266
|
+
val selectionContextHash = params.getString("selectionContextHash") ?: return false
|
|
267
|
+
return getInstance().acceptReleaseCatalog(
|
|
268
|
+
catalogId = catalogId,
|
|
269
|
+
scopeKey = scopeKey,
|
|
270
|
+
generation = params.getDouble("generation").toLong(),
|
|
271
|
+
catalogHash = catalogHash,
|
|
272
|
+
channel = channel,
|
|
273
|
+
selectionContextHash = selectionContextHash,
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
override fun getActiveUpdateState(): WritableNativeMap = getInstance().getActiveUpdateState().toWritableNativeMap()
|
|
278
|
+
|
|
279
|
+
override fun getReleaseCatalogCache(
|
|
280
|
+
partition: String,
|
|
281
|
+
promise: Promise,
|
|
282
|
+
) {
|
|
283
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
284
|
+
promise.resolve(getInstance().getReleaseCatalogCache(partition))
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
override fun setReleaseCatalogCache(
|
|
289
|
+
partition: String,
|
|
290
|
+
payload: String,
|
|
291
|
+
promise: Promise,
|
|
292
|
+
) {
|
|
293
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
294
|
+
promise.resolve(getInstance().setReleaseCatalogCache(partition, payload))
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
override fun removeReleaseCatalogCache(
|
|
299
|
+
partition: String,
|
|
300
|
+
promise: Promise,
|
|
301
|
+
) {
|
|
302
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
303
|
+
promise.resolve(getInstance().removeReleaseCatalogCache(partition))
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
override fun isReleaseSelectionCurrent(params: ReadableMap): Boolean {
|
|
308
|
+
val catalogId = params.getString("catalogId") ?: return false
|
|
309
|
+
val scopeKey = params.getString("scopeKey") ?: return false
|
|
310
|
+
val catalogHash = params.getString("catalogHash") ?: return false
|
|
311
|
+
val channel = params.getString("channel") ?: return false
|
|
312
|
+
val selectionContextHash = params.getString("selectionContextHash") ?: return false
|
|
313
|
+
return getInstance().isReleaseSelectionCurrent(
|
|
314
|
+
catalogId = catalogId,
|
|
315
|
+
scopeKey = scopeKey,
|
|
316
|
+
generation = params.getDouble("generation").toLong(),
|
|
317
|
+
catalogHash = catalogHash,
|
|
318
|
+
channel = channel,
|
|
319
|
+
selectionContextHash = selectionContextHash,
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
override fun commitReleaseSelection(
|
|
324
|
+
params: ReadableMap,
|
|
325
|
+
promise: Promise,
|
|
326
|
+
) {
|
|
327
|
+
val selection = parseSelection(params)
|
|
328
|
+
if (selection == null) {
|
|
329
|
+
promise.reject("INVALID_SELECTION", "Invalid Release selection")
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
moduleScope.launch {
|
|
333
|
+
try {
|
|
334
|
+
promise.resolve(getInstance().commitReleaseSelection(selection))
|
|
335
|
+
} catch (error: Exception) {
|
|
336
|
+
promise.reject("UNKNOWN_ERROR", error.message, error)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
238
341
|
override fun getTypedExportedConstants(): Map<String, Any?> {
|
|
239
342
|
val constants: MutableMap<String, Any?> = HashMap()
|
|
240
343
|
constants["MIN_BUNDLE_ID"] = HotUpdater.getMinBundleId()
|
|
@@ -284,6 +387,19 @@ class HotUpdaterModule internal constructor(
|
|
|
284
387
|
|
|
285
388
|
override fun getManifest(): WritableNativeMap = getInstance().getManifest().toWritableNativeMap()
|
|
286
389
|
|
|
390
|
+
override fun getInstallId(): String = getInstance().getInstallId()
|
|
391
|
+
|
|
392
|
+
override fun getUserId(): String? = getInstance().getUserId()
|
|
393
|
+
|
|
394
|
+
override fun getUsername(): String? = getInstance().getUsername()
|
|
395
|
+
|
|
396
|
+
override fun setUser(
|
|
397
|
+
userId: String?,
|
|
398
|
+
username: String?,
|
|
399
|
+
) {
|
|
400
|
+
getInstance().setUser(userId, username)
|
|
401
|
+
}
|
|
402
|
+
|
|
287
403
|
override fun resetChannel(promise: Promise) {
|
|
288
404
|
moduleScope.launch {
|
|
289
405
|
try {
|
|
@@ -107,6 +107,28 @@ class HotUpdaterModule internal constructor(
|
|
|
107
107
|
return parsedAssets
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
private fun parseSelection(params: ReadableMap): PersistedSelection? {
|
|
111
|
+
if (!params.hasKey("selection") || params.isNull("selection")) return null
|
|
112
|
+
val selection = params.getMap("selection") ?: return null
|
|
113
|
+
val bundleId = selection.getString("bundleId") ?: return null
|
|
114
|
+
return PersistedSelection(
|
|
115
|
+
kind = selection.getString("kind") ?: return null,
|
|
116
|
+
releaseId = selection.getString("releaseId"),
|
|
117
|
+
bundleId = bundleId,
|
|
118
|
+
catalogId = selection.getString("catalogId"),
|
|
119
|
+
scopeKey = selection.getString("scopeKey"),
|
|
120
|
+
generation =
|
|
121
|
+
if (selection.hasKey("generation") && !selection.isNull("generation")) {
|
|
122
|
+
selection.getDouble("generation").toLong()
|
|
123
|
+
} else {
|
|
124
|
+
null
|
|
125
|
+
},
|
|
126
|
+
catalogHash = selection.getString("catalogHash"),
|
|
127
|
+
channel = selection.getString("channel") ?: "",
|
|
128
|
+
selectionContextHash = selection.getString("selectionContextHash"),
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
110
132
|
@ReactMethod
|
|
111
133
|
override fun reload(promise: Promise) {
|
|
112
134
|
moduleScope.launch {
|
|
@@ -165,6 +187,7 @@ class HotUpdaterModule internal constructor(
|
|
|
165
187
|
val manifestFileHash = params.getString("manifestFileHash")
|
|
166
188
|
val changedAssets = parseChangedAssets(params)
|
|
167
189
|
val channel = params.getString("channel")
|
|
190
|
+
val selection = parseSelection(params)
|
|
168
191
|
|
|
169
192
|
val impl = getInstance()
|
|
170
193
|
|
|
@@ -176,6 +199,7 @@ class HotUpdaterModule internal constructor(
|
|
|
176
199
|
manifestFileHash,
|
|
177
200
|
changedAssets,
|
|
178
201
|
channel,
|
|
202
|
+
selection,
|
|
179
203
|
) { progress ->
|
|
180
204
|
// Post to Main thread for React Native event emission
|
|
181
205
|
Handler(Looper.getMainLooper()).post {
|
|
@@ -231,6 +255,93 @@ class HotUpdaterModule internal constructor(
|
|
|
231
255
|
}
|
|
232
256
|
}
|
|
233
257
|
|
|
258
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
259
|
+
override fun acceptReleaseCatalog(params: ReadableMap): Boolean {
|
|
260
|
+
val catalogId = params.getString("catalogId") ?: return false
|
|
261
|
+
val scopeKey = params.getString("scopeKey") ?: return false
|
|
262
|
+
val catalogHash = params.getString("catalogHash") ?: return false
|
|
263
|
+
val channel = params.getString("channel") ?: return false
|
|
264
|
+
val selectionContextHash = params.getString("selectionContextHash") ?: return false
|
|
265
|
+
return getInstance().acceptReleaseCatalog(
|
|
266
|
+
catalogId = catalogId,
|
|
267
|
+
scopeKey = scopeKey,
|
|
268
|
+
generation = params.getDouble("generation").toLong(),
|
|
269
|
+
catalogHash = catalogHash,
|
|
270
|
+
channel = channel,
|
|
271
|
+
selectionContextHash = selectionContextHash,
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
276
|
+
override fun getActiveUpdateState(): String = org.json.JSONObject(getInstance().getActiveUpdateState()).toString()
|
|
277
|
+
|
|
278
|
+
@ReactMethod
|
|
279
|
+
override fun getReleaseCatalogCache(
|
|
280
|
+
partition: String,
|
|
281
|
+
promise: Promise,
|
|
282
|
+
) {
|
|
283
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
284
|
+
promise.resolve(getInstance().getReleaseCatalogCache(partition))
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@ReactMethod
|
|
289
|
+
override fun setReleaseCatalogCache(
|
|
290
|
+
partition: String,
|
|
291
|
+
payload: String,
|
|
292
|
+
promise: Promise,
|
|
293
|
+
) {
|
|
294
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
295
|
+
promise.resolve(getInstance().setReleaseCatalogCache(partition, payload))
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@ReactMethod
|
|
300
|
+
override fun removeReleaseCatalogCache(
|
|
301
|
+
partition: String,
|
|
302
|
+
promise: Promise,
|
|
303
|
+
) {
|
|
304
|
+
moduleScope.launch(Dispatchers.IO) {
|
|
305
|
+
promise.resolve(getInstance().removeReleaseCatalogCache(partition))
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
310
|
+
override fun isReleaseSelectionCurrent(params: ReadableMap): Boolean {
|
|
311
|
+
val catalogId = params.getString("catalogId") ?: return false
|
|
312
|
+
val scopeKey = params.getString("scopeKey") ?: return false
|
|
313
|
+
val catalogHash = params.getString("catalogHash") ?: return false
|
|
314
|
+
val channel = params.getString("channel") ?: return false
|
|
315
|
+
val selectionContextHash = params.getString("selectionContextHash") ?: return false
|
|
316
|
+
return getInstance().isReleaseSelectionCurrent(
|
|
317
|
+
catalogId = catalogId,
|
|
318
|
+
scopeKey = scopeKey,
|
|
319
|
+
generation = params.getDouble("generation").toLong(),
|
|
320
|
+
catalogHash = catalogHash,
|
|
321
|
+
channel = channel,
|
|
322
|
+
selectionContextHash = selectionContextHash,
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@ReactMethod
|
|
327
|
+
override fun commitReleaseSelection(
|
|
328
|
+
params: ReadableMap,
|
|
329
|
+
promise: Promise,
|
|
330
|
+
) {
|
|
331
|
+
val selection = parseSelection(params)
|
|
332
|
+
if (selection == null) {
|
|
333
|
+
promise.reject("INVALID_SELECTION", "Invalid Release selection")
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
moduleScope.launch {
|
|
337
|
+
try {
|
|
338
|
+
promise.resolve(getInstance().commitReleaseSelection(selection))
|
|
339
|
+
} catch (error: Exception) {
|
|
340
|
+
promise.reject("UNKNOWN_ERROR", error.message, error)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
234
345
|
@ReactMethod
|
|
235
346
|
fun addListener(
|
|
236
347
|
@Suppress("UNUSED_PARAMETER") eventName: String?,
|
|
@@ -299,6 +410,23 @@ class HotUpdaterModule internal constructor(
|
|
|
299
410
|
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
300
411
|
override fun getCohort(): String = cohortService.getCohort()
|
|
301
412
|
|
|
413
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
414
|
+
override fun getInstallId(): String = getInstance().getInstallId()
|
|
415
|
+
|
|
416
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
417
|
+
override fun getUserId(): String? = getInstance().getUserId()
|
|
418
|
+
|
|
419
|
+
@ReactMethod(isBlockingSynchronousMethod = true)
|
|
420
|
+
override fun getUsername(): String? = getInstance().getUsername()
|
|
421
|
+
|
|
422
|
+
@ReactMethod
|
|
423
|
+
override fun setUser(
|
|
424
|
+
userId: String?,
|
|
425
|
+
username: String?,
|
|
426
|
+
) {
|
|
427
|
+
getInstance().setUser(userId, username)
|
|
428
|
+
}
|
|
429
|
+
|
|
302
430
|
@ReactMethod
|
|
303
431
|
override fun resetChannel(promise: Promise) {
|
|
304
432
|
moduleScope.launch {
|
|
@@ -13,6 +13,33 @@ abstract class HotUpdaterSpec internal constructor(
|
|
|
13
13
|
promise: Promise,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
+
abstract fun acceptReleaseCatalog(params: ReadableMap): Boolean
|
|
17
|
+
|
|
18
|
+
abstract fun getActiveUpdateState(): String
|
|
19
|
+
|
|
20
|
+
abstract fun getReleaseCatalogCache(
|
|
21
|
+
partition: String,
|
|
22
|
+
promise: Promise,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
abstract fun setReleaseCatalogCache(
|
|
26
|
+
partition: String,
|
|
27
|
+
payload: String,
|
|
28
|
+
promise: Promise,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
abstract fun removeReleaseCatalogCache(
|
|
32
|
+
partition: String,
|
|
33
|
+
promise: Promise,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
abstract fun isReleaseSelectionCurrent(params: ReadableMap): Boolean
|
|
37
|
+
|
|
38
|
+
abstract fun commitReleaseSelection(
|
|
39
|
+
params: ReadableMap,
|
|
40
|
+
promise: Promise,
|
|
41
|
+
)
|
|
42
|
+
|
|
16
43
|
abstract fun reload(promise: Promise)
|
|
17
44
|
|
|
18
45
|
abstract fun reloadProcess(promise: Promise)
|
|
@@ -33,5 +60,16 @@ abstract class HotUpdaterSpec internal constructor(
|
|
|
33
60
|
|
|
34
61
|
abstract fun getManifest(): String
|
|
35
62
|
|
|
63
|
+
abstract fun getInstallId(): String
|
|
64
|
+
|
|
65
|
+
abstract fun getUserId(): String?
|
|
66
|
+
|
|
67
|
+
abstract fun getUsername(): String?
|
|
68
|
+
|
|
69
|
+
abstract fun setUser(
|
|
70
|
+
userId: String?,
|
|
71
|
+
username: String?,
|
|
72
|
+
)
|
|
73
|
+
|
|
36
74
|
abstract fun resetChannel(promise: Promise)
|
|
37
75
|
}
|