@hot-updater/react-native 1.0.0-rc.17 → 1.0.0-rc.21

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.
Files changed (240) hide show
  1. package/lib/commonjs/checkForUpdate.js +7 -9
  2. package/lib/commonjs/checkForUpdate.js.map +1 -1
  3. package/lib/commonjs/clientPlugin.js +7 -43
  4. package/lib/commonjs/clientPlugin.js.map +1 -1
  5. package/lib/commonjs/httpClient.js +7 -9
  6. package/lib/commonjs/httpClient.js.map +1 -1
  7. package/lib/commonjs/index.js +10 -0
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/native.js +4 -4
  10. package/lib/commonjs/native.js.map +1 -1
  11. package/lib/commonjs/pluginHost.js +26 -190
  12. package/lib/commonjs/pluginHost.js.map +1 -1
  13. package/lib/commonjs/releaseCatalogCache.js +7 -59
  14. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  15. package/lib/commonjs/sdkVersion.js +1 -1
  16. package/lib/module/checkForUpdate.js +1 -3
  17. package/lib/module/checkForUpdate.js.map +1 -1
  18. package/lib/module/clientPlugin.js +1 -41
  19. package/lib/module/clientPlugin.js.map +1 -1
  20. package/lib/module/httpClient.js +1 -3
  21. package/lib/module/httpClient.js.map +1 -1
  22. package/lib/module/index.js +2 -0
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/native.js +1 -1
  25. package/lib/module/native.js.map +1 -1
  26. package/lib/module/pluginHost.js +22 -187
  27. package/lib/module/pluginHost.js.map +1 -1
  28. package/lib/module/releaseCatalogCache.js +3 -54
  29. package/lib/module/releaseCatalogCache.js.map +1 -1
  30. package/lib/module/sdkVersion.js +1 -1
  31. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/clientPlugin.d.ts +1 -184
  33. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/httpClient.d.ts +1 -2
  35. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/index.d.ts +1 -0
  37. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/native.d.ts +1 -1
  39. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/pluginHost.d.ts +3 -28
  41. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +1 -13
  43. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  45. package/lib/typescript/commonjs/types.d.ts +1 -1
  46. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/wrap.types.d.ts +2 -2
  48. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  49. package/lib/typescript/module/clientPlugin.d.ts +1 -184
  50. package/lib/typescript/module/clientPlugin.d.ts.map +1 -1
  51. package/lib/typescript/module/httpClient.d.ts +1 -2
  52. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  53. package/lib/typescript/module/index.d.ts +1 -0
  54. package/lib/typescript/module/index.d.ts.map +1 -1
  55. package/lib/typescript/module/native.d.ts +1 -1
  56. package/lib/typescript/module/native.d.ts.map +1 -1
  57. package/lib/typescript/module/pluginHost.d.ts +3 -28
  58. package/lib/typescript/module/pluginHost.d.ts.map +1 -1
  59. package/lib/typescript/module/releaseCatalogCache.d.ts +1 -13
  60. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  61. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  62. package/lib/typescript/module/types.d.ts +1 -1
  63. package/lib/typescript/module/types.d.ts.map +1 -1
  64. package/lib/typescript/module/wrap.types.d.ts +2 -2
  65. package/package.json +7 -16
  66. package/src/checkForUpdate.ts +4 -6
  67. package/src/clientPlugin.ts +17 -231
  68. package/src/httpClient.ts +4 -4
  69. package/src/index.ts +7 -0
  70. package/src/native.ts +1 -1
  71. package/src/pluginHost.ts +24 -241
  72. package/src/releaseCatalogCache.ts +7 -136
  73. package/src/sdkVersion.ts +1 -1
  74. package/src/types.ts +1 -1
  75. package/src/wrap.types.ts +2 -2
  76. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  77. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -2236
  78. package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +0 -220
  79. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -264
  80. package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +0 -97
  81. package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +0 -71
  82. package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +0 -106
  83. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  84. package/android/src/test/java/com/hotupdater/NoBackupContext.kt +0 -11
  85. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -397
  86. package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +0 -44
  87. package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +0 -57
  88. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  89. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  90. package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +0 -206
  91. package/lib/commonjs/appReady.spec.js +0 -360
  92. package/lib/commonjs/appReady.spec.js.map +0 -1
  93. package/lib/commonjs/appReady.test-utils.js +0 -25
  94. package/lib/commonjs/appReady.test-utils.js.map +0 -1
  95. package/lib/commonjs/baseURL.js +0 -16
  96. package/lib/commonjs/baseURL.js.map +0 -1
  97. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  98. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  99. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -936
  100. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  101. package/lib/commonjs/entryImportGraph.spec.js +0 -50
  102. package/lib/commonjs/entryImportGraph.spec.js.map +0 -1
  103. package/lib/commonjs/httpClient.spec.js +0 -171
  104. package/lib/commonjs/httpClient.spec.js.map +0 -1
  105. package/lib/commonjs/index.spec.js +0 -395
  106. package/lib/commonjs/index.spec.js.map +0 -1
  107. package/lib/commonjs/iosInterop.spec.js +0 -18
  108. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  109. package/lib/commonjs/native.spec.js +0 -910
  110. package/lib/commonjs/native.spec.js.map +0 -1
  111. package/lib/commonjs/pluginHost.spec.js +0 -292
  112. package/lib/commonjs/pluginHost.spec.js.map +0 -1
  113. package/lib/commonjs/plugins/insights/eventId.js +0 -129
  114. package/lib/commonjs/plugins/insights/eventId.js.map +0 -1
  115. package/lib/commonjs/plugins/insights/eventId.spec.js +0 -47
  116. package/lib/commonjs/plugins/insights/eventId.spec.js.map +0 -1
  117. package/lib/commonjs/plugins/insights/index.js +0 -272
  118. package/lib/commonjs/plugins/insights/index.js.map +0 -1
  119. package/lib/commonjs/plugins/insights/insights.spec.js +0 -640
  120. package/lib/commonjs/plugins/insights/insights.spec.js.map +0 -1
  121. package/lib/commonjs/plugins/insights/sender.js +0 -128
  122. package/lib/commonjs/plugins/insights/sender.js.map +0 -1
  123. package/lib/commonjs/plugins/insights/sender.spec.js +0 -265
  124. package/lib/commonjs/plugins/insights/sender.spec.js.map +0 -1
  125. package/lib/commonjs/plugins/insights/state.js +0 -145
  126. package/lib/commonjs/plugins/insights/state.js.map +0 -1
  127. package/lib/commonjs/releaseCatalogCache.spec.js +0 -213
  128. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  129. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  130. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  131. package/lib/commonjs/store.notification.spec.js +0 -84
  132. package/lib/commonjs/store.notification.spec.js.map +0 -1
  133. package/lib/commonjs/store.spec.js +0 -182
  134. package/lib/commonjs/store.spec.js.map +0 -1
  135. package/lib/commonjs/updateError.spec.js +0 -149
  136. package/lib/commonjs/updateError.spec.js.map +0 -1
  137. package/lib/commonjs/wrap.progress.spec.js +0 -195
  138. package/lib/commonjs/wrap.progress.spec.js.map +0 -1
  139. package/lib/commonjs/wrap.spec.js +0 -232
  140. package/lib/commonjs/wrap.spec.js.map +0 -1
  141. package/lib/module/appReady.spec.js +0 -359
  142. package/lib/module/appReady.spec.js.map +0 -1
  143. package/lib/module/appReady.test-utils.js +0 -19
  144. package/lib/module/appReady.test-utils.js.map +0 -1
  145. package/lib/module/baseURL.js +0 -11
  146. package/lib/module/baseURL.js.map +0 -1
  147. package/lib/module/catalogCacheNative.spec.js +0 -31
  148. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  149. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -935
  150. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  151. package/lib/module/entryImportGraph.spec.js +0 -50
  152. package/lib/module/entryImportGraph.spec.js.map +0 -1
  153. package/lib/module/httpClient.spec.js +0 -171
  154. package/lib/module/httpClient.spec.js.map +0 -1
  155. package/lib/module/index.spec.js +0 -394
  156. package/lib/module/index.spec.js.map +0 -1
  157. package/lib/module/iosInterop.spec.js +0 -18
  158. package/lib/module/iosInterop.spec.js.map +0 -1
  159. package/lib/module/native.spec.js +0 -909
  160. package/lib/module/native.spec.js.map +0 -1
  161. package/lib/module/pluginHost.spec.js +0 -291
  162. package/lib/module/pluginHost.spec.js.map +0 -1
  163. package/lib/module/plugins/insights/eventId.js +0 -122
  164. package/lib/module/plugins/insights/eventId.js.map +0 -1
  165. package/lib/module/plugins/insights/eventId.spec.js +0 -47
  166. package/lib/module/plugins/insights/eventId.spec.js.map +0 -1
  167. package/lib/module/plugins/insights/index.js +0 -268
  168. package/lib/module/plugins/insights/index.js.map +0 -1
  169. package/lib/module/plugins/insights/insights.spec.js +0 -639
  170. package/lib/module/plugins/insights/insights.spec.js.map +0 -1
  171. package/lib/module/plugins/insights/sender.js +0 -123
  172. package/lib/module/plugins/insights/sender.js.map +0 -1
  173. package/lib/module/plugins/insights/sender.spec.js +0 -265
  174. package/lib/module/plugins/insights/sender.spec.js.map +0 -1
  175. package/lib/module/plugins/insights/state.js +0 -139
  176. package/lib/module/plugins/insights/state.js.map +0 -1
  177. package/lib/module/releaseCatalogCache.spec.js +0 -213
  178. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  179. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  180. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  181. package/lib/module/store.notification.spec.js +0 -85
  182. package/lib/module/store.notification.spec.js.map +0 -1
  183. package/lib/module/store.spec.js +0 -181
  184. package/lib/module/store.spec.js.map +0 -1
  185. package/lib/module/updateError.spec.js +0 -149
  186. package/lib/module/updateError.spec.js.map +0 -1
  187. package/lib/module/wrap.progress.spec.js +0 -196
  188. package/lib/module/wrap.progress.spec.js.map +0 -1
  189. package/lib/module/wrap.spec.js +0 -233
  190. package/lib/module/wrap.spec.js.map +0 -1
  191. package/lib/typescript/commonjs/appReady.test-utils.d.ts +0 -9
  192. package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +0 -1
  193. package/lib/typescript/commonjs/baseURL.d.ts +0 -4
  194. package/lib/typescript/commonjs/baseURL.d.ts.map +0 -1
  195. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +0 -18
  196. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +0 -1
  197. package/lib/typescript/commonjs/plugins/insights/index.d.ts +0 -40
  198. package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +0 -1
  199. package/lib/typescript/commonjs/plugins/insights/sender.d.ts +0 -74
  200. package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +0 -1
  201. package/lib/typescript/commonjs/plugins/insights/state.d.ts +0 -46
  202. package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +0 -1
  203. package/lib/typescript/module/appReady.test-utils.d.ts +0 -9
  204. package/lib/typescript/module/appReady.test-utils.d.ts.map +0 -1
  205. package/lib/typescript/module/baseURL.d.ts +0 -4
  206. package/lib/typescript/module/baseURL.d.ts.map +0 -1
  207. package/lib/typescript/module/plugins/insights/eventId.d.ts +0 -18
  208. package/lib/typescript/module/plugins/insights/eventId.d.ts.map +0 -1
  209. package/lib/typescript/module/plugins/insights/index.d.ts +0 -40
  210. package/lib/typescript/module/plugins/insights/index.d.ts.map +0 -1
  211. package/lib/typescript/module/plugins/insights/sender.d.ts +0 -74
  212. package/lib/typescript/module/plugins/insights/sender.d.ts.map +0 -1
  213. package/lib/typescript/module/plugins/insights/state.d.ts +0 -46
  214. package/lib/typescript/module/plugins/insights/state.d.ts.map +0 -1
  215. package/plugins/insights/package.json +0 -8
  216. package/src/appReady.spec.ts +0 -393
  217. package/src/appReady.test-utils.ts +0 -31
  218. package/src/baseURL.ts +0 -15
  219. package/src/catalogCacheNative.spec.ts +0 -63
  220. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -966
  221. package/src/entryImportGraph.spec.ts +0 -65
  222. package/src/httpClient.spec.ts +0 -216
  223. package/src/index.spec.ts +0 -434
  224. package/src/iosInterop.spec.ts +0 -30
  225. package/src/native.spec.ts +0 -978
  226. package/src/pluginHost.spec.ts +0 -297
  227. package/src/plugins/insights/eventId.spec.ts +0 -60
  228. package/src/plugins/insights/eventId.ts +0 -171
  229. package/src/plugins/insights/index.ts +0 -367
  230. package/src/plugins/insights/insights.spec.ts +0 -732
  231. package/src/plugins/insights/sender.spec.ts +0 -287
  232. package/src/plugins/insights/sender.ts +0 -221
  233. package/src/plugins/insights/state.ts +0 -184
  234. package/src/releaseCatalogCache.spec.ts +0 -304
  235. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  236. package/src/store.notification.spec.ts +0 -102
  237. package/src/store.spec.ts +0 -215
  238. package/src/updateError.spec.ts +0 -144
  239. package/src/wrap.progress.spec.ts +0 -232
  240. package/src/wrap.spec.ts +0 -262
