@pigeonmal/react-native-nitro-fetch 0.1.6

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 (104) hide show
  1. package/NitroFetch.podspec +30 -0
  2. package/android/CMakeLists.txt +70 -0
  3. package/android/build.gradle +130 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  7. package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +72 -0
  8. package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +58 -0
  9. package/android/src/main/java/com/margelo/nitro/nitrofetch/NativeStorage.kt +102 -0
  10. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetch.kt +94 -0
  11. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +331 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchPackage.kt +22 -0
  13. package/ios/FetchCache.swift +56 -0
  14. package/ios/NativeStorage.swift +61 -0
  15. package/ios/NitroAutoPrefetcher.swift +45 -0
  16. package/ios/NitroBootstrap.mm +27 -0
  17. package/ios/NitroFetch.swift +9 -0
  18. package/ios/NitroFetchClient.swift +230 -0
  19. package/lib/module/NitroFetch.nitro.js +4 -0
  20. package/lib/module/NitroFetch.nitro.js.map +1 -0
  21. package/lib/module/NitroInstances.js +8 -0
  22. package/lib/module/NitroInstances.js.map +1 -0
  23. package/lib/module/fetch.js +522 -0
  24. package/lib/module/fetch.js.map +1 -0
  25. package/lib/module/index.js +12 -0
  26. package/lib/module/index.js.map +1 -0
  27. package/lib/module/package.json +1 -0
  28. package/lib/module/type.js +2 -0
  29. package/lib/module/type.js.map +1 -0
  30. package/lib/typescript/package.json +1 -0
  31. package/lib/typescript/src/NitroFetch.nitro.d.ts +48 -0
  32. package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -0
  33. package/lib/typescript/src/NitroInstances.d.ts +5 -0
  34. package/lib/typescript/src/NitroInstances.d.ts.map +1 -0
  35. package/lib/typescript/src/fetch.d.ts +28 -0
  36. package/lib/typescript/src/fetch.d.ts.map +1 -0
  37. package/lib/typescript/src/index.d.ts +6 -0
  38. package/lib/typescript/src/index.d.ts.map +1 -0
  39. package/lib/typescript/src/type.d.ts +4 -0
  40. package/lib/typescript/src/type.d.ts.map +1 -0
  41. package/nitro.json +25 -0
  42. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +54 -0
  43. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +66 -0
  44. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +96 -0
  45. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +66 -0
  46. package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.cpp +49 -0
  47. package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.hpp +64 -0
  48. package/nitrogen/generated/android/c++/JNitroHeader.hpp +57 -0
  49. package/nitrogen/generated/android/c++/JNitroRequest.hpp +100 -0
  50. package/nitrogen/generated/android/c++/JNitroRequestMethod.hpp +74 -0
  51. package/nitrogen/generated/android/c++/JNitroResponse.hpp +102 -0
  52. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +60 -0
  53. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +60 -0
  54. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchSpec.kt +52 -0
  55. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroHeader.kt +32 -0
  56. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +47 -0
  57. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequestMethod.kt +26 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroResponse.kt +50 -0
  59. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/nitrofetchOnLoad.kt +35 -0
  60. package/nitrogen/generated/android/nitrofetch+autolinking.cmake +85 -0
  61. package/nitrogen/generated/android/nitrofetch+autolinking.gradle +27 -0
  62. package/nitrogen/generated/android/nitrofetchOnLoad.cpp +64 -0
  63. package/nitrogen/generated/android/nitrofetchOnLoad.hpp +25 -0
  64. package/nitrogen/generated/ios/NitroFetch+autolinking.rb +60 -0
  65. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +90 -0
  66. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +321 -0
  67. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +69 -0
  68. package/nitrogen/generated/ios/NitroFetchAutolinking.mm +49 -0
  69. package/nitrogen/generated/ios/NitroFetchAutolinking.swift +55 -0
  70. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +11 -0
  71. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +85 -0
  72. package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.cpp +11 -0
  73. package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +103 -0
  74. package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.cpp +11 -0
  75. package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.hpp +75 -0
  76. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  77. package/nitrogen/generated/ios/swift/Func_void_NitroResponse.swift +47 -0
  78. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  79. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +51 -0
  80. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +145 -0
  81. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +51 -0
  82. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +161 -0
  83. package/nitrogen/generated/ios/swift/HybridNitroFetchSpec.swift +49 -0
  84. package/nitrogen/generated/ios/swift/HybridNitroFetchSpec_cxx.swift +126 -0
  85. package/nitrogen/generated/ios/swift/NitroHeader.swift +46 -0
  86. package/nitrogen/generated/ios/swift/NitroRequest.swift +206 -0
  87. package/nitrogen/generated/ios/swift/NitroRequestMethod.swift +60 -0
  88. package/nitrogen/generated/ios/swift/NitroResponse.swift +162 -0
  89. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +23 -0
  90. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +64 -0
  91. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +23 -0
  92. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +69 -0
  93. package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.cpp +21 -0
  94. package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.hpp +64 -0
  95. package/nitrogen/generated/shared/c++/NitroHeader.hpp +71 -0
  96. package/nitrogen/generated/shared/c++/NitroRequest.hpp +98 -0
  97. package/nitrogen/generated/shared/c++/NitroRequestMethod.hpp +96 -0
  98. package/nitrogen/generated/shared/c++/NitroResponse.hpp +99 -0
  99. package/package.json +162 -0
  100. package/src/NitroFetch.nitro.ts +67 -0
  101. package/src/NitroInstances.ts +14 -0
  102. package/src/fetch.ts +603 -0
  103. package/src/index.tsx +17 -0
  104. package/src/type.ts +3 -0
