@onekeyfe/react-native-range-downloader 3.0.81-alpha.0 → 3.0.81-alpha.10

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 (38) hide show
  1. package/ReactNativeRangeDownloader.podspec +1 -1
  2. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +12 -5
  3. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweep.kt +111 -0
  4. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +185 -189
  5. package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +16 -20
  6. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +39 -0
  7. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDeadlineTest.kt +26 -0
  8. package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactOrphanSweepTest.kt +107 -0
  9. package/ios/FirmwareArtifactStore.swift +662 -408
  10. package/ios/RangeDownloadLogic.swift +297 -0
  11. package/ios/ReactNativeRangeDownloader.swift +42 -471
  12. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +3 -7
  13. package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
  14. package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +7 -6
  15. package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +7 -7
  16. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +0 -19
  17. package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +0 -1
  18. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +2 -2
  19. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +3 -3
  20. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +0 -4
  21. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +15 -0
  22. package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +0 -3
  23. package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +0 -11
  24. package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +32 -13
  25. package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +39 -8
  26. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +0 -1
  27. package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +0 -19
  28. package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +6 -5
  29. package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +9 -9
  30. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +0 -1
  31. package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +0 -4
  32. package/package.json +2 -2
  33. package/src/ReactNativeRangeDownloader.nitro.ts +4 -11
  34. package/ios/FirmwareBackgroundSessionEventRouter.swift +0 -17
  35. package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +0 -76
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +0 -38
  37. package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +0 -48
  38. package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReconcileParams.hpp +0 -76
@@ -9,6 +9,9 @@ import java.io.File
9
9
  import java.security.MessageDigest
10
10
  import java.util.concurrent.CopyOnWriteArrayList
11
11
  import java.util.concurrent.atomic.AtomicLong
12
+ import kotlinx.coroutines.CoroutineScope
13
+ import kotlinx.coroutines.Dispatchers
14
+ import kotlinx.coroutines.SupervisorJob
12
15
 
13
16
  // P1: Nitro adapter for the Android concurrent multi-range downloader.
14
17
  //
@@ -34,6 +37,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
34
37
 
35
38
  private val listeners = CopyOnWriteArrayList<Listener>()
36
39
  private val nextListenerId = AtomicLong(1)
40
+ private val firmwareArtifactScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
37
41
 
38
42
  // Active downloads keyed by "channel|taskId" so cancel/discardArtifacts can
39
43
  // flip the abort flag + stop the worker pool BEFORE deleting files, instead of
@@ -268,7 +272,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
268
272
 