@@ -1,118 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertFalse
5
- import org.junit.Assert.assertNull
6
- import org.junit.Assert.assertTrue
7
- import org.junit.Rule
8
- import org.junit.Test
9
- import org.junit.rules.TemporaryFolder
10
- import java.io.File
11
- import java.nio.file.Files
12
- import java.nio.file.StandardCopyOption
13
-
14
- class ReleaseCatalogCacheServiceTest {
15
- @get:Rule
16
- val temporaryFolder = TemporaryFolder()
17
-
18
- @Test
19
- fun `persists checksum verified entries without credential filenames`() {
20
- val root = temporaryFolder.newFolder("catalog-cache")
21
- val first = createService(root)
22
- val partition = "https://updates.example.com|x-api-key=raw-secret"
23
-
24
- assertTrue(first.set(partition, "validated-catalog"))
25
- assertEquals("validated-catalog", createService(root).get(partition))
26
-
27
- val entry = root.listFiles()!!.single()
28
- assertTrue(entry.name.matches(Regex("[0-9a-f]{64}\\.catalog")))
29
- assertFalse(entry.name.contains("raw-secret"))
30
- assertFalse(entry.readText().contains("raw-secret"))
31
-
32
- entry.appendText("corrupt")
33
- assertNull(createService(root).get(partition))
34
- assertFalse(entry.exists())
35
- }
36
-
37
- @Test
38
- fun `failed atomic replacement preserves the previous entry`() {
39
- val root = temporaryFolder.newFolder("interrupted-write")
40
- val partition = "partition"
41
- assertTrue(createService(root).set(partition, "previous"))
42
-
43
- val interrupted =
44
- ReleaseCatalogCacheService(
45
- rootDirectory = root,
46
- atomicReader = ::readForTest,
47
- atomicWriter = { _, _ -> false },
48
- atomicRemover = ::removeForTest,
49
- )
50
- assertFalse(interrupted.set(partition, "replacement"))
51
- assertEquals("previous", createService(root).get(partition))
52
- }
53
-
54
- @Test
55
- fun `evicts least recently used entries and rejects oversized payloads`() {
56
- val root = temporaryFolder.newFolder("bounded-cache")
57
- val service =
58
- ReleaseCatalogCacheService(
59
- rootDirectory = root,
60
- maxEntries = 2,
61
- maxEntryBytes = 16,
62
- maxTotalBytes = 200,
63
- atomicReader = ::readForTest,
64
- atomicWriter = ::writeForTest,
65
- atomicRemover = ::removeForTest,
66
- )
67
-
68
- assertTrue(service.set("one", "first"))
69
- Thread.sleep(5)
70
- assertTrue(service.set("two", "second"))
71
- Thread.sleep(5)
72
- assertTrue(service.set("three", "third"))
73
-
74
- assertNull(service.get("one"))
75
- assertEquals("second", service.get("two"))
76
- assertEquals("third", service.get("three"))
77
- assertFalse(service.set("two", "x".repeat(17)))
78
- assertEquals("second", service.get("two"))
79
- }
80
-
81
- private fun createService(root: File): ReleaseCatalogCacheService =
82
- ReleaseCatalogCacheService(
83
- rootDirectory = root,
84
- atomicReader = ::readForTest,
85
- atomicWriter = ::writeForTest,
86
- atomicRemover = ::removeForTest,
87
- )
88
-
89
- private fun readForTest(file: File): ByteArray? = if (file.isFile) file.readBytes() else null
90
-
91
- private fun removeForTest(file: File): Boolean = !file.exists() || file.delete()
92
-
93
- private fun writeForTest(
94
- destination: File,
95
- bytes: ByteArray,
96
- ): Boolean =
97
- try {
98
- val temporary = File.createTempFile("entry", ".tmp", destination.parentFile)
99
- temporary.writeBytes(bytes)
100
- try {
101
- Files.move(
102
- temporary.toPath(),
103
- destination.toPath(),
104
- StandardCopyOption.ATOMIC_MOVE,
105
- StandardCopyOption.REPLACE_EXISTING,
106
- )
107
- } catch (_: Exception) {
108
- Files.move(
109
- temporary.toPath(),
110
- destination.toPath(),
111
- StandardCopyOption.REPLACE_EXISTING,
112
- )
113
- }
114
- true
115
- } catch (_: Exception) {
116
- false
117
- }
118
- }
@@ -1,206 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertArrayEquals
4
- import org.junit.Assert.assertEquals
5
- import org.junit.Assert.assertThrows
6
- import org.junit.Rule
7
- import org.junit.Test
8
- import org.junit.rules.TemporaryFolder
9
- import java.io.ByteArrayInputStream
10
- import java.io.ByteArrayOutputStream
11
- import java.io.File
12
- import java.io.IOException
13
- import java.util.Base64
14
-
15
- class TarBrArchiveExtractorTest {
16
- @get:Rule
17
- val temporaryFolder = TemporaryFolder()
18
-
19
- @Test
20
- fun `extracts exact regular file set`() {
21
- val destination = extract(VALID, 3072, mapOf("assets/image.png" to 5, "index.android.bundle" to 6))
22
-
23
- assertArrayEquals("image".toByteArray(), File(destination, "assets/image.png").readBytes())
24
- assertArrayEquals("bundle".toByteArray(), File(destination, "index.android.bundle").readBytes())
25
- }
26
-
27
- @Test
28
- fun `supports a POSIX PAX long path`() {
29
- val path = "assets/${"x".repeat(120)}.bin"
30
- val destination = extract(PAX_LONG_PATH, 3072, mapOf(path to 4))
31
-
32
- assertArrayEquals("long".toByteArray(), File(destination, path).readBytes())
33
- }
34
-
35
- @Test
36
- fun `rejects duplicate unsafe linked and malformed archives`() {
37
- val cases =
38
- listOf(
39
- Triple(DUPLICATE, 3072L, mapOf("index.android.bundle" to 6L)),
40
- Triple(TRAVERSAL, 2048L, mapOf("../escape" to 3L)),
41
- Triple(SYMLINK, 1536L, mapOf("index.android.bundle" to 0L)),
42
- Triple(SINGLE_END_BLOCK, 1536L, mapOf("index.android.bundle" to 6L)),
43
- Triple(TRAILING_NON_ZERO, 2560L, mapOf("index.android.bundle" to 6L)),
44
- )
45
-
46
- cases.forEachIndexed { index, (fixture, tarSize, files) ->
47
- val archive = temporaryFolder.newFile("invalid-$index.tar.br").apply { writeBytes(decode(fixture)) }
48
- val destination = File(temporaryFolder.root, "invalid-$index")
49
-
50
- assertThrows(Exception::class.java) {
51
- TarBrArchiveExtractor.extract(archive, destination, tarSize, files)
52
- }
53
- check(!destination.exists())
54
- }
55
- }
56
-
57
- @Test
58
- fun `rejects dangling consecutive duplicate and unsupported extension metadata`() {
59
- val pathRecord = paxRecord("path", "index.android.bundle")
60
- val malformedShortRecord = "3 \n".toByteArray()
61
- val cases =
62
- listOf(
63
- tarArchive(tarEntry("PaxHeader", 'x', pathRecord)),
64
- tarArchive(
65
- tarEntry("PaxHeader", 'x', pathRecord),
66
- tarEntry("PaxHeader", 'x', pathRecord),
67
- ),
68
- tarArchive(
69
- tarEntry("PaxHeader", 'x', pathRecord + paxRecord("path", "other")),
70
- regularEntry("ignored", "bundle"),
71
- ),
72
- tarArchive(
73
- tarEntry("PaxHeader", 'x', malformedShortRecord),
74
- regularEntry("ignored", "bundle"),
75
- ),
76
- tarArchive(
77
- tarEntry("PaxHeader", 'x', paxRecord("linkpath", "target")),
78
- regularEntry("index.android.bundle", "bundle"),
79
- ),
80
- tarArchive(tarEntry("LongLink", 'L', "name\u0000".toByteArray())),
81
- )
82
-
83
- cases.forEach { bytes ->
84
- assertThrows(IOException::class.java) { consumeTar(bytes) }
85
- }
86
- }
87
-
88
- @Test
89
- fun `preserves exact header paths and applies PAX size`() {
90
- val spacedPathArchive = tarArchive(regularEntry("index.android.bundle ", "bundle"))
91
- TarArchiveInputStream(ByteArrayInputStream(spacedPathArchive)).use { input ->
92
- assertEquals("index.android.bundle ", input.getNextEntry()?.name)
93
- }
94
-
95
- val payload = "data".toByteArray()
96
- val paxSizeArchive =
97
- tarArchive(
98
- tarEntry("PaxHeader", 'x', paxRecord("size", payload.size.toString())),
99
- tarEntry("index.android.bundle", '0', payload, declaredSize = 1),
100
- )
101
- TarArchiveInputStream(ByteArrayInputStream(paxSizeArchive)).use { input ->
102
- val entry = input.getNextEntry()
103
- assertEquals(payload.size.toLong(), entry?.size)
104
- assertArrayEquals(payload, input.readBytes())
105
- assertEquals(null, input.getNextEntry())
106
- }
107
- }
108
-
109
- private fun extract(
110
- fixture: String,
111
- tarSize: Long,
112
- files: Map<String, Long>,
113
- ): File {
114
- val archive = temporaryFolder.newFile("fixture-${System.nanoTime()}.tar.br").apply { writeBytes(decode(fixture)) }
115
- val destination = File(temporaryFolder.root, "extracted-${System.nanoTime()}")
116
- TarBrArchiveExtractor.extract(archive, destination, tarSize, files)
117
- return destination
118
- }
119
-
120
- private fun decode(value: String): ByteArray = Base64.getDecoder().decode(value)
121
-
122
- private fun consumeTar(bytes: ByteArray) {
123
- TarArchiveInputStream(ByteArrayInputStream(bytes)).use { input ->
124
- while (input.getNextEntry() != null) input.readBytes()
125
- }
126
- }
127
-
128
- private fun paxRecord(
129
- key: String,
130
- value: String,
131
- ): ByteArray {
132
- var length = key.toByteArray().size + value.toByteArray().size + 3
133
- while (true) {
134
- val record = "$length $key=$value\n".toByteArray()
135
- if (record.size == length) return record
136
- length = record.size
137
- }
138
- }
139
-
140
- private fun regularEntry(
141
- name: String,
142
- content: String,
143
- ): ByteArray = tarEntry(name, '0', content.toByteArray())
144
-
145
- private fun tarEntry(
146
- name: String,
147
- type: Char,
148
- payload: ByteArray,
149
- declaredSize: Int = payload.size,
150
- ): ByteArray {
151
- val header = ByteArray(512)
152
- name.toByteArray().copyInto(header, endIndex = name.toByteArray().size.coerceAtMost(100))
153
- writeOctal(header, 100, 8, 420)
154
- writeOctal(header, 108, 8, 0)
155
- writeOctal(header, 116, 8, 0)
156
- writeOctal(header, 124, 12, declaredSize)
157
- writeOctal(header, 136, 12, 0)
158
- for (index in 148 until 156) header[index] = ' '.code.toByte()
159
- header[156] = type.code.toByte()
160
- "ustar".toByteArray().copyInto(header, 257)
161
- writeOctal(header, 148, 8, header.sumOf { it.toInt() and 0xff })
162
-
163
- return ByteArrayOutputStream()
164
- .apply {
165
- write(header)
166
- write(payload)
167
- write(ByteArray((512 - payload.size % 512) % 512))
168
- }.toByteArray()
169
- }
170
-
171
- private fun tarArchive(vararg entries: ByteArray): ByteArray =
172
- ByteArrayOutputStream()
173
- .apply {
174
- entries.forEach(::write)
175
- write(ByteArray(1024))
176
- }.toByteArray()
177
-
178
- private fun writeOctal(
179
- target: ByteArray,
180
- offset: Int,
181
- length: Int,
182
- value: Int,
183
- ) {
184
- val digits = value.toString(8).padStart(length - 2, '0')
185
- digits.toByteArray().copyInto(target, offset)
186
- target[offset + length - 2] = 0
187
- target[offset + length - 1] = ' '.code.toByte()
188
- }
189
-
190
- companion object {
191
- const val VALID =
192
- "G/8LYI7UVnlCO6lhAMTqIo5tMQLiY7NptuP57iYyogHNeF9BlHNiXHeDMLCAJL3h9im3UyYnSs3aLbJIIBsjml0YwV1+Hd11htDfIA8GgPKHFrFNtD7SOfZo/h9mbgDYuSAJIPmy9Z2b"
193
- private const val DUPLICATE =
194
- "G/8L4I/UVnlCO6lh6sQAIm5ssV7ksdnUyXHelkgImbdLFCl08DwgyVOGdSmhfs8LgniesZ82/bHw7qIYoXwdSTsMVSkOBkDMM0vqHj11Ew=="
195
- private const val TRAVERSAL =
196
- "G/8H+AfKbRfTdvBDW1YMVZ0c3w7fLhJCyAFHCIm3xTUACIRyzK7qNZRgrBkUGlOQ+PtOBlH/uDAUZ9s9Dw=="
197
- private const val SYMLINK =
198
- "G/8FoI7UYs0ZrwQiHgYxvWizaUNMJ8cnP4h9lyiDwKKELcCA8pSZdZEoyN91S9BvNObx0n8IcrtxYn2sUe6QrffCuHk8LCgA"
199
- private const val SINGLE_END_BLOCK =
200
- "G/8F4I/UVnlCO6lh6sQAIm5ssV7ksdnUyfH9WyIhZN4uUaQSeB6Q5DGGvrXYkguCOs/YT5v+GLy7ECOUryNph6EqxcEAinlmEw=="
201
- private const val TRAILING_NON_ZERO =
202
- "G/8J4Adqc2ds55CPYbC2qk6O87ZEQsi0tUsUKUXN80TyGEPfWmyJBUGcZ6y72vQfJjUzyChogu7Xu5ODMLcUB0MBYu7ZOLkF"
203
- private const val PAX_LONG_PATH =
204
- "G/8LYAbCtn2mBfjh0GTohqrNYvNuZjueb2Ij3vmAJjz8C6jLIEtYgqi7gAMNAII62ZkiZILxgnbd1QSM8usRpO6ZyCgYZiz8dTRcx0YB/gG5MBRqZPyVyrk8J47iYenP+zXoZcGa8QLgayISZoflgdi14Q=="
205
- }
206
- }
@@ -1,360 +0,0 @@
1
- "use strict";
2
-
3
- var _vitest = require("vitest");
4
- var _appReadyTestUtils = require("./appReady.test-utils.js");
5
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
6
- _vitest.vi.mock("react-native", () => ({
7
- Platform: {
8
- OS: "ios"
9
- }
10
- }));
11
- const mocks = _vitest.vi.hoisted(() => {
12
- Reflect.set(globalThis, "HotUpdater", {
13
- SDK_VERSION: "test-sdk-version"
14
- });
15
- return {
16
- getAppVersion: _vitest.vi.fn(() => "1.0.0"),
17
- getBundleId: _vitest.vi.fn(() => "bundle-id"),
18
- getChannel: _vitest.vi.fn(() => "production"),
19
- getActiveUpdateState: _vitest.vi.fn(),
20
- getCohort: _vitest.vi.fn(() => "123"),
21
- getFingerprintHash: _vitest.vi.fn(() => "fingerprint-hash"),
22
- getInstallId: _vitest.vi.fn(() => "install-id"),
23
- getStorageItem: _vitest.vi.fn(() => null),
24
- setStorageItem: _vitest.vi.fn(),
25
- readNotifyAppReady: _vitest.vi.fn(() => (0, _appReadyTestUtils.createNotifyReadResult)())
26
- };
27
- });
28
- _vitest.vi.mock("./native", () => mocks);
29
- const recordingPlugin = (hooks = events => ({
30
- onAppReady: result => {
31
- events.push(["onAppReady", result]);
32
- },
33
- onBundleDownloaded: info => {
34
- events.push(["onBundleDownloaded", info]);
35
- }
36
- })) => {
37
- const events = [];
38
- const plugin = {
39
- id: "recorder",
40
- setup: () => hooks(events)
41
- };
42
- return {
43
- events,
44
- plugin
45
- };
46
- };
47
- const configure = async (plugins, onError) => {
48
- const {
49
- configurePlugins
50
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./pluginHost")));
51
- configurePlugins(plugins, {
52
- baseURL: "https://updates.example.com",
53
- ...(onError ? {
54
- onError
55
- } : {})
56
- });
57
- };
58
- (0, _vitest.describe)("app-ready reporting to plugins", () => {
59
- (0, _vitest.beforeEach)(() => {
60
- _vitest.vi.resetModules();
61
- _vitest.vi.unstubAllGlobals();
62
- _vitest.vi.useRealTimers();
63
- for (const mock of Object.values(mocks)) mock.mockReset();
64
- mocks.getAppVersion.mockReturnValue("1.0.0");
65
- mocks.getBundleId.mockReturnValue("bundle-id");
66
- mocks.getChannel.mockReturnValue("production");
67
- mocks.getActiveUpdateState.mockReturnValue({
68
- activeSelection: null,
69
- stableSelection: null,
70
- verificationPending: false
71
- });
72
- mocks.getCohort.mockReturnValue("123");
73
- mocks.getFingerprintHash.mockReturnValue("fingerprint-hash");
74
- mocks.getInstallId.mockReturnValue("install-id");
75
- mocks.getStorageItem.mockReturnValue(null);
76
- mocks.readNotifyAppReady.mockReturnValue((0, _appReadyTestUtils.createNotifyReadResult)());
77
- });
78
- _vitest.it.each([{
79
- label: "running selection",
80
- pending: false,
81
- channel: "production",
82
- expected: "release-running"
83
- }, {
84
- label: "staged selection",
85
- pending: true,
86
- channel: "production",
87
- expected: "release-running"
88
- }, {
89
- label: "another channel",
90
- pending: false,
91
- channel: "preview",
92
- expected: null
93
- }])("attributes an unchanged launch with $label", async ({
94
- pending,
95
- channel,
96
- expected
97
- }) => {
98
- (0, _appReadyTestUtils.stubNotifyFrame)();
99
- const running = {
100
- kind: "BUNDLE",
101
- releaseId: "release-running",
102
- bundleId: "bundle-id",
103
- channel
104
- };
105
- mocks.getActiveUpdateState.mockReturnValue({
106
- activeSelection: pending ? {
107
- ...running,
108
- releaseId: "release-pending",
109
- bundleId: "bundle-pending"
110
- } : running,
111
- stableSelection: pending ? running : null,
112
- verificationPending: pending
113
- });
114
- const {
115
- events,
116
- plugin
117
- } = recordingPlugin();
118
- await configure([plugin]);
119
- const {
120
- handleNotifyAppReady
121
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
122
- const readiness = handleNotifyAppReady({});
123
- await _vitest.vi.runOnlyPendingTimersAsync();
124
- await readiness;
125
- (0, _vitest.expect)(events).toEqual([["onAppReady", {
126
- status: "UNCHANGED",
127
- channel: "production",
128
- bundleId: "bundle-id",
129
- releaseId: expected,
130
- previousProcessExit: null
131
- }]]);
132
- });
133
- _vitest.it.each(["UPDATE_APPLIED", "RECOVERED"])("reports %s with the persisted transition and calls onNotifyAppReady", async status => {
134
- (0, _appReadyTestUtils.stubNotifyFrame)();
135
- mocks.readNotifyAppReady.mockReturnValue((0, _appReadyTestUtils.createNotifyReadResult)({
136
- status,
137
- fromBundleId: "bundle-a",
138
- toBundleId: "bundle-b"
139
- }, {
140
- type: status,
141
- fromBundleId: "bundle-a",
142
- fromReleaseId: "release-a",
143
- toBundleId: "bundle-b",
144
- toReleaseId: "release-b",
145
- updateStrategy: "fingerprint"
146
- }));
147
- const {
148
- events,
149
- plugin
150
- } = recordingPlugin();
151
- await configure([plugin]);
152
- const onNotifyAppReady = _vitest.vi.fn();
153
- const {
154
- handleNotifyAppReady
155
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
156
- const readiness = handleNotifyAppReady({
157
- onNotifyAppReady
158
- });
159
- await _vitest.vi.runOnlyPendingTimersAsync();
160
- await readiness;
161
- (0, _vitest.expect)(events).toEqual([["onAppReady", {
162
- status,
163
- channel: "production",
164
- fromBundleId: "bundle-a",
165
- fromReleaseId: "release-a",
166
- toBundleId: "bundle-b",
167
- toReleaseId: "release-b",
168
- updateStrategy: "fingerprint",
169
- previousProcessExit: null
170
- }]]);
171
- (0, _vitest.expect)(onNotifyAppReady).toHaveBeenCalledWith({
172
- status,
173
- fromBundleId: "bundle-a",
174
- toBundleId: "bundle-b"
175
- });
176
- });
177
- (0, _vitest.it)("waits for native launch verification before reporting", async () => {
178
- (0, _appReadyTestUtils.stubNotifyFrame)();
179
- mocks.readNotifyAppReady.mockReturnValueOnce((0, _appReadyTestUtils.createNotifyReadResult)(undefined, null, true)).mockReturnValueOnce((0, _appReadyTestUtils.createNotifyReadResult)(undefined, null, true)).mockReturnValue((0, _appReadyTestUtils.createNotifyReadResult)());
180
- const {
181
- events,
182
- plugin
183
- } = recordingPlugin();
184
- await configure([plugin]);
185
- const {
186
- handleNotifyAppReady
187
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
188
- const readiness = handleNotifyAppReady({});
189
- await _vitest.vi.runOnlyPendingTimersAsync();
190
- (0, _vitest.expect)(events).toEqual([]);
191
- await _vitest.vi.runAllTimersAsync();
192
- await readiness;
193
- (0, _vitest.expect)(mocks.readNotifyAppReady).toHaveBeenCalledTimes(3);
194
- (0, _vitest.expect)(events).toHaveLength(1);
195
- });
196
- (0, _vitest.it)("reports the launch once per runtime while every init call gets readiness", async () => {
197
- (0, _appReadyTestUtils.stubNotifyFrame)();
198
- const {
199
- events,
200
- plugin
201
- } = recordingPlugin();
202
- await configure([plugin]);
203
- const onNotifyAppReady = _vitest.vi.fn();
204
- const {
205
- handleNotifyAppReady
206
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
207
- const first = handleNotifyAppReady({
208
- onNotifyAppReady
209
- });
210
- const second = handleNotifyAppReady({
211
- onNotifyAppReady
212
- });
213
- await _vitest.vi.runOnlyPendingTimersAsync();
214
- await Promise.all([first, second]);
215
- (0, _vitest.expect)(events).toHaveLength(1);
216
- (0, _vitest.expect)(onNotifyAppReady).toHaveBeenCalledTimes(2);
217
- });
218
- (0, _vitest.it)("skips onAppReady for an applied launch without its persisted transition", async () => {
219
- (0, _appReadyTestUtils.stubNotifyFrame)();
220
- mocks.readNotifyAppReady.mockReturnValue((0, _appReadyTestUtils.createNotifyReadResult)({
221
- status: "UPDATE_APPLIED",
222
- fromBundleId: "bundle-a",
223
- toBundleId: "bundle-b"
224
- }, null));
225
- const {
226
- events,
227
- plugin
228
- } = recordingPlugin();
229
- await configure([plugin]);
230
- const onNotifyAppReady = _vitest.vi.fn();
231
- const {
232
- handleNotifyAppReady
233
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
234
- const readiness = handleNotifyAppReady({
235
- onNotifyAppReady
236
- });
237
- await _vitest.vi.runOnlyPendingTimersAsync();
238
- await readiness;
239
- (0, _vitest.expect)(events).toEqual([]);
240
- (0, _vitest.expect)(onNotifyAppReady).toHaveBeenCalledOnce();
241
- });
242
- (0, _vitest.it)("reports a throwing or rejecting hook through onError and keeps readiness", async () => {
243
- (0, _appReadyTestUtils.stubNotifyFrame)();
244
- const onError = _vitest.vi.fn();
245
- const throwing = {
246
- id: "throwing",
247
- setup: () => ({
248
- onAppReady: () => {
249
- throw new Error("hook failed");
250
- }
251
- })
252
- };
253
- const rejecting = {
254
- id: "rejecting",
255
- setup: () => ({
256
- onAppReady: async () => {
257
- throw new Error("hook rejected");
258
- }
259
- })
260
- };
261
- await configure([throwing, rejecting], onError);
262
- const onNotifyAppReady = _vitest.vi.fn();
263
- const {
264
- handleNotifyAppReady
265
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
266
- const readiness = handleNotifyAppReady({
267
- onNotifyAppReady
268
- });
269
- await _vitest.vi.runOnlyPendingTimersAsync();
270
- await readiness;
271
- await _vitest.vi.runAllTimersAsync();
272
- (0, _vitest.expect)(onNotifyAppReady).toHaveBeenCalledWith({
273
- status: "UNCHANGED"
274
- });
275
- (0, _vitest.expect)(onError).toHaveBeenCalledTimes(2);
276
- (0, _vitest.expect)(onError).toHaveBeenCalledWith(_vitest.expect.objectContaining({
277
- message: '[HotUpdater] Plugin "throwing" failed in onAppReady',
278
- cause: _vitest.expect.objectContaining({
279
- message: "hook failed"
280
- })
281
- }));
282
- (0, _vitest.expect)(onError).toHaveBeenCalledWith(_vitest.expect.objectContaining({
283
- message: '[HotUpdater] Plugin "rejecting" failed in onAppReady'
284
- }));
285
- });
286
- (0, _vitest.it)("delivers events after the launch, even when they happen first", async () => {
287
- (0, _appReadyTestUtils.stubNotifyFrame)();
288
- const {
289
- events,
290
- plugin
291
- } = recordingPlugin();
292
- await configure([plugin]);
293
- const {
294
- emitAfterAppReady,
295
- handleNotifyAppReady
296
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
297
- const readiness = handleNotifyAppReady({});
298
- emitAfterAppReady("onBundleDownloaded", () => ({
299
- channel: "production",
300
- fromBundleId: "bundle-id",
301
- fromReleaseId: null,
302
- toBundleId: "next-bundle",
303
- toReleaseId: "next-release",
304
- updateStrategy: "appVersion",
305
- delivery: "manifest",
306
- patchFallback: false
307
- }));
308
- (0, _vitest.expect)(events).toEqual([]);
309
- await _vitest.vi.runOnlyPendingTimersAsync();
310
- await readiness;
311
- await Promise.resolve();
312
- (0, _vitest.expect)(events.map(event => event[0])).toEqual(["onAppReady", "onBundleDownloaded"]);
313
- });
314
- (0, _vitest.it)("passes on why the previous process exited, and keeps it for later events", async () => {
315
- (0, _appReadyTestUtils.stubNotifyFrame)();
316
- mocks.readNotifyAppReady.mockReturnValue((0, _appReadyTestUtils.createNotifyReadResult)({
317
- status: "RECOVERED",
318
- fromBundleId: "bundle-b",
319
- toBundleId: "bundle-a"
320
- }, {
321
- type: "RECOVERED",
322
- fromBundleId: "bundle-b",
323
- fromReleaseId: null,
324
- toBundleId: "bundle-a",
325
- toReleaseId: null,
326
- updateStrategy: "appVersion"
327
- }, false, "CRASH_NATIVE"));
328
- const {
329
- events,
330
- plugin
331
- } = recordingPlugin();
332
- await configure([plugin]);
333
- const {
334
- getPreviousProcessExit,
335
- handleNotifyAppReady
336
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
337
- (0, _vitest.expect)(getPreviousProcessExit()).toBeNull();
338
- const readiness = handleNotifyAppReady({});
339
- await _vitest.vi.runOnlyPendingTimersAsync();
340
- await readiness;
341
- (0, _vitest.expect)(events).toEqual([["onAppReady", _vitest.expect.objectContaining({
342
- status: "RECOVERED",
343
- previousProcessExit: "CRASH_NATIVE"
344
- })]]);
345
- (0, _vitest.expect)(getPreviousProcessExit()).toBe("CRASH_NATIVE");
346
- });
347
- (0, _vitest.it)("builds no event when no plugin listens", async () => {
348
- (0, _appReadyTestUtils.stubNotifyFrame)();
349
- await configure([]);
350
- const {
351
- handleNotifyAppReady
352
- } = await Promise.resolve().then(() => _interopRequireWildcard(require("./appReady")));
353
- const readiness = handleNotifyAppReady({});
354
- await _vitest.vi.runOnlyPendingTimersAsync();
355
- await readiness;
356
- (0, _vitest.expect)(mocks.getActiveUpdateState).not.toHaveBeenCalled();
357
- (0, _vitest.expect)(mocks.getBundleId).not.toHaveBeenCalled();
358
- });
359
- });
360
- //# sourceMappingURL=appReady.spec.js.map