react-native-nitro-fetch 0.1.1
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/LICENSE +20 -0
- package/NitroFetch.podspec +30 -0
- package/README.md +134 -0
- package/android/CMakeLists.txt +70 -0
- package/android/build.gradle +131 -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 +91 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +48 -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 +256 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchPackage.kt +22 -0
- package/ios/FetchCache.swift +56 -0
- package/ios/NitroAutoPrefetcher.swift +81 -0
- package/ios/NitroBootstrap.mm +27 -0
- package/ios/NitroFetch.swift +9 -0
- package/ios/NitroFetchClient.swift +205 -0
- package/lib/module/NitroFetch.nitro.js +7 -0
- package/lib/module/NitroFetch.nitro.js.map +1 -0
- package/lib/module/NitroInstances.js +6 -0
- package/lib/module/NitroInstances.js.map +1 -0
- package/lib/module/fetch.js +441 -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/typescript/package.json +1 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts +39 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -0
- package/lib/typescript/src/NitroInstances.d.ts +3 -0
- package/lib/typescript/src/NitroInstances.d.ts.map +1 -0
- package/lib/typescript/src/fetch.d.ts +26 -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/nitro.json +21 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +96 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +65 -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 +103 -0
- package/nitrogen/generated/android/c++/JNitroRequestMethod.hpp +74 -0
- package/nitrogen/generated/android/c++/JNitroResponse.hpp +105 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +56 -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 +80 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.gradle +27 -0
- package/nitrogen/generated/android/nitrofetchOnLoad.cpp +54 -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 +73 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +298 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +67 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.mm +41 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.swift +40 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +101 -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/HybridNitroFetchClientSpec.swift +50 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +149 -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 +199 -0
- package/nitrogen/generated/ios/swift/NitroRequestMethod.swift +60 -0
- package/nitrogen/generated/ios/swift/NitroResponse.swift +155 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +68 -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 +101 -0
- package/nitrogen/generated/shared/c++/NitroRequestMethod.hpp +96 -0
- package/nitrogen/generated/shared/c++/NitroResponse.hpp +102 -0
- package/package.json +177 -0
- package/src/NitroFetch.nitro.ts +57 -0
- package/src/NitroInstances.ts +8 -0
- package/src/fetch.ts +426 -0
- package/src/index.tsx +17 -0
package/src/fetch.ts
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NitroFetch as NitroFetchModule,
|
|
3
|
+
NitroHeader,
|
|
4
|
+
NitroRequest,
|
|
5
|
+
NitroResponse,
|
|
6
|
+
} from './NitroFetch.nitro';
|
|
7
|
+
import { NitroFetch as NitroFetchSingleton } from './NitroInstances';
|
|
8
|
+
|
|
9
|
+
// No base64: pass strings/ArrayBuffers directly
|
|
10
|
+
|
|
11
|
+
function headersToPairs(headers?: HeadersInit): NitroHeader[] | undefined {
|
|
12
|
+
'worklet';
|
|
13
|
+
if (!headers) return undefined;
|
|
14
|
+
const pairs: NitroHeader[] = [];
|
|
15
|
+
if (headers instanceof Headers) {
|
|
16
|
+
headers.forEach((v, k) => pairs.push({ key: k, value: v }));
|
|
17
|
+
return pairs;
|
|
18
|
+
}
|
|
19
|
+
if (Array.isArray(headers)) {
|
|
20
|
+
// Convert tuple pairs to objects if needed
|
|
21
|
+
for (const entry of headers as any[]) {
|
|
22
|
+
if (Array.isArray(entry) && entry.length >= 2) {
|
|
23
|
+
pairs.push({ key: String(entry[0]), value: String(entry[1]) });
|
|
24
|
+
} else if (entry && typeof entry === 'object' && 'key' in entry && 'value' in entry) {
|
|
25
|
+
pairs.push(entry as NitroHeader);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return pairs;
|
|
29
|
+
}
|
|
30
|
+
// Record<string, string>
|
|
31
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
32
|
+
pairs.push({ key: k, value: String(v) });
|
|
33
|
+
}
|
|
34
|
+
return pairs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeBody(body: BodyInit | null | undefined): { bodyString?: string; bodyBytes?: ArrayBuffer } | undefined {
|
|
38
|
+
'worklet';
|
|
39
|
+
if (body == null) return undefined;
|
|
40
|
+
if (typeof body === 'string') return { bodyString: body };
|
|
41
|
+
if (body instanceof URLSearchParams) return { bodyString: body.toString() };
|
|
42
|
+
if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) return { bodyBytes: body };
|
|
43
|
+
if (ArrayBuffer.isView(body)) {
|
|
44
|
+
const view = body as ArrayBufferView;
|
|
45
|
+
// Pass a copy/slice of the underlying bytes without base64
|
|
46
|
+
//@ts-ignore
|
|
47
|
+
return { bodyBytes: view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) };
|
|
48
|
+
}
|
|
49
|
+
// TODO: Blob/FormData support can be added later
|
|
50
|
+
throw new Error('Unsupported body type for nitro fetch');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
function pairsToHeaders(pairs: NitroHeader[]): Headers {
|
|
55
|
+
'worklet';
|
|
56
|
+
const h = new Headers();
|
|
57
|
+
for (const { key, value } of pairs) h.append(key, value);
|
|
58
|
+
return h;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const NitroFetchHybrid: NitroFetchModule = NitroFetchSingleton;
|
|
62
|
+
|
|
63
|
+
let client: ReturnType<NitroFetchModule['createClient']> | undefined;
|
|
64
|
+
|
|
65
|
+
function ensureClient() {
|
|
66
|
+
if (client) return client;
|
|
67
|
+
try {
|
|
68
|
+
client = NitroFetchHybrid.createClient();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error('Failed to create NitroFetch client', err);
|
|
71
|
+
// native not ready; keep undefined
|
|
72
|
+
}
|
|
73
|
+
return client;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildNitroRequest(input: RequestInfo | URL, init?: RequestInit): NitroRequest {
|
|
77
|
+
'worklet';
|
|
78
|
+
let url: string;
|
|
79
|
+
let method: string | undefined;
|
|
80
|
+
let headersInit: HeadersInit | undefined;
|
|
81
|
+
let body: BodyInit | null | undefined;
|
|
82
|
+
|
|
83
|
+
if (typeof input === 'string' || input instanceof URL) {
|
|
84
|
+
url = String(input);
|
|
85
|
+
method = init?.method;
|
|
86
|
+
headersInit = init?.headers;
|
|
87
|
+
body = init?.body ?? null;
|
|
88
|
+
} else {
|
|
89
|
+
// Request object
|
|
90
|
+
url = input.url;
|
|
91
|
+
method = input.method;
|
|
92
|
+
headersInit = input.headers as any;
|
|
93
|
+
// Clone body if needed – Request objects in RN typically allow direct access
|
|
94
|
+
body = init?.body ?? null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const headers = headersToPairs(headersInit);
|
|
98
|
+
const normalized = normalizeBody(body);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
url,
|
|
102
|
+
method: (method?.toUpperCase() as any) ?? 'GET',
|
|
103
|
+
headers,
|
|
104
|
+
bodyString: normalized?.bodyString,
|
|
105
|
+
bodyBytes: normalized?.bodyBytes,
|
|
106
|
+
followRedirects: true,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function nitroFetchRaw(input: RequestInfo | URL, init?: RequestInit): Promise<NitroResponse> {
|
|
111
|
+
'worklet';
|
|
112
|
+
const hasNative = typeof (NitroFetchHybrid as any)?.createClient === 'function';
|
|
113
|
+
if (!hasNative) {
|
|
114
|
+
// Fallback path not supported for raw; use global fetch and synthesize minimal shape
|
|
115
|
+
// @ts-ignore: global fetch exists in RN
|
|
116
|
+
const res = await fetch(input as any, init);
|
|
117
|
+
const url = (res as any).url ?? String(input);
|
|
118
|
+
const bytes = await res.arrayBuffer();
|
|
119
|
+
const headers: NitroHeader[] = [];
|
|
120
|
+
res.headers.forEach((v, k) => headers.push({ key: k, value: v }));
|
|
121
|
+
return {
|
|
122
|
+
url,
|
|
123
|
+
status: res.status,
|
|
124
|
+
statusText: res.statusText,
|
|
125
|
+
ok: res.ok,
|
|
126
|
+
redirected: (res as any).redirected ?? false,
|
|
127
|
+
headers,
|
|
128
|
+
bodyBytes: bytes,
|
|
129
|
+
bodyString: undefined,
|
|
130
|
+
} as NitroResponse;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const req = buildNitroRequest(input, init);
|
|
134
|
+
ensureClient();
|
|
135
|
+
if (!client || typeof (client as any).request !== 'function') throw new Error('NitroFetch client not available');
|
|
136
|
+
const res: NitroResponse = await client.request(req);
|
|
137
|
+
return res;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function nitroFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
|
141
|
+
'worklet';
|
|
142
|
+
// If native implementation is not present yet, fallback to global fetch
|
|
143
|
+
const hasNative = typeof (NitroFetchHybrid as any)?.createClient === 'function';
|
|
144
|
+
if (!hasNative) {
|
|
145
|
+
// @ts-ignore: global fetch exists in RN
|
|
146
|
+
return fetch(input as any, init);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const res = await nitroFetchRaw(input, init);
|
|
150
|
+
|
|
151
|
+
const bytes = res.bodyBytes
|
|
152
|
+
? new Uint8Array(res.bodyBytes)
|
|
153
|
+
: res.bodyString != null
|
|
154
|
+
? new TextEncoder().encode(res.bodyString)
|
|
155
|
+
: new Uint8Array();
|
|
156
|
+
|
|
157
|
+
// If Response is available, construct a real Response object for compatibility
|
|
158
|
+
if (typeof Response !== 'undefined') {
|
|
159
|
+
const respInit: ResponseInit = {
|
|
160
|
+
status: res.status,
|
|
161
|
+
statusText: res.statusText,
|
|
162
|
+
headers: res.headers.reduce((acc, { key, value }) => {
|
|
163
|
+
acc[key] = value;
|
|
164
|
+
return acc;
|
|
165
|
+
}, {} as Record<string, string>),
|
|
166
|
+
};
|
|
167
|
+
// Prefer native bytes if available
|
|
168
|
+
return new Response(res.bodyBytes ?? bytes, respInit);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Fallback lightweight Response-like object (minimal methods)
|
|
172
|
+
const headersObj = res.headers.reduce((acc, { key, value }) => {
|
|
173
|
+
acc[key] = value;
|
|
174
|
+
return acc;
|
|
175
|
+
}, {} as Record<string, string>);
|
|
176
|
+
const light: any = {
|
|
177
|
+
url: res.url,
|
|
178
|
+
ok: res.ok,
|
|
179
|
+
status: res.status,
|
|
180
|
+
statusText: res.statusText,
|
|
181
|
+
redirected: res.redirected,
|
|
182
|
+
headers: headersObj,
|
|
183
|
+
arrayBuffer: async () => (res.bodyBytes ?? bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)),
|
|
184
|
+
text: async () => res.bodyString ?? new TextDecoder().decode(bytes),
|
|
185
|
+
json: async () => JSON.parse(res.bodyString ?? new TextDecoder().decode(bytes)),
|
|
186
|
+
};
|
|
187
|
+
return light as Response;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Start a native prefetch. Requires a `prefetchKey` header on the request.
|
|
191
|
+
export async function prefetch(input: RequestInfo | URL, init?: RequestInit): Promise<void> {
|
|
192
|
+
// If native implementation is not present yet, do nothing
|
|
193
|
+
const hasNative = typeof (NitroFetchHybrid as any)?.createClient === 'function';
|
|
194
|
+
if (!hasNative) return;
|
|
195
|
+
|
|
196
|
+
// Build NitroRequest and ensure prefetchKey header exists
|
|
197
|
+
const req = buildNitroRequest(input, init);
|
|
198
|
+
const hasKey = req.headers?.some(h => h.key.toLowerCase() === 'prefetchkey') ?? false;
|
|
199
|
+
// Also support passing prefetchKey via non-standard field on init
|
|
200
|
+
const fromInit = (init as any)?.prefetchKey as string | undefined;
|
|
201
|
+
if (!hasKey && fromInit) {
|
|
202
|
+
req.headers = (req.headers ?? []).concat([{ key: 'prefetchKey', value: fromInit }]);
|
|
203
|
+
}
|
|
204
|
+
const finalHasKey = req.headers?.some(h => h.key.toLowerCase() === 'prefetchkey');
|
|
205
|
+
if (!finalHasKey) {
|
|
206
|
+
throw new Error('prefetch requires a \"prefetchKey\" header');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Ensure client and call native prefetch
|
|
210
|
+
ensureClient();
|
|
211
|
+
if (!client || typeof (client as any).prefetch !== 'function') return;
|
|
212
|
+
await client.prefetch(req);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Persist a request to MMKV so native can prefetch it on app start.
|
|
216
|
+
// Stores an array of entries under the same key Android reads: "nitrofetch_autoprefetch_queue".
|
|
217
|
+
export async function prefetchOnAppStart(
|
|
218
|
+
input: RequestInfo | URL,
|
|
219
|
+
init?: RequestInit & { prefetchKey?: string }
|
|
220
|
+
): Promise<void> {
|
|
221
|
+
// Resolve request and prefetchKey
|
|
222
|
+
const req = buildNitroRequest(input, init);
|
|
223
|
+
const fromHeader = req.headers?.find(h => h.key.toLowerCase() === 'prefetchkey')?.value;
|
|
224
|
+
const fromInit = (init as any)?.prefetchKey as string | undefined;
|
|
225
|
+
const prefetchKey = fromHeader ?? fromInit;
|
|
226
|
+
if (!prefetchKey) {
|
|
227
|
+
throw new Error('prefetchOnAppStart requires a "prefetchKey" (header or init.prefetchKey)');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Convert headers to a plain object for storage
|
|
231
|
+
const headersObj = (req.headers ?? []).reduce((acc, { key, value }) => {
|
|
232
|
+
acc[String(key)] = String(value);
|
|
233
|
+
return acc;
|
|
234
|
+
}, {} as Record<string, string>);
|
|
235
|
+
|
|
236
|
+
const entry = {
|
|
237
|
+
url: req.url,
|
|
238
|
+
prefetchKey,
|
|
239
|
+
headers: headersObj,
|
|
240
|
+
} as const;
|
|
241
|
+
|
|
242
|
+
// Write or append to MMKV queue
|
|
243
|
+
try {
|
|
244
|
+
// Dynamically require to keep it optional for consumers
|
|
245
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
246
|
+
const { MMKV } = require('react-native-mmkv');
|
|
247
|
+
const storage = new MMKV(); // default instance matches Android's defaultMMKV
|
|
248
|
+
const KEY = 'nitrofetch_autoprefetch_queue';
|
|
249
|
+
let arr: any[] = [];
|
|
250
|
+
try {
|
|
251
|
+
const raw = storage.getString(KEY);
|
|
252
|
+
if (raw) arr = JSON.parse(raw);
|
|
253
|
+
if (!Array.isArray(arr)) arr = [];
|
|
254
|
+
} catch {
|
|
255
|
+
arr = [];
|
|
256
|
+
}
|
|
257
|
+
arr.push(entry);
|
|
258
|
+
storage.set(KEY, JSON.stringify(arr));
|
|
259
|
+
} catch (e) {
|
|
260
|
+
console.warn('react-native-mmkv not available; prefetchOnAppStart is a no-op', e);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Remove one entry (by prefetchKey) from the auto-prefetch queue in MMKV.
|
|
265
|
+
export async function removeFromAutoPrefetch(prefetchKey: string): Promise<void> {
|
|
266
|
+
// No-op on iOS
|
|
267
|
+
try {
|
|
268
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
269
|
+
const { MMKV } = require('react-native-mmkv');
|
|
270
|
+
const storage = new MMKV();
|
|
271
|
+
const KEY = 'nitrofetch_autoprefetch_queue';
|
|
272
|
+
let arr: any[] = [];
|
|
273
|
+
try {
|
|
274
|
+
const raw = storage.getString(KEY);
|
|
275
|
+
if (raw) arr = JSON.parse(raw);
|
|
276
|
+
if (!Array.isArray(arr)) arr = [];
|
|
277
|
+
} catch {
|
|
278
|
+
arr = [];
|
|
279
|
+
}
|
|
280
|
+
const next = arr.filter((e) => e && e.prefetchKey !== prefetchKey);
|
|
281
|
+
if (next.length === 0) {
|
|
282
|
+
if (typeof (storage as any).delete === 'function') {
|
|
283
|
+
(storage as any).delete(KEY);
|
|
284
|
+
} else {
|
|
285
|
+
storage.set(KEY, JSON.stringify([]));
|
|
286
|
+
}
|
|
287
|
+
} else if (next.length !== arr.length) {
|
|
288
|
+
storage.set(KEY, JSON.stringify(next));
|
|
289
|
+
}
|
|
290
|
+
} catch (e) {
|
|
291
|
+
console.warn('react-native-mmkv not available; removeFromAutoPrefetch is a no-op', e);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Remove all entries from the auto-prefetch queue in MMKV.
|
|
296
|
+
export async function removeAllFromAutoprefetch(): Promise<void> {
|
|
297
|
+
try {
|
|
298
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
299
|
+
const { MMKV } = require('react-native-mmkv');
|
|
300
|
+
const storage = new MMKV();
|
|
301
|
+
const KEY = 'nitrofetch_autoprefetch_queue';
|
|
302
|
+
if (typeof (storage as any).delete === 'function') {
|
|
303
|
+
(storage as any).delete(KEY);
|
|
304
|
+
} else {
|
|
305
|
+
storage.set(KEY, JSON.stringify([]));
|
|
306
|
+
}
|
|
307
|
+
} catch (e) {
|
|
308
|
+
console.warn('react-native-mmkv not available; removeAllFromAutoprefetch is a no-op', e);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Optional off-thread processing using react-native-worklets-core
|
|
313
|
+
export type NitroWorkletMapper<T> = (payload: {
|
|
314
|
+
url: string;
|
|
315
|
+
status: number;
|
|
316
|
+
statusText: string;
|
|
317
|
+
ok: boolean;
|
|
318
|
+
redirected: boolean;
|
|
319
|
+
headers: NitroHeader[];
|
|
320
|
+
bodyBytes?: ArrayBuffer;
|
|
321
|
+
bodyString?: string;
|
|
322
|
+
}) => T;
|
|
323
|
+
|
|
324
|
+
let nitroRuntime: any | undefined;
|
|
325
|
+
let WorkletsRef: any | undefined;
|
|
326
|
+
function ensureWorkletRuntime(name = 'nitro-fetch'): any | undefined {
|
|
327
|
+
console.log('ensuring worklet runtime');
|
|
328
|
+
try {
|
|
329
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
330
|
+
const { Worklets } = require('react-native-worklets-core');
|
|
331
|
+
nitroRuntime = nitroRuntime ?? Worklets.createRuntime(name);
|
|
332
|
+
console.log('nitroRuntime:', !!nitroRuntime);
|
|
333
|
+
return nitroRuntime;
|
|
334
|
+
} catch {
|
|
335
|
+
console.warn('react-native-worklets-core not available');
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function getWorklets(): any | undefined {
|
|
341
|
+
try {
|
|
342
|
+
if (WorkletsRef) return WorkletsRef;
|
|
343
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
344
|
+
const { Worklets } = require('react-native-worklets-core');
|
|
345
|
+
WorkletsRef = Worklets;
|
|
346
|
+
return WorkletsRef;
|
|
347
|
+
} catch {
|
|
348
|
+
console.warn('react-native-worklets-core not available');
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export async function nitroFetchOnWorklet<T>(
|
|
354
|
+
input: RequestInfo | URL,
|
|
355
|
+
init: RequestInit | undefined,
|
|
356
|
+
mapWorklet: NitroWorkletMapper<T>,
|
|
357
|
+
options?: { preferBytes?: boolean; runtimeName?: string }
|
|
358
|
+
): Promise<T> {
|
|
359
|
+
|
|
360
|
+
console.log('nitroFetchOnWorklet: starting');
|
|
361
|
+
const preferBytes = options?.preferBytes !== false; // default true
|
|
362
|
+
console.log('nitroFetchOnWorklet: preferBytes:', preferBytes);
|
|
363
|
+
let rt: any | undefined;
|
|
364
|
+
let Worklets: any | undefined;
|
|
365
|
+
try {
|
|
366
|
+
rt = ensureWorkletRuntime(options?.runtimeName);
|
|
367
|
+
console.log('nitroFetchOnWorklet: runtime created?', !!rt);
|
|
368
|
+
Worklets = getWorklets();
|
|
369
|
+
console.log('nitroFetchOnWorklet: Worklets available?', !!Worklets);
|
|
370
|
+
} catch (e) {
|
|
371
|
+
console.error('nitroFetchOnWorklet: setup failed', e);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Fallback: if runtime is not available, do the work on JS
|
|
375
|
+
if (!rt || !Worklets || typeof rt.run !== 'function') {
|
|
376
|
+
console.warn('nitroFetchOnWorklet: no runtime, mapping on JS thread');
|
|
377
|
+
const res = await nitroFetchRaw(input, init);
|
|
378
|
+
const payload = {
|
|
379
|
+
url: res.url,
|
|
380
|
+
status: res.status,
|
|
381
|
+
statusText: res.statusText,
|
|
382
|
+
ok: res.ok,
|
|
383
|
+
redirected: res.redirected,
|
|
384
|
+
headers: res.headers,
|
|
385
|
+
bodyBytes: preferBytes ? res.bodyBytes : undefined,
|
|
386
|
+
bodyString: preferBytes ? undefined : res.bodyString,
|
|
387
|
+
} as const;
|
|
388
|
+
return mapWorklet(payload as any);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return await new Promise<T>((resolve, reject) => {
|
|
392
|
+
try {
|
|
393
|
+
console.log('nitroFetchOnWorklet: about to call rt.run');
|
|
394
|
+
rt.run(async (map: NitroWorkletMapper<T>) => {
|
|
395
|
+
'worklet';
|
|
396
|
+
try {
|
|
397
|
+
console.log('nitroFetchOnWorklet: running fetch on worklet thread');
|
|
398
|
+
const res = await nitroFetchRaw(input, init);
|
|
399
|
+
console.log('nitroFetchOnWorklet: fetch completed');
|
|
400
|
+
const url = res.url;
|
|
401
|
+
const status = res.status;
|
|
402
|
+
const statusText = res.statusText;
|
|
403
|
+
const ok = res.ok;
|
|
404
|
+
const redirected = res.redirected;
|
|
405
|
+
const headersPairs: NitroHeader[] = res.headers;
|
|
406
|
+
const bodyBytes: ArrayBuffer | undefined = preferBytes ? res.bodyBytes : undefined;
|
|
407
|
+
const bodyString: string | undefined = preferBytes ? undefined : res.bodyString;
|
|
408
|
+
const payload = { url, status, statusText, ok, redirected, headers: headersPairs, bodyBytes, bodyString };
|
|
409
|
+
const out = map(payload);
|
|
410
|
+
// Resolve back on JS thread
|
|
411
|
+
Worklets.runOnJS(resolve)(out as any);
|
|
412
|
+
} catch (e) {
|
|
413
|
+
Worklets.runOnJS(reject)(e as any);
|
|
414
|
+
}
|
|
415
|
+
}, mapWorklet as any);
|
|
416
|
+
} catch (e) {
|
|
417
|
+
console.error('nitroFetchOnWorklet: rt.run failed', e);
|
|
418
|
+
reject(e);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export const x = ensureWorkletRuntime();
|
|
424
|
+
export const y = getWorklets();
|
|
425
|
+
|
|
426
|
+
export type { NitroRequest, NitroResponse } from './NitroFetch.nitro';
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
nitroFetch as fetch,
|
|
3
|
+
nitroFetchOnWorklet,
|
|
4
|
+
prefetch,
|
|
5
|
+
prefetchOnAppStart,
|
|
6
|
+
removeFromAutoPrefetch,
|
|
7
|
+
removeAllFromAutoprefetch,
|
|
8
|
+
} from './fetch';
|
|
9
|
+
export type { NitroRequest, NitroResponse } from './fetch';
|
|
10
|
+
export { NitroFetch } from './NitroInstances';
|
|
11
|
+
import './fetch';
|
|
12
|
+
|
|
13
|
+
// Keep legacy export to avoid breaking any local tests/usages during scaffolding.
|
|
14
|
+
// Will be removed once native Cronet path is ready.
|
|
15
|
+
export function multiply(a: number, b: number): number {
|
|
16
|
+
return a * b;
|
|
17
|
+
}
|