269
273
  override fun getFirmwareArtifactCapabilities(): FirmwareArtifactCapabilities {
270
274
  return FirmwareArtifactCapabilities(
271
- firmwareArtifactProtocolVersion = 1.0,
275
+ firmwareArtifactProtocolVersion = 3.0,
272
276
  supportedRouteTypes = arrayOf("domain", "pinnedIp"),
273
277
  supportsArchiveMaterialization = true,
274
278
  maxReadBytes = FirmwareArtifactStore.MAX_READ_BYTES.toDouble(),
@@ -278,7 +282,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
278
282
  override fun downloadFirmwareArtifact(
279
283
  params: FirmwareArtifactDownloadParams,
280
284
  ): Promise<FirmwareArtifactReceipt> {
281
- return Promise.async {
285
+ return Promise.async(firmwareArtifactScope) {
282
286
  val artifact = FirmwareArtifactStore.download(params)
283
287
  FirmwareArtifactReceipt(
284
288
  artifactRef = artifact.artifactRef,
@@ -291,7 +295,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
291
295
  override fun cancelFirmwareArtifactDownloads(
292
296
  params: FirmwareArtifactCancelParams,
293
297
  ): Promise<Unit> {
294
- return Promise.async {
298
+ return Promise.async(firmwareArtifactScope) {
295
299
  FirmwareArtifactStore.cancelDownloads(params.transactionId)
296
300
  }
297
301
  }
@@ -299,7 +303,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
299
303
  override fun discardFirmwareArtifact(
300
304
  params: FirmwareArtifactRefParams,
301
305
  ): Promise<Unit> {
302
- return Promise.async {
306
+ return Promise.async(firmwareArtifactScope) {
303
307
  FirmwareArtifactStore.discard(params.artifactRef)
304
308
  }
305
309
  }
@@ -307,7 +311,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
307
311
  override fun openFirmwareArtifact(
308
312
  params: FirmwareArtifactRefParams,
309
313
  ): Promise<FirmwareArtifactReaderInfo> {
310
- return Promise.async {
314
+ return Promise.async(firmwareArtifactScope) {
311
315
  val (readerId, size) = FirmwareArtifactStore.open(params.artifactRef)
312
316
  FirmwareArtifactReaderInfo(readerId = readerId, size = size.toDouble())
313
317
  }
@@ -316,7 +320,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
316
320
  override fun readFirmwareArtifact(
317
321
  params: FirmwareArtifactReaderReadParams,
318
322
  ): Promise<ArrayBuffer> {
319
- return Promise.async {
323
+ return Promise.async(firmwareArtifactScope) {
320
324
  require(
321
325
  params.offset.isFinite() &&
322
326
  params.offset >= 0 &&
@@ -340,7 +344,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
340
344
  override fun closeFirmwareArtifact(
341
345
  params: FirmwareArtifactReaderCloseParams,
342
346
  ): Promise<Unit> {
343
- return Promise.async {
347
+ return Promise.async(firmwareArtifactScope) {
344
348
  FirmwareArtifactStore.close(params.readerId)
345
349
  }
346
350
  }
@@ -348,7 +352,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
348
352
  override fun materializeFirmwareArchive(
349
353
  params: FirmwareArchiveMaterializeParams,
350
354
  ): Promise<FirmwareArchiveMaterializeResult> {
351
- return Promise.async {
355
+ return Promise.async(firmwareArtifactScope) {
352
356
  val artifacts = FirmwareArtifactStore.materializeArchive(
353
357
  params.leaseRef,
354
358
  params.archiveArtifactRef,
@@ -372,7 +376,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
372
376
  override fun createFirmwareArtifactLease(
373
377
  params: FirmwareArtifactLeaseCreateParams,
374
378
  ): Promise<FirmwareArtifactLease> {
375
- return Promise.async {
379
+ return Promise.async(firmwareArtifactScope) {
376
380
  FirmwareArtifactLease(
377
381
  leaseRef = FirmwareArtifactStore.createLease(params.transactionId),
378
382
  )
@@ -382,7 +386,7 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
382
386
  override fun retainFirmwareArtifact(
383
387
  params: FirmwareArtifactLeaseRetainParams,
384
388
  ): Promise<Unit> {
385
- return Promise.async {
389
+ return Promise.async(firmwareArtifactScope) {
386
390
  FirmwareArtifactStore.retain(params.leaseRef, params.artifactRef)
387
391
  }
388
392
  }
@@ -390,21 +394,13 @@ class ReactNativeRangeDownloader : HybridReactNativeRangeDownloaderSpec() {
390
394
  override fun releaseFirmwareArtifactLease(
391
395
  params: FirmwareArtifactLeaseReleaseParams,
392
396
  ): Promise<Unit> {
393
- return Promise.async {
397
+ return Promise.async(firmwareArtifactScope) {
394
398
  FirmwareArtifactStore.releaseLease(params.leaseRef, params.disposition)
395
399
  }
396
400
  }
397
401
 
398
- override fun reconcileFirmwareArtifactLeases(
399
- params: FirmwareArtifactLeaseReconcileParams,
400
- ): Promise<Unit> {
401
- return Promise.async {
402
- FirmwareArtifactStore.reconcileLeases(params.activeLeaseRefs)
403
- }
404
- }
405
-
406
402
  override fun sweepFirmwareArtifactOrphans(): Promise<FirmwareArtifactSweepResult> {
407
- return Promise.async {
403
+ return Promise.async(firmwareArtifactScope) {
408
404
  val (deletedFiles, deletedBytes) = FirmwareArtifactStore.sweepOrphans()
409
405
  FirmwareArtifactSweepResult(
410
406
  deletedFiles = deletedFiles.toDouble(),
@@ -1,10 +1,19 @@
1
1
  package com.margelo.nitro.reactnativerangedownloader
2
2
 
3
+ import java.io.File
4
+ import java.io.FileOutputStream
5
+ import java.util.zip.ZipEntry
6
+ import java.util.zip.ZipOutputStream
3
7
  import org.junit.Assert.assertEquals
4
8
  import org.junit.Assert.assertThrows
9
+ import org.junit.Rule
5
10
  import org.junit.Test
11
+ import org.junit.rules.TemporaryFolder
6
12
 
7
13
  class FirmwareArchiveRulesTest {
14
+ @get:Rule
15
+ val temporaryFolder = TemporaryFolder()
16
+
8
17
  private fun entry(
9
18
  artifactId: String = "resource-entry",
10
19
  entryName: String = "assets/icon.png",
@@ -72,4 +81,34 @@ class FirmwareArchiveRulesTest {
72
81
  )
73
82
  }
74
83
  }
84
+
85
+ @Test
86
+ fun acceptsPortableEntriesWithoutExpectedIntegrityMetadata() {
87
+ val archive = createArchive("assets/icon.png", byteArrayOf(1, 2, 3))
88
+
89
+ val entries = FirmwareArchiveRules.validateCentralDirectory(archive, null)
90
+
91
+ assertEquals(1, entries.size)
92
+ assertEquals("assets/icon.png", entries.single().name)
93
+ assertEquals(3, entries.single().uncompressedSize)
94
+ }
95
+
96
+ @Test
97
+ fun rejectsTraversalWithoutExpectedIntegrityMetadata() {
98
+ val archive = createArchive("../icon.png", byteArrayOf(1))
99
+
100
+ assertThrows(IllegalArgumentException::class.java) {
101
+ FirmwareArchiveRules.validateCentralDirectory(archive, null)
102
+ }
103
+ }
104
+
105
+ private fun createArchive(entryName: String, content: ByteArray): File {
106
+ val archive = temporaryFolder.newFile("firmware.zip")
107
+ ZipOutputStream(FileOutputStream(archive)).use { zip ->
108
+ zip.putNextEntry(ZipEntry(entryName))
109
+ zip.write(content)
110
+ zip.closeEntry()
111
+ }
112
+ return archive
113
+ }
75
114
  }
@@ -0,0 +1,26 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertThrows
5
+ import org.junit.Test
6
+
7
+ class FirmwareArtifactDeadlineTest {
8
+ @Test
9
+ fun defaultsToABoundedThreeMinuteDeadline() {
10
+ assertEquals(180.0, validateFirmwareDownloadDeadlineSeconds(null), 0.0)
11
+ }
12
+
13
+ @Test
14
+ fun preservesFractionalDeadlines() {
15
+ assertEquals(2.75, validateFirmwareDownloadDeadlineSeconds(2.75), 0.0)
16
+ }
17
+
18
+ @Test
19
+ fun rejectsUnboundedOrInvalidDeadlines() {
20
+ listOf(0.0, -1.0, Double.NaN, Double.POSITIVE_INFINITY, 86_400.1).forEach {
21
+ assertThrows(IllegalArgumentException::class.java) {
22
+ validateFirmwareDownloadDeadlineSeconds(it)
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,107 @@
1
+ package com.margelo.nitro.reactnativerangedownloader
2
+
3
+ import java.io.File
4
+ import org.junit.Assert.assertEquals
5
+ import org.junit.Assert.assertFalse
6
+ import org.junit.Assert.assertTrue
7
+ import org.junit.Rule
8
+ import org.junit.Test
9
+ import org.junit.rules.TemporaryFolder
10
+
11
+ class FirmwareArtifactOrphanSweepTest {
12
+ @get:Rule
13
+ val temporaryFolder = TemporaryFolder()
14
+
15
+ @Test
16
+ fun removesOnlyStaleRootScratchEntriesWithExactNamesAndTypes() {
17
+ val root = temporaryFolder.newFolder("firmware-artifact-sweep")
18
+ val nowMs = 2_000_000_000_000L
19
+ val staleAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS - 1
20
+ val freshAt = nowMs - FIRMWARE_ARTIFACT_SCRATCH_GRACE_MS + 1
21
+ val staleArchive = File(
22
+ root,
23
+ "archive-00000000-0000-4000-8000-000000000001",
24
+ ).apply {
25
+ assertTrue(mkdirs())
26
+ File(this, "0.entry").writeText("archive")
27
+ }
28
+ val stalePromote = File(
29
+ root,
30
+ ".promote-00000000-0000-4000-8000-000000000002",
31
+ ).apply { writeText("promote") }
32
+ val freshArchive = File(
33
+ root,
34
+ "archive-00000000-0000-4000-8000-000000000003",
35
+ ).apply { assertTrue(mkdirs()) }
36
+ val freshPromote = File(
37
+ root,
38
+ ".promote-00000000-0000-4000-8000-000000000004",
39
+ ).apply { writeText("promote") }
40
+ val malformedArchive = File(
41
+ root,
42
+ "archive-00000000-0000-4000-8000-000000000005.extra",
43
+ ).apply { assertTrue(mkdirs()) }
44
+ val malformedPromote = File(
45
+ root,
46
+ ".promote-00000000-0000-4000-8000-000000000006.tmp",
47
+ ).apply { writeText("promote") }
48
+ val archiveNamedFile = File(
49
+ root,
50
+ "archive-00000000-0000-4000-8000-000000000007",
51
+ ).apply { writeText("promote") }
52
+ val promoteNamedDirectory = File(
53
+ root,
54
+ ".promote-00000000-0000-4000-8000-000000000008",
55
+ ).apply { assertTrue(mkdirs()) }
56
+ val nestedArchive = File(
57
+ File(root, "nested"),
58
+ "archive-00000000-0000-4000-8000-000000000009",
59
+ ).apply { assertTrue(mkdirs()) }
60
+
61
+ for (
62
+ entry in listOf(
63
+ staleArchive,
64
+ stalePromote,
65
+ malformedArchive,
66
+ malformedPromote,
67
+ archiveNamedFile,
68
+ promoteNamedDirectory,
69
+ nestedArchive,
70
+ )
71
+ ) {
72
+ assertTrue(entry.setLastModified(staleAt))
73
+ }
74
+ for (entry in listOf(freshArchive, freshPromote)) {
75
+ assertTrue(entry.setLastModified(freshAt))
76
+ }
77
+
78
+ val result = sweepFirmwareArtifactOrphansAtRoot(
79
+ root = root,
80
+ retainedSha256 = emptySet(),
81
+ activeSha256 = emptySet(),
82
+ openPaths = emptySet(),
83
+ nowMs = nowMs,
84
+ )
85
+
86
+ assertEquals(2, result.first)
87
+ assertEquals(14L, result.second)
88
+ assertFalse(staleArchive.exists())
89
+ assertFalse(stalePromote.exists())
90
+ for (
91
+ retainedEntry in listOf(
92
+ freshArchive,
93
+ freshPromote,
94
+ malformedArchive,
95
+ malformedPromote,
96
+ archiveNamedFile,
97
+ promoteNamedDirectory,
98
+ nestedArchive,
99
+ )
100
+ ) {
101
+ assertTrue(
102
+ "Unexpectedly removed ${retainedEntry.name}",
103
+ retainedEntry.exists(),
104
+ )
105
+ }
106
+ }
107
+ }