@@ -0,0 +1,30 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NitroFetch"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "http://google.com.git", :tag => "#{s.version}" }
15
+
16
+
17
+ s.source_files = [
18
+ "ios/**/*.{swift}",
19
+ "ios/**/*.{m,mm}",
20
+ "cpp/**/*.{hpp,cpp}",
21
+ ]
22
+
23
+ s.dependency 'React-jsi'
24
+ s.dependency 'React-callinvoker'
25
+
26
+ load 'nitrogen/generated/ios/NitroFetch+autolinking.rb'
27
+ add_nitrogen_files(s)
28
+
29
+ install_modules_dependencies(s)
30
+ end
@@ -0,0 +1,70 @@
1
+ project(nitrofetch)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME nitrofetch)
5
+ set(CMAKE_VERBOSE_MAKEFILE ON)
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED
10
+ src/main/cpp/cpp-adapter.cpp
11
+ )
12
+
13
+ # Add Nitrogen specs :)
14
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/nitrofetch+autolinking.cmake)
15
+
16
+ # Set up local includes
17
+ include_directories("src/main/cpp" "../cpp")
18
+
19
+ # Allow Gradle to pass a CRONET_ROOT pointing to extracted headers/libs (see build.gradle prepareCronet task)
20
+ if (DEFINED CRONET_ROOT)
21
+ set(CRONET_ROOT_DIR ${CRONET_ROOT})
22
+ else()
23
+ set(CRONET_ROOT_DIR ${CMAKE_SOURCE_DIR}/cronet)
24
+ endif()
25
+
26
+ # Optional: include Cronet headers if present (placed by script under android/cronet/include)
27
+ if (EXISTS ${CRONET_ROOT_DIR}/include)
28
+ message(STATUS "Cronet headers base: ${CRONET_ROOT_DIR}/include")
29
+ include_directories(${CRONET_ROOT_DIR}/include)
30
+ # Some Cronet packages nest headers under include/cronet
31
+ if (EXISTS ${CRONET_ROOT_DIR}/include/cronet)
32
+ include_directories(${CRONET_ROOT_DIR}/include/cronet)
33
+ if (EXISTS ${CRONET_ROOT_DIR}/include/cronet/cronet_c.h)
34
+ message(STATUS "Found cronet_c.h: ${CRONET_ROOT_DIR}/include/cronet/cronet_c.h")
35
+ else()
36
+ message(WARNING "cronet_c.h not found under ${CRONET_ROOT_DIR}/include/cronet")
37
+ endif()
38
+ if (EXISTS ${CRONET_ROOT_DIR}/include/cronet/cronet.idl_c.h)
39
+ message(STATUS "Found cronet.idl_c.h: ${CRONET_ROOT_DIR}/include/cronet/cronet.idl_c.h")
40
+ else()
41
+ message(WARNING "cronet.idl_c.h not found under ${CRONET_ROOT_DIR}/include/cronet")
42
+ endif()
43
+ else()
44
+ message(WARNING "Cronet nested include dir not found: ${CRONET_ROOT_DIR}/include/cronet")
45
+ endif()
46
+ endif()
47
+
48
+ find_library(LOG_LIB log)
49
+
50
+ # Link all libraries together
51
+ target_link_libraries(
52
+ ${PACKAGE_NAME}
53
+ ${LOG_LIB}
54
+ android # <-- Android core
55
+ )
56
+
57
+ ## Kotlin-based implementation only; no custom C++ headers forced.
58
+
59
+ # Optional: link Cronet if library file is present (drop-in via prepare script or Gradle task)
60
+ set(CRONET_LIB_DIR ${CRONET_ROOT_DIR}/libs/${ANDROID_ABI})
61
+ if (EXISTS ${CRONET_LIB_DIR})
62
+ file(GLOB CRONET_LIBS "${CRONET_LIB_DIR}/*cronet*.so")
63
+ if (CRONET_LIBS)
64
+ message(STATUS "Linking Cronet from ${CRONET_LIB_DIR}")
65
+ target_link_libraries(${PACKAGE_NAME} ${CRONET_LIBS})
66
+ target_compile_definitions(${PACKAGE_NAME} PRIVATE NITROFETCH_LINKS_CRONET=1)
67
+ else()
68
+ message(WARNING "Cronet libs not found in ${CRONET_LIB_DIR}")
69
+ endif()
70
+ endif()
@@ -0,0 +1,130 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['NitroFetch_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+ def reactNativeArchitectures() {
19
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
20
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
21
+ }
22
+
23
+ apply plugin: "com.android.library"
24
+ apply plugin: "kotlin-android"
25
+ apply from: '../nitrogen/generated/android/nitrofetch+autolinking.gradle'
26
+
27
+ // Do not apply React Gradle plugin in library to avoid RN codegen duplicating app classes
28
+
29
+ def getExtOrIntegerDefault(name) {
30
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroFetch_" + name]).toInteger()
31
+ }
32
+
33
+ android {
34
+ namespace "com.margelo.nitro.nitrofetch"
35
+
36
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
37
+
38
+ defaultConfig {
39
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
40
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
41
+
42
+ externalNativeBuild {
43
+ cmake {
44
+ cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
45
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
46
+ abiFilters (*reactNativeArchitectures())
47
+ }
48
+ }
49
+ }
50
+
51
+ externalNativeBuild {
52
+ cmake {
53
+ path "CMakeLists.txt"
54
+ }
55
+ }
56
+
57
+ packagingOptions {
58
+ excludes = [
59
+ "**/libc++_shared.so",
60
+ "**/libfbjni.so",
61
+ "**/libjsi.so",
62
+ "**/libfolly_json.so",
63
+ "**/libfolly_runtime.so",
64
+ "**/libglog.so",
65
+ "**/libhermes.so",
66
+ "**/libhermes-executor-debug.so",
67
+ "**/libhermes_executor.so",
68
+ "**/libreactnative.so",
69
+ "**/libreactnativejni.so",
70
+ "**/libturbomodulejsijni.so",
71
+ "**/libreact_nativemodule_core.so",
72
+ "**/libjscexecutor.so"
73
+ ]
74
+ }
75
+
76
+ buildFeatures {
77
+ buildConfig true
78
+ prefab true
79
+ }
80
+
81
+ buildTypes {
82
+ release {
83
+ minifyEnabled false
84
+ }
85
+ }
86
+
87
+ lintOptions {
88
+ disable "GradleCompatible"
89
+ }
90
+
91
+ compileOptions {
92
+ sourceCompatibility JavaVersion.VERSION_1_8
93
+ targetCompatibility JavaVersion.VERSION_1_8
94
+ }
95
+
96
+ sourceSets {
97
+ main {
98
+ java.srcDirs += [
99
+ "generated/java",
100
+ "generated/jni"
101
+ ]
102
+ }
103
+ }
104
+ }
105
+
106
+ repositories {
107
+ mavenCentral()
108
+ google()
109
+ }
110
+
111
+ def kotlin_version = getExtOrDefault("kotlinVersion")
112
+
113
+ dependencies {
114
+ implementation "com.facebook.react:react-android"
115
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
116
+ implementation project(":react-native-nitro-modules")
117
+ // Provide org.chromium.net Java API for CronetEngine in Kotlin
118
+ // Cronet
119
+ api "com.google.android.gms:play-services-cronet:18.0.1"
120
+ implementation("com.squareup.okio:okio:2.9.0")
121
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
122
+
123
+ }
124
+
125
+ configurations {
126
+ cronetAar
127
+ }
128
+
129
+
130
+ // No automatic fetching of Cronet headers/libs; library uses Cronet Java API only.
@@ -0,0 +1,5 @@
1
+ NitroFetch_kotlinVersion=2.0.21
2
+ NitroFetch_minSdkVersion=24
3
+ NitroFetch_targetSdkVersion=34
4
+ NitroFetch_compileSdkVersion=35
5
+ NitroFetch_ndkVersion=27.1.12297006
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,6 @@
1
+ #include <jni.h>
2
+ #include "nitrofetchOnLoad.hpp"
3
+
4
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
+ return margelo::nitro::nitrofetch::initialize(vm);
6
+ }
@@ -0,0 +1,72 @@
1
+ package com.margelo.nitro.nitrofetch
2
+
3
+ import android.app.Application
4
+ import android.content.Context
5
+ import org.json.JSONArray
6
+ import org.json.JSONObject
7
+ import java.util.concurrent.CompletableFuture
8
+
9
+
10
+ object AutoPrefetcher {
11
+ @Volatile private var initialized = false
12
+ private const val KEY_QUEUE = "nitrofetch_autoprefetch_queue"
13
+ private const val PREFS_NAME = "nitro_fetch_storage"
14
+
15
+ fun prefetchOnStart(app: Application) {
16
+ if (initialized) return
17
+ initialized = true
18
+ try {
19
+ val prefs = app.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
20
+ val raw = prefs.getString(KEY_QUEUE, null) ?: ""
21
+ if (raw.isEmpty()) return
22
+ val arr = JSONArray(raw)
23
+ for (i in 0 until arr.length()) {
24
+ val o = arr.optJSONObject(i) ?: continue
25
+ val url = o.optString("url", null) ?: continue
26
+ val prefetchKey = o.optString("prefetchKey", null) ?: continue
27
+ val headersObj = o.optJSONObject("headers") ?: JSONObject()
28
+ val headersList = mutableListOf<Pair<String, String>>()
29
+ headersObj.keys().forEachRemaining { k ->
30
+ headersList.add(k to headersObj.optString(k, ""))
31
+ }
32
+ // Ensure prefetchKey header is present
33
+ headersList.add("prefetchKey" to prefetchKey)
34
+
35
+ val headerObjs = headersList.map { (k, v) -> NitroHeader(k, v) }.toTypedArray()
36
+ val req = NitroRequest(
37
+ url = url,
38
+ method = null,
39
+ headers = headerObjs,
40
+ bodyString = null,
41
+ bodyBytes = null,
42
+ timeoutMs = null,
43
+ followRedirects = null
44
+ )
45
+
46
+ // If already pending or fresh, skip starting a new one
47
+ if (FetchCache.getPending(prefetchKey) != null) continue
48
+ if (FetchCache.hasFreshResult(prefetchKey, 5_000L)) continue
49
+
50
+ val future = CompletableFuture<NitroResponse>()
51
+ FetchCache.setPending(prefetchKey, future)
52
+ NitroFetchClient.fetch(req,
53
+ onSuccess = { res ->
54
+ try {
55
+ FetchCache.complete(prefetchKey, res)
56
+ future.complete(res)
57
+ } catch (t: Throwable) {
58
+ FetchCache.completeExceptionally(prefetchKey, t)
59
+ future.completeExceptionally(t)
60
+ }
61
+ },
62
+ onFail = { err ->
63
+ FetchCache.completeExceptionally(prefetchKey, err)
64
+ future.completeExceptionally(err)
65
+ }
66
+ )
67
+ }
68
+ } catch (_: Throwable) {
69
+ // ignore – prefetch-on-start is best-effort
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,58 @@
1
+ package com.margelo.nitro.nitrofetch
2
+
3
+ import java.util.concurrent.CompletableFuture
4
+ import java.util.concurrent.ConcurrentHashMap
5
+
6
+ data class CachedEntry(val response: NitroResponse, val timestampMs: Long)
7
+
8
+ object FetchCache {
9
+ private val pending = ConcurrentHashMap<String, CompletableFuture<NitroResponse>>()
10
+ private val results = ConcurrentHashMap<String, CachedEntry>()
11
+
12
+ fun getPending(key: String): CompletableFuture<NitroResponse>? = pending[key]
13
+
14
+ fun setPending(key: String, future: CompletableFuture<NitroResponse>) {
15
+ pending[key] = future
16
+ // Cleanup: remove pending entry when completed
17
+ future.whenComplete { _, _ ->
18
+ pending.remove(key)
19
+ }
20
+ }
21
+
22
+ fun complete(key: String, value: NitroResponse) {
23
+ results[key] = CachedEntry(value, System.currentTimeMillis())
24
+ pending[key]?.complete(value)
25
+ pending.remove(key)
26
+ }
27
+
28
+ fun completeExceptionally(key: String, t: Throwable) {
29
+ pending[key]?.completeExceptionally(t)
30
+ pending.remove(key)
31
+ }
32
+
33
+ fun getResult(key: String): NitroResponse? {
34
+ val entry = results.remove(key) ?: return null
35
+ return entry.response
36
+ }
37
+
38
+ fun getResultIfFresh(key: String, maxAgeMs: Long): NitroResponse? {
39
+ val entry = results.remove(key) ?: return null
40
+ val age = System.currentTimeMillis() - entry.timestampMs
41
+ return if (age <= maxAgeMs) entry.response else null
42
+ }
43
+
44
+ /**
45
+ * Check if a fresh result exists WITHOUT consuming it.
46
+ * Used to check if we should skip starting a new prefetch.
47
+ */
48
+ fun hasFreshResult(key: String, maxAgeMs: Long): Boolean {
49
+ val entry = results[key] ?: return false
50
+ val age = System.currentTimeMillis() - entry.timestampMs
51
+ return age <= maxAgeMs
52
+ }
53
+
54
+ fun clear() {
55
+ pending.clear()
56
+ results.clear()
57
+ }
58
+ }
@@ -0,0 +1,102 @@
1
+ package com.margelo.nitro.nitrofetch
2
+
3
+ import android.app.Application
4
+ import android.content.Context
5
+ import android.content.SharedPreferences
6
+ import android.util.Log
7
+ import com.facebook.proguard.annotations.DoNotStrip
8
+ import com.margelo.nitro.NitroModules
9
+
10
+
11
+
12
+ @DoNotStrip
13
+ class NativeStorage : HybridNativeStorageSpec() {
14
+
15
+ companion object {
16
+ private const val TAG = "HybridNativeStorage"
17
+ private const val PREFS_NAME = "nitro_fetch_storage"
18
+
19
+
20
+ private val sharedPreferences: SharedPreferences by lazy {
21
+ val context = NitroModules.applicationContext ?: throw Error("Cannot get Android Context - No Context available!")
22
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
23
+ }
24
+
25
+
26
+ }
27
+
28
+ /**
29
+ * Retrieves a string value for the given key.
30
+ *
31
+ * @param key The key to look up in storage
32
+ * @return The stored string value, or empty string if key doesn't exist
33
+ * @throws IllegalStateException if SharedPreferences is not available
34
+ */
35
+ override fun getString(key: String): String {
36
+ return try {
37
+ val value = sharedPreferences.getString(key, null)
38
+ if (value != null) {
39
+ Log.d(TAG, "Retrieved value for key: $key")
40
+ value
41
+ } else {
42
+ Log.d(TAG, "Key not found: $key, returning empty string")
43
+ ""
44
+ }
45
+ } catch (t: Throwable) {
46
+ Log.e(TAG, "Error getting string for key: $key", t)
47
+ throw RuntimeException("Failed to get string for key: $key", t)
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Stores a string value with the given key.
53
+ *
54
+ * @param key The key to store the value under
55
+ * @param value The string value to store
56
+ * @throws IllegalStateException if SharedPreferences is not available
57
+ * @throws RuntimeException if the write operation fails
58
+ */
59
+ override fun setString(key: String, value: String) {
60
+ try {
61
+ val editor = sharedPreferences.edit()
62
+ editor.putString(key, value)
63
+ val success = editor.commit() // commit() is synchronous and returns boolean
64
+ if (success) {
65
+ Log.d(TAG, "Successfully stored value for key: $key")
66
+ } else {
67
+ Log.e(TAG, "Failed to commit value for key: $key")
68
+ throw RuntimeException("Failed to store value for key: $key")
69
+ }
70
+ } catch (t: Throwable) {
71
+ Log.e(TAG, "Error setting string for key: $key", t)
72
+ throw RuntimeException("Failed to set string for key: $key", t)
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Deletes the value associated with the given key.
78
+ * If the key doesn't exist, this is a no-op.
79
+ *
80
+ * @param key The key to delete from storage
81
+ * @throws IllegalStateException if SharedPreferences is not available
82
+ * @throws RuntimeException if the delete operation fails
83
+ */
84
+ override fun removeString(key: String) {
85
+ try {
86
+ val editor = sharedPreferences.edit()
87
+ editor.remove(key)
88
+ val success = editor.commit() // commit() is synchronous and returns boolean
89
+ if (success) {
90
+ Log.d(TAG, "Successfully deleted key: $key")
91
+ } else {
92
+ Log.e(TAG, "Failed to commit deletion for key: $key")
93
+ throw RuntimeException("Failed to delete key: $key")
94
+ }
95
+ } catch (t: Throwable) {
96
+ Log.e(TAG, "Error deleting key: $key", t)
97
+ throw RuntimeException("Failed to delete key: $key", t)
98
+ }
99
+ }
100
+ }
101
+
102
+
@@ -0,0 +1,94 @@
1
+ package com.margelo.nitro.nitrofetch
2
+
3
+ import android.app.Application
4
+ import android.util.Log
5
+ import com.facebook.proguard.annotations.DoNotStrip
6
+ import org.chromium.net.CronetEngine
7
+ import org.chromium.net.CronetProvider
8
+ import java.io.File
9
+ import java.util.concurrent.Executor
10
+ import java.util.concurrent.Executors
11
+
12
+ @DoNotStrip
13
+ class NitroFetch : HybridNitroFetchSpec() {
14
+ // Generated base may expect env-less createClient.
15
+ override fun createClient(): NitroFetchClient {
16
+ return NitroFetchClient(getEngine(), ioExecutor)
17
+ }
18
+
19
+ companion object {
20
+ @Volatile private var engineRef: CronetEngine? = null
21
+
22
+ // Simpler & safer for callbacks than a pool (avoid reentrancy races in glue code).
23
+ val ioExecutor: Executor by lazy {
24
+ val cores = Runtime.getRuntime().availableProcessors().coerceAtLeast(2)
25
+ Executors.newFixedThreadPool(cores) { r ->
26
+ Thread(r, "NitroCronet-io").apply {
27
+ isDaemon = true
28
+ priority = Thread.NORM_PRIORITY
29
+ }
30
+ }
31
+ }
32
+
33
+ fun getEngine(): CronetEngine {
34
+ engineRef?.let { return it }
35
+ synchronized(this) {
36
+ engineRef?.let { return it }
37
+
38
+ val app = currentApplication() ?: initialApplication()
39
+ ?: throw IllegalStateException("NitroFetch: Application not available")
40
+
41
+ // Log available providers and prefer the Native one (avoids Play-Services DNS quirks)
42
+ val providers = CronetProvider.getAllProviders(app)
43
+ providers.forEach { Log.i("NitroFetch", "Cronet provider: ${it.name} v=${it.version}") }
44
+ val nativeProvider = providers.firstOrNull { it.name.contains("Native", ignoreCase = true) }
45
+
46
+ val cacheDir = File(app.cacheDir, "nitrofetch_cronet_cache").apply { mkdirs() }
47
+ val builder = (nativeProvider?.createBuilder() ?: CronetEngine.Builder(app))
48
+ .enableHttp2(true)
49
+ .enableQuic(true)
50
+ .enableBrotli(true)
51
+ .setStoragePath(cacheDir.absolutePath)
52
+ .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 10 * 1024 * 1024)
53
+ .setUserAgent("NitroFetch/0.1")
54
+
55
+
56
+ // --- Optional debugging knobs (uncomment temporarily) ---
57
+ // Enable NetLog-like tracing in NetworkService:
58
+ // builder.setExperimentalOptions("""{"NetworkService":{"enable_network_logging":true}}""")
59
+ //
60
+ // Prove DNS issues by mapping a host (TESTING ONLY, remove in prod):
61
+ // builder.setExperimentalOptions("""{"HostResolverRules":{"host_resolver_rules":"MAP httpbin.org 54.167.17.38"}}""")
62
+
63
+ val engine = builder.build()
64
+ Log.i("NitroFetch", "CronetEngine initialized. Provider=${nativeProvider?.name ?: "Default"} Cache=${cacheDir.absolutePath}")
65
+ engineRef = engine
66
+ return engine
67
+ }
68
+ }
69
+
70
+ fun shutdown() {
71
+ synchronized(this) {
72
+ try {
73
+ engineRef?.shutdown()
74
+ } catch (_: Throwable) {
75
+ // ignore – shutdown is best-effort
76
+ } finally {
77
+ engineRef = null
78
+ }
79
+ }
80
+ }
81
+
82
+ private fun currentApplication(): Application? = try {
83
+ val cls = Class.forName("android.app.ActivityThread")
84
+ val m = cls.getMethod("currentApplication")
85
+ m.invoke(null) as? Application
86
+ } catch (_: Throwable) { null }
87
+
88
+ private fun initialApplication(): Application? = try {
89
+ val cls = Class.forName("android.app.AppGlobals")
90
+ val m = cls.getMethod("getInitialApplication")
91
+ m.invoke(null) as? Application
92
+ } catch (_: Throwable) { null }
93
+ }
94
+ }