@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.
- package/NitroFetch.podspec +30 -0
- package/android/CMakeLists.txt +70 -0
- package/android/build.gradle +130 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +72 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +58 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NativeStorage.kt +102 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetch.kt +94 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +331 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchPackage.kt +22 -0
- package/ios/FetchCache.swift +56 -0
- package/ios/NativeStorage.swift +61 -0
- package/ios/NitroAutoPrefetcher.swift +45 -0
- package/ios/NitroBootstrap.mm +27 -0
- package/ios/NitroFetch.swift +9 -0
- package/ios/NitroFetchClient.swift +230 -0
- package/lib/module/NitroFetch.nitro.js +4 -0
- package/lib/module/NitroFetch.nitro.js.map +1 -0
- package/lib/module/NitroInstances.js +8 -0
- package/lib/module/NitroInstances.js.map +1 -0
- package/lib/module/fetch.js +522 -0
- package/lib/module/fetch.js.map +1 -0
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/type.js +2 -0
- package/lib/module/type.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts +48 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -0
- package/lib/typescript/src/NitroInstances.d.ts +5 -0
- package/lib/typescript/src/NitroInstances.d.ts.map +1 -0
- package/lib/typescript/src/fetch.d.ts +28 -0
- package/lib/typescript/src/fetch.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/type.d.ts +4 -0
- package/lib/typescript/src/type.d.ts.map +1 -0
- package/nitro.json +25 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +54 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +96 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.cpp +49 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.hpp +64 -0
- package/nitrogen/generated/android/c++/JNitroHeader.hpp +57 -0
- package/nitrogen/generated/android/c++/JNitroRequest.hpp +100 -0
- package/nitrogen/generated/android/c++/JNitroRequestMethod.hpp +74 -0
- package/nitrogen/generated/android/c++/JNitroResponse.hpp +102 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchSpec.kt +52 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroHeader.kt +32 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequestMethod.kt +26 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroResponse.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/nitrofetchOnLoad.kt +35 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.cmake +85 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrofetchOnLoad.cpp +64 -0
- package/nitrogen/generated/android/nitrofetchOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/NitroFetch+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +90 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +321 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +69 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.mm +49 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.swift +55 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +85 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +103 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.hpp +75 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_NitroResponse.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +51 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +145 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +51 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +161 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchSpec.swift +49 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchSpec_cxx.swift +126 -0
- package/nitrogen/generated/ios/swift/NitroHeader.swift +46 -0
- package/nitrogen/generated/ios/swift/NitroRequest.swift +206 -0
- package/nitrogen/generated/ios/swift/NitroRequestMethod.swift +60 -0
- package/nitrogen/generated/ios/swift/NitroResponse.swift +162 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +69 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/NitroHeader.hpp +71 -0
- package/nitrogen/generated/shared/c++/NitroRequest.hpp +98 -0
- package/nitrogen/generated/shared/c++/NitroRequestMethod.hpp +96 -0
- package/nitrogen/generated/shared/c++/NitroResponse.hpp +99 -0
- package/package.json +162 -0
- package/src/NitroFetch.nitro.ts +67 -0
- package/src/NitroInstances.ts +14 -0
- package/src/fetch.ts +603 -0
- package/src/index.tsx +17 -0
- package/src/type.ts +3 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import NitroModules
|
|
3
|
+
|
|
4
|
+
final class NitroFetchClient: HybridNitroFetchClientSpec {
|
|
5
|
+
func requestSync(req: NitroRequest) throws -> NitroResponse {
|
|
6
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
7
|
+
var result: Result<NitroResponse, Error>?
|
|
8
|
+
|
|
9
|
+
Task {
|
|
10
|
+
do {
|
|
11
|
+
let response = try await NitroFetchClient.requestStatic(req)
|
|
12
|
+
result = .success(response)
|
|
13
|
+
} catch {
|
|
14
|
+
result = .failure(error)
|
|
15
|
+
}
|
|
16
|
+
semaphore.signal()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
semaphore.wait()
|
|
20
|
+
|
|
21
|
+
switch result! {
|
|
22
|
+
case .success(let response):
|
|
23
|
+
return response
|
|
24
|
+
case .failure(let error):
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Async version - returns Promise<NitroResponse>
|
|
30
|
+
func request(req: NitroRequest) throws -> Promise<NitroResponse> {
|
|
31
|
+
let promise = Promise<NitroResponse>.init()
|
|
32
|
+
Task {
|
|
33
|
+
do {
|
|
34
|
+
let response = try await NitroFetchClient.requestStatic(req)
|
|
35
|
+
promise.resolve(withResult: response)
|
|
36
|
+
} catch {
|
|
37
|
+
promise.reject(withError: error)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return promise
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func prefetch(req: NitroRequest) throws -> Promise<Void> {
|
|
44
|
+
let promise = Promise<Void>.init()
|
|
45
|
+
Task {
|
|
46
|
+
do {
|
|
47
|
+
try await NitroFetchClient.prefetchStatic(req)
|
|
48
|
+
promise.resolve(withResult: ())
|
|
49
|
+
} catch {
|
|
50
|
+
promise.reject(withError: error)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
return promise
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Shared URLSession for static operations
|
|
58
|
+
private static let session: URLSession = {
|
|
59
|
+
let config = URLSessionConfiguration.default
|
|
60
|
+
config.requestCachePolicy = .useProtocolCachePolicy
|
|
61
|
+
config.urlCache = URLCache(memoryCapacity: 32 * 1024 * 1024,
|
|
62
|
+
diskCapacity: 100 * 1024 * 1024,
|
|
63
|
+
diskPath: "nitrofetch_urlcache")
|
|
64
|
+
return URLSession(configuration: config)
|
|
65
|
+
}()
|
|
66
|
+
|
|
67
|
+
private static func findPrefetchKey(_ req: NitroRequest) -> String? {
|
|
68
|
+
guard let headers = req.headers else { return nil }
|
|
69
|
+
for h in headers {
|
|
70
|
+
if h.key.caseInsensitiveCompare("prefetchKey") == .orderedSame {
|
|
71
|
+
return h.value
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return nil
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// MARK: - Static API usable from native bootstrap
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
public class func requestStatic(_ req: NitroRequest) async throws -> NitroResponse {
|
|
81
|
+
if let key = findPrefetchKey(req) {
|
|
82
|
+
// If a prefetched result is fresh, return immediately
|
|
83
|
+
if let cached = FetchCache.getResultIfFresh(key, maxAgeMs: 5_000) {
|
|
84
|
+
var headers = cached.headers ?? []
|
|
85
|
+
headers.append(NitroHeader(key: "nitroPrefetched", value: "true"))
|
|
86
|
+
return NitroResponse(url: cached.url,
|
|
87
|
+
status: cached.status,
|
|
88
|
+
statusText: cached.statusText,
|
|
89
|
+
ok: cached.ok,
|
|
90
|
+
redirected: cached.redirected,
|
|
91
|
+
headers: headers,
|
|
92
|
+
bodyString: cached.bodyString,
|
|
93
|
+
bodyBytes: cached.bodyBytes)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// If a prefetch is already pending, await and reuse its result
|
|
97
|
+
if FetchCache.getPending(key) {
|
|
98
|
+
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<NitroResponse, Error>) in
|
|
99
|
+
FetchCache.addPending(key) { result in
|
|
100
|
+
switch result {
|
|
101
|
+
case .success(let res):
|
|
102
|
+
// Mirror Android: mark response as coming from prefetch
|
|
103
|
+
var headers = res.headers ?? []
|
|
104
|
+
headers.append(NitroHeader(key: "nitroPrefetched", value: "true"))
|
|
105
|
+
let wrapped = NitroResponse(url: res.url,
|
|
106
|
+
status: res.status,
|
|
107
|
+
statusText: res.statusText,
|
|
108
|
+
ok: res.ok,
|
|
109
|
+
redirected: res.redirected,
|
|
110
|
+
headers: headers,
|
|
111
|
+
bodyString: res.bodyString,
|
|
112
|
+
bodyBytes: res.bodyBytes)
|
|
113
|
+
continuation.resume(returning: wrapped)
|
|
114
|
+
case .failure(let err):
|
|
115
|
+
continuation.resume(throwing: err)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let (urlRequest, finalURL) = try buildURLRequest(req)
|
|
123
|
+
let (data, response) = try await session.data(for: urlRequest)
|
|
124
|
+
guard let http = response as? HTTPURLResponse else {
|
|
125
|
+
throw NSError(domain: "NitroFetch", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let headersPairs: [NitroHeader] = http.allHeaderFields.compactMap { k, v in
|
|
129
|
+
guard let key = k as? String else { return nil }
|
|
130
|
+
return NitroHeader(key: key, value: String(describing: v))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Choose bodyString by default (matching Android’s first pass)
|
|
134
|
+
let charset = NitroFetchClient.detectCharset(from: http) ?? String.Encoding.utf8
|
|
135
|
+
let bodyStr = String(data: data, encoding: charset) ?? String(data: data, encoding: .utf8)
|
|
136
|
+
|
|
137
|
+
let res = NitroResponse(
|
|
138
|
+
url: finalURL?.absoluteString ?? http.url?.absoluteString ?? req.url,
|
|
139
|
+
status: Double(http.statusCode),
|
|
140
|
+
statusText: HTTPURLResponse.localizedString(forStatusCode: http.statusCode),
|
|
141
|
+
ok: (200...299).contains(http.statusCode),
|
|
142
|
+
redirected: (finalURL?.absoluteString ?? http.url?.absoluteString ?? req.url) != req.url,
|
|
143
|
+
headers: headersPairs,
|
|
144
|
+
bodyString: bodyStr,
|
|
145
|
+
bodyBytes: nil
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
// Do not write to cache here; only prefetch should populate the cache
|
|
149
|
+
|
|
150
|
+
return res
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public class func prefetchStatic(_ req: NitroRequest) async throws {
|
|
154
|
+
guard let key = findPrefetchKey(req) else {
|
|
155
|
+
throw NSError(domain: "NitroFetch", code: -2, userInfo: [NSLocalizedDescriptionKey: "prefetch: missing 'prefetchKey' header"])
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if FetchCache.getResultIfFresh(key, maxAgeMs: 5_000) != nil {
|
|
159
|
+
return // already have a fresh result
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if FetchCache.getPending(key) {
|
|
163
|
+
return // already pending
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Mark pending and start the request
|
|
167
|
+
FetchCache.addPending(key) { _ in /* ignored here */ }
|
|
168
|
+
Task.detached {
|
|
169
|
+
do {
|
|
170
|
+
let (urlRequest, finalURL) = try buildURLRequest(req)
|
|
171
|
+
let (data, response) = try await session.data(for: urlRequest)
|
|
172
|
+
guard let http = response as? HTTPURLResponse else {
|
|
173
|
+
throw NSError(domain: "NitroFetch", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])
|
|
174
|
+
}
|
|
175
|
+
let headersPairs: [NitroHeader] = http.allHeaderFields.compactMap { k, v in
|
|
176
|
+
guard let key = k as? String else { return nil }
|
|
177
|
+
return NitroHeader(key: key, value: String(describing: v))
|
|
178
|
+
}
|
|
179
|
+
let charset = NitroFetchClient.detectCharset(from: http) ?? .utf8
|
|
180
|
+
let bodyStr = String(data: data, encoding: charset) ?? String(data: data, encoding: .utf8)
|
|
181
|
+
let res = NitroResponse(
|
|
182
|
+
url: finalURL?.absoluteString ?? http.url?.absoluteString ?? req.url,
|
|
183
|
+
status: Double(http.statusCode),
|
|
184
|
+
statusText: HTTPURLResponse.localizedString(forStatusCode: http.statusCode),
|
|
185
|
+
ok: (200...299).contains(http.statusCode),
|
|
186
|
+
redirected: (finalURL?.absoluteString ?? http.url?.absoluteString ?? req.url) != req.url,
|
|
187
|
+
headers: headersPairs,
|
|
188
|
+
bodyString: bodyStr,
|
|
189
|
+
bodyBytes: nil
|
|
190
|
+
)
|
|
191
|
+
FetchCache.complete(key, with: .success(res))
|
|
192
|
+
} catch {
|
|
193
|
+
FetchCache.complete(key, with: .failure(error))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private static func reqToHttpMethod(_ req: NitroRequest) -> String? {
|
|
199
|
+
return req.method?.stringValue
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private static func buildURLRequest(_ req: NitroRequest) throws -> (URLRequest, URL?) {
|
|
203
|
+
guard let url = URL(string: req.url) else {
|
|
204
|
+
throw NSError(domain: "NitroFetch", code: -3, userInfo: [NSLocalizedDescriptionKey: "Invalid URL: \(req.url)"])
|
|
205
|
+
}
|
|
206
|
+
var r = URLRequest(url: url)
|
|
207
|
+
if let m = req.method?.rawValue { r.httpMethod = reqToHttpMethod(req) }
|
|
208
|
+
if let headers = req.headers {
|
|
209
|
+
for h in headers { r.addValue(h.value, forHTTPHeaderField: h.key) }
|
|
210
|
+
}
|
|
211
|
+
if let s = req.bodyString {
|
|
212
|
+
r.httpBody = s.data(using: .utf8)
|
|
213
|
+
}
|
|
214
|
+
if let t = req.timeoutMs, t > 0 { r.timeoutInterval = TimeInterval(t) / 1000.0 }
|
|
215
|
+
return (r, nil)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private static func detectCharset(from http: HTTPURLResponse) -> String.Encoding? {
|
|
219
|
+
if let ct = http.value(forHTTPHeaderField: "Content-Type")?.lowercased() {
|
|
220
|
+
if let range = ct.range(of: "charset=") {
|
|
221
|
+
let charset = String(ct[range.upperBound...]).trimmingCharacters(in: .whitespaces)
|
|
222
|
+
let mapped = CFStringConvertIANACharSetNameToEncoding(charset as CFString)
|
|
223
|
+
if mapped != kCFStringEncodingInvalidId {
|
|
224
|
+
return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(mapped))
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return nil
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["NitroFetch.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
|
+
// Create singletons once per JS runtime
|
|
5
|
+
export const NitroFetch = NitroModules.createHybridObject('NitroFetch');
|
|
6
|
+
export const NativeStorage = NitroModules.createHybridObject('NativeStorage');
|
|
7
|
+
export const boxedNitroFetch = NitroModules.box(NitroFetch);
|
|
8
|
+
//# sourceMappingURL=NitroInstances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NitroModules","NitroFetch","createHybridObject","NativeStorage","boxedNitroFetch","box"],"sourceRoot":"../../src","sources":["NitroInstances.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAMzD;AACA,OAAO,MAAMC,UAA0B,GACrCD,YAAY,CAACE,kBAAkB,CAAiB,YAAY,CAAC;AAE/D,OAAO,MAAMC,aAAgC,GAC3CH,YAAY,CAACE,kBAAkB,CAAoB,eAAe,CAAC;AAErE,OAAO,MAAME,eAAe,GAAGJ,YAAY,CAACK,GAAG,CAACJ,UAAU,CAAC","ignoreList":[]}
|