react-native-nitro-fetch 0.1.3 → 0.1.5

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 (50) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +7 -28
  3. package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +11 -1
  4. package/android/src/main/java/com/margelo/nitro/nitrofetch/NativeStorage.kt +102 -0
  5. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +53 -29
  6. package/ios/NativeStorage.swift +61 -0
  7. package/ios/NitroAutoPrefetcher.swift +5 -41
  8. package/ios/NitroFetchClient.swift +25 -0
  9. package/lib/module/NitroFetch.nitro.js +0 -3
  10. package/lib/module/NitroFetch.nitro.js.map +1 -1
  11. package/lib/module/NitroInstances.js +2 -0
  12. package/lib/module/NitroInstances.js.map +1 -1
  13. package/lib/module/fetch.js +79 -125
  14. package/lib/module/fetch.js.map +1 -1
  15. package/lib/typescript/src/NitroFetch.nitro.d.ts +9 -0
  16. package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -1
  17. package/lib/typescript/src/NitroInstances.d.ts +3 -1
  18. package/lib/typescript/src/NitroInstances.d.ts.map +1 -1
  19. package/lib/typescript/src/fetch.d.ts.map +1 -1
  20. package/nitro.json +4 -0
  21. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +54 -0
  22. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +66 -0
  23. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +5 -0
  24. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +1 -0
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +60 -0
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +4 -0
  27. package/nitrogen/generated/android/nitrofetch+autolinking.cmake +9 -4
  28. package/nitrogen/generated/android/nitrofetchOnLoad.cpp +10 -0
  29. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +17 -0
  30. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +44 -0
  31. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +5 -0
  32. package/nitrogen/generated/ios/NitroFetchAutolinking.mm +8 -0
  33. package/nitrogen/generated/ios/NitroFetchAutolinking.swift +15 -0
  34. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +11 -0
  35. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +85 -0
  36. package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +8 -0
  37. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +51 -0
  38. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +145 -0
  39. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +1 -0
  40. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +12 -0
  41. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +23 -0
  42. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +64 -0
  43. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +1 -0
  44. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +1 -0
  45. package/package.json +8 -24
  46. package/src/NitroFetch.nitro.ts +15 -5
  47. package/src/NitroInstances.ts +7 -1
  48. package/src/fetch.ts +158 -122
  49. package/LICENSE +0 -20
  50. package/README.md +0 -151
package/src/fetch.ts CHANGED
@@ -4,7 +4,11 @@ import type {
4
4
  NitroRequest,
5
5
  NitroResponse,
6
6
  } from './NitroFetch.nitro';
7
- import { NitroFetch as NitroFetchSingleton } from './NitroInstances';
7
+ import {
8
+ boxedNitroFetch,
9
+ NitroFetch as NitroFetchSingleton,
10
+ } from './NitroInstances';
11
+ import { NativeStorage as NativeStorageSingleton } from './NitroInstances';
8
12
 
9
13
  // No base64: pass strings/ArrayBuffers directly
10
14
 
@@ -21,7 +25,12 @@ function headersToPairs(headers?: HeadersInit): NitroHeader[] | undefined {
21
25
  for (const entry of headers as any[]) {
22
26
  if (Array.isArray(entry) && entry.length >= 2) {
23
27
  pairs.push({ key: String(entry[0]), value: String(entry[1]) });
24
- } else if (entry && typeof entry === 'object' && 'key' in entry && 'value' in entry) {
28
+ } else if (
29
+ entry &&
30
+ typeof entry === 'object' &&
31
+ 'key' in entry &&
32
+ 'value' in entry
33
+ ) {
25
34
  pairs.push(entry as NitroHeader);
26
35
  }
27
36
  }
@@ -34,30 +43,30 @@ function headersToPairs(headers?: HeadersInit): NitroHeader[] | undefined {
34
43
  return pairs;
35
44
  }
36
45
 
37
- function normalizeBody(body: BodyInit | null | undefined): { bodyString?: string; bodyBytes?: ArrayBuffer } | undefined {
46
+ function normalizeBody(
47
+ body: BodyInit | null | undefined
48
+ ): { bodyString?: string; bodyBytes?: ArrayBuffer } | undefined {
38
49
  'worklet';
39
50
  if (body == null) return undefined;
40
51
  if (typeof body === 'string') return { bodyString: body };
41
52
  if (body instanceof URLSearchParams) return { bodyString: body.toString() };
42
- if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer) return { bodyBytes: body };
53
+ if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer)
54
+ return { bodyBytes: body };
43
55
  if (ArrayBuffer.isView(body)) {
44
56
  const view = body as ArrayBufferView;
45
57
  // 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) };
58
+ return {
59
+ //@ts-ignore
60
+ bodyBytes: view.buffer.slice(
61
+ view.byteOffset,
62
+ view.byteOffset + view.byteLength
63
+ ),
64
+ };
48
65
  }
49
66
  // TODO: Blob/FormData support can be added later
50
67
  throw new Error('Unsupported body type for nitro fetch');
51
68
  }
52
69
 
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
70
  const NitroFetchHybrid: NitroFetchModule = NitroFetchSingleton;
62
71
 
63
72
  let client: ReturnType<NitroFetchModule['createClient']> | undefined;
@@ -73,7 +82,10 @@ function ensureClient() {
73
82
  return client;
74
83
  }
75
84
 
76
- function buildNitroRequest(input: RequestInfo | URL, init?: RequestInit): NitroRequest {
85
+ function buildNitroRequest(
86
+ input: RequestInfo | URL,
87
+ init?: RequestInit
88
+ ): NitroRequest {
77
89
  'worklet';
78
90
  let url: string;
79
91
  let method: string | undefined;
@@ -102,14 +114,18 @@ function buildNitroRequest(input: RequestInfo | URL, init?: RequestInit): NitroR
102
114
  method: (method?.toUpperCase() as any) ?? 'GET',
103
115
  headers,
104
116
  bodyString: normalized?.bodyString,
105
- bodyBytes: "",//normalized?.bodyBytes,
117
+ // Only include bodyBytes when provided to avoid signaling upload data unintentionally
118
+ bodyBytes: undefined as any,
106
119
  followRedirects: true,
107
120
  };
108
121
  }
109
122
 
110
- async function nitroFetchRaw(input: RequestInfo | URL, init?: RequestInit): Promise<NitroResponse> {
111
- 'worklet';
112
- const hasNative = typeof (NitroFetchHybrid as any)?.createClient === 'function';
123
+ async function nitroFetchRaw(
124
+ input: RequestInfo | URL,
125
+ init?: RequestInit
126
+ ): Promise<NitroResponse> {
127
+ const hasNative =
128
+ typeof (NitroFetchHybrid as any)?.createClient === 'function';
113
129
  if (!hasNative) {
114
130
  // Fallback path not supported for raw; use global fetch and synthesize minimal shape
115
131
  // @ts-ignore: global fetch exists in RN
@@ -132,59 +148,103 @@ async function nitroFetchRaw(input: RequestInfo | URL, init?: RequestInit): Prom
132
148
 
133
149
  const req = buildNitroRequest(input, init);
134
150
  ensureClient();
135
- if (!client || typeof (client as any).request !== 'function') throw new Error('NitroFetch client not available');
151
+ if (!client || typeof (client as any).request !== 'function')
152
+ throw new Error('NitroFetch client not available');
136
153
  const res: NitroResponse = await client.request(req);
137
154
  return res;
138
155
  }
139
156
 
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);
157
+ // Simple Headers-like class that supports get() method
158
+ class NitroHeaders {
159
+ private _headers: Map<string, string>;
160
+
161
+ constructor(headers: NitroHeader[]) {
162
+ this._headers = new Map();
163
+ for (const { key, value } of headers) {
164
+ // Headers are case-insensitive, normalize to lowercase
165
+ this._headers.set(key.toLowerCase(), value);
166
+ }
167
+ }
168
+
169
+ get(name: string): string | null {
170
+ return this._headers.get(name.toLowerCase()) ?? null;
147
171
  }
148
172
 
173
+ has(name: string): boolean {
174
+ return this._headers.has(name.toLowerCase());
175
+ }
176
+
177
+ forEach(callback: (value: string, key: string) => void): void {
178
+ this._headers.forEach(callback);
179
+ }
180
+
181
+ entries(): IterableIterator<[string, string]> {
182
+ return this._headers.entries();
183
+ }
184
+
185
+ keys(): IterableIterator<string> {
186
+ return this._headers.keys();
187
+ }
188
+
189
+ values(): IterableIterator<string> {
190
+ return this._headers.values();
191
+ }
192
+ }
193
+
194
+ export async function nitroFetch(
195
+ input: RequestInfo | URL,
196
+ init?: RequestInit
197
+ ): Promise<Response> {
149
198
  const res = await nitroFetchRaw(input, init);
150
199
 
151
- // Fallback lightweight Response-like object (minimal methods)
152
- const headersObj = res.headers.reduce((acc, { key, value }) => {
153
- acc[key] = value;
154
- return acc;
155
- }, {} as Record<string, string>);
200
+ const headersObj = new NitroHeaders(res.headers);
201
+
202
+ const bodyBytes = res.bodyBytes;
203
+ const bodyString = res.bodyString;
156
204
 
157
- const light: any = {
205
+ const makeLight = (): any => ({
158
206
  url: res.url,
159
207
  ok: res.ok,
160
208
  status: res.status,
161
209
  statusText: res.statusText,
162
210
  redirected: res.redirected,
163
211
  headers: headersObj,
164
- arrayBuffer: async () => res.bodyBytes,
165
- text: async () => res.bodyString,
166
- json: async () => JSON.parse(res.bodyString ?? '{}'),
167
- };
212
+ arrayBuffer: async () => bodyBytes,
213
+ text: async () => bodyString,
214
+ json: async () => JSON.parse(bodyString ?? '{}'),
215
+ clone: () => makeLight(),
216
+ });
217
+
218
+ const light: any = makeLight();
168
219
  return light as Response;
169
220
  }
170
221
 
171
222
  // Start a native prefetch. Requires a `prefetchKey` header on the request.
172
- export async function prefetch(input: RequestInfo | URL, init?: RequestInit): Promise<void> {
223
+ export async function prefetch(
224
+ input: RequestInfo | URL,
225
+ init?: RequestInit
226
+ ): Promise<void> {
173
227
  // If native implementation is not present yet, do nothing
174
- const hasNative = typeof (NitroFetchHybrid as any)?.createClient === 'function';
228
+ const hasNative =
229
+ typeof (NitroFetchHybrid as any)?.createClient === 'function';
175
230
  if (!hasNative) return;
176
231
 
177
232
  // Build NitroRequest and ensure prefetchKey header exists
178
233
  const req = buildNitroRequest(input, init);
179
- const hasKey = req.headers?.some(h => h.key.toLowerCase() === 'prefetchkey') ?? false;
234
+ const hasKey =
235
+ req.headers?.some((h) => h.key.toLowerCase() === 'prefetchkey') ?? false;
180
236
  // Also support passing prefetchKey via non-standard field on init
181
237
  const fromInit = (init as any)?.prefetchKey as string | undefined;
182
238
  if (!hasKey && fromInit) {
183
- req.headers = (req.headers ?? []).concat([{ key: 'prefetchKey', value: fromInit }]);
239
+ req.headers = (req.headers ?? []).concat([
240
+ { key: 'prefetchKey', value: fromInit },
241
+ ]);
184
242
  }
185
- const finalHasKey = req.headers?.some(h => h.key.toLowerCase() === 'prefetchkey');
243
+ const finalHasKey = req.headers?.some(
244
+ (h) => h.key.toLowerCase() === 'prefetchkey'
245
+ );
186
246
  if (!finalHasKey) {
187
- throw new Error('prefetch requires a \"prefetchKey\" header');
247
+ throw new Error('prefetch requires a "prefetchKey" header');
188
248
  }
189
249
 
190
250
  // Ensure client and call native prefetch
@@ -193,26 +253,32 @@ export async function prefetch(input: RequestInfo | URL, init?: RequestInit): Pr
193
253
  await client.prefetch(req);
194
254
  }
195
255
 
196
- // Persist a request to MMKV so native can prefetch it on app start.
197
- // Stores an array of entries under the same key Android reads: "nitrofetch_autoprefetch_queue".
256
+ // Persist a request to storage so native can prefetch it on app start.
198
257
  export async function prefetchOnAppStart(
199
258
  input: RequestInfo | URL,
200
259
  init?: RequestInit & { prefetchKey?: string }
201
260
  ): Promise<void> {
202
261
  // Resolve request and prefetchKey
203
262
  const req = buildNitroRequest(input, init);
204
- const fromHeader = req.headers?.find(h => h.key.toLowerCase() === 'prefetchkey')?.value;
263
+ const fromHeader = req.headers?.find(
264
+ (h) => h.key.toLowerCase() === 'prefetchkey'
265
+ )?.value;
205
266
  const fromInit = (init as any)?.prefetchKey as string | undefined;
206
267
  const prefetchKey = fromHeader ?? fromInit;
207
268
  if (!prefetchKey) {
208
- throw new Error('prefetchOnAppStart requires a "prefetchKey" (header or init.prefetchKey)');
269
+ throw new Error(
270
+ 'prefetchOnAppStart requires a "prefetchKey" (header or init.prefetchKey)'
271
+ );
209
272
  }
210
273
 
211
274
  // Convert headers to a plain object for storage
212
- const headersObj = (req.headers ?? []).reduce((acc, { key, value }) => {
213
- acc[String(key)] = String(value);
214
- return acc;
215
- }, {} as Record<string, string>);
275
+ const headersObj = (req.headers ?? []).reduce(
276
+ (acc, { key, value }) => {
277
+ acc[String(key)] = String(value);
278
+ return acc;
279
+ },
280
+ {} as Record<string, string>
281
+ );
216
282
 
217
283
  const entry = {
218
284
  url: req.url,
@@ -220,42 +286,40 @@ export async function prefetchOnAppStart(
220
286
  headers: headersObj,
221
287
  } as const;
222
288
 
223
- // Write or append to MMKV queue
289
+ // Write or append to storage queue
224
290
  try {
225
- // Dynamically require to keep it optional for consumers
226
- // eslint-disable-next-line @typescript-eslint/no-var-requires
227
- const { MMKV } = require('react-native-mmkv');
228
- const storage = new MMKV(); // default instance matches Android's defaultMMKV
229
291
  const KEY = 'nitrofetch_autoprefetch_queue';
230
292
  let arr: any[] = [];
231
293
  try {
232
- const raw = storage.getString(KEY);
294
+ const raw = NativeStorageSingleton.getString(
295
+ 'nitrofetch_autoprefetch_queue'
296
+ );
233
297
  if (raw) arr = JSON.parse(raw);
234
298
  if (!Array.isArray(arr)) arr = [];
235
299
  } catch {
236
300
  arr = [];
237
301
  }
238
- if (arr.some(e => e && e.prefetchKey === prefetchKey)) {
239
- arr = arr.filter(e => e && e.prefetchKey !== prefetchKey);
302
+ if (arr.some((e) => e && e.prefetchKey === prefetchKey)) {
303
+ arr = arr.filter((e) => e && e.prefetchKey !== prefetchKey);
240
304
  }
241
305
  arr.push(entry);
242
- storage.set(KEY, JSON.stringify(arr));
306
+ NativeStorageSingleton.setString(KEY, JSON.stringify(arr));
243
307
  } catch (e) {
244
- console.warn('react-native-mmkv not available; prefetchOnAppStart is a no-op', e);
308
+ console.warn('Failed to persist prefetch queue', e);
245
309
  }
246
310
  }
247
311
 
248
- // Remove one entry (by prefetchKey) from the auto-prefetch queue in MMKV.
249
- export async function removeFromAutoPrefetch(prefetchKey: string): Promise<void> {
250
- // No-op on iOS
312
+ // Remove one entry (by prefetchKey) from the auto-prefetch queue.
313
+ export async function removeFromAutoPrefetch(
314
+ prefetchKey: string
315
+ ): Promise<void> {
251
316
  try {
252
- // eslint-disable-next-line @typescript-eslint/no-var-requires
253
- const { MMKV } = require('react-native-mmkv');
254
- const storage = new MMKV();
255
317
  const KEY = 'nitrofetch_autoprefetch_queue';
256
318
  let arr: any[] = [];
257
319
  try {
258
- const raw = storage.getString(KEY);
320
+ const raw = NativeStorageSingleton.getString(
321
+ 'nitrofetch_autoprefetch_queue'
322
+ );
259
323
  if (raw) arr = JSON.parse(raw);
260
324
  if (!Array.isArray(arr)) arr = [];
261
325
  } catch {
@@ -263,34 +327,19 @@ export async function removeFromAutoPrefetch(prefetchKey: string): Promise<void>
263
327
  }
264
328
  const next = arr.filter((e) => e && e.prefetchKey !== prefetchKey);
265
329
  if (next.length === 0) {
266
- if (typeof (storage as any).delete === 'function') {
267
- (storage as any).delete(KEY);
268
- } else {
269
- storage.set(KEY, JSON.stringify([]));
270
- }
330
+ NativeStorageSingleton.removeString(KEY);
271
331
  } else if (next.length !== arr.length) {
272
- storage.set(KEY, JSON.stringify(next));
332
+ NativeStorageSingleton.setString(KEY, JSON.stringify(next));
273
333
  }
274
334
  } catch (e) {
275
- console.warn('react-native-mmkv not available; removeFromAutoPrefetch is a no-op', e);
335
+ console.warn('Failed to remove from prefetch queue', e);
276
336
  }
277
337
  }
278
338
 
279
- // Remove all entries from the auto-prefetch queue in MMKV.
339
+ // Remove all entries from the auto-prefetch queue.
280
340
  export async function removeAllFromAutoprefetch(): Promise<void> {
281
- try {
282
- // eslint-disable-next-line @typescript-eslint/no-var-requires
283
- const { MMKV } = require('react-native-mmkv');
284
- const storage = new MMKV();
285
- const KEY = 'nitrofetch_autoprefetch_queue';
286
- if (typeof (storage as any).delete === 'function') {
287
- (storage as any).delete(KEY);
288
- } else {
289
- storage.set(KEY, JSON.stringify([]));
290
- }
291
- } catch (e) {
292
- console.warn('react-native-mmkv not available; removeAllFromAutoprefetch is a no-op', e);
293
- }
341
+ const KEY = 'nitrofetch_autoprefetch_queue';
342
+ NativeStorageSingleton.setString(KEY, JSON.stringify([]));
294
343
  }
295
344
 
296
345
  // Optional off-thread processing using react-native-worklets-core
@@ -310,9 +359,8 @@ let WorkletsRef: any | undefined;
310
359
  function ensureWorkletRuntime(name = 'nitro-fetch'): any | undefined {
311
360
  console.log('ensuring worklet runtime');
312
361
  try {
313
- // eslint-disable-next-line @typescript-eslint/no-var-requires
314
362
  const { Worklets } = require('react-native-worklets-core');
315
- nitroRuntime = nitroRuntime ?? Worklets.createRuntime(name);
363
+ nitroRuntime = nitroRuntime ?? Worklets.createContext(name);
316
364
  console.log('nitroRuntime:', !!nitroRuntime);
317
365
  return nitroRuntime;
318
366
  } catch {
@@ -324,7 +372,7 @@ function ensureWorkletRuntime(name = 'nitro-fetch'): any | undefined {
324
372
  function getWorklets(): any | undefined {
325
373
  try {
326
374
  if (WorkletsRef) return WorkletsRef;
327
- // eslint-disable-next-line @typescript-eslint/no-var-requires
375
+
328
376
  const { Worklets } = require('react-native-worklets-core');
329
377
  WorkletsRef = Worklets;
330
378
  return WorkletsRef;
@@ -355,7 +403,7 @@ export async function nitroFetchOnWorklet<T>(
355
403
  }
356
404
 
357
405
  // Fallback: if runtime is not available, do the work on JS
358
- if (!rt || !Worklets || typeof rt.run !== 'function') {
406
+ if (!rt || !Worklets || typeof rt.runAsync !== 'function') {
359
407
  console.warn('nitroFetchOnWorklet: no runtime, mapping on JS thread');
360
408
  const res = await nitroFetchRaw(input, init);
361
409
  const payload = {
@@ -370,36 +418,24 @@ export async function nitroFetchOnWorklet<T>(
370
418
  } as const;
371
419
  return mapWorklet(payload as any);
372
420
  }
421
+ console.log('nitroFetchOnWorklet: running on worklet thread');
422
+ return await rt.runAsync(() => {
423
+ 'worklet';
424
+ const unboxedNitroFetch = boxedNitroFetch.unbox();
425
+ const unboxedClient = unboxedNitroFetch.createClient();
426
+ const res = unboxedClient.requestSync(buildNitroRequest(input, init));
427
+ const payload = {
428
+ url: res.url,
429
+ status: res.status,
430
+ statusText: res.statusText,
431
+ ok: res.ok,
432
+ redirected: res.redirected,
433
+ headers: res.headers,
434
+ bodyBytes: preferBytes ? res.bodyBytes : undefined,
435
+ bodyString: preferBytes ? undefined : res.bodyString,
436
+ } as const;
373
437
 
374
- return await new Promise<T>((resolve, reject) => {
375
- try {
376
- console.log('nitroFetchOnWorklet: about to call rt.run');
377
- rt.run(async (map: NitroWorkletMapper<T>) => {
378
- 'worklet';
379
- try {
380
- console.log('nitroFetchOnWorklet: running fetch on worklet thread');
381
- const res = await nitroFetchRaw(input, init);
382
- console.log('nitroFetchOnWorklet: fetch completed');
383
- const url = res.url;
384
- const status = res.status;
385
- const statusText = res.statusText;
386
- const ok = res.ok;
387
- const redirected = res.redirected;
388
- const headersPairs: NitroHeader[] = res.headers;
389
- const bodyBytes: ArrayBuffer | undefined = undefined; // preferBytes ? res.bodyBytes : undefined;
390
- const bodyString: string | undefined = preferBytes ? undefined : res.bodyString;
391
- const payload = { url, status, statusText, ok, redirected, headers: headersPairs, bodyBytes, bodyString };
392
- const out = map(payload);
393
- // Resolve back on JS thread
394
- Worklets.runOnJS(resolve)(out as any);
395
- } catch (e) {
396
- Worklets.runOnJS(reject)(e as any);
397
- }
398
- }, mapWorklet as any);
399
- } catch (e) {
400
- console.error('nitroFetchOnWorklet: rt.run failed', e);
401
- reject(e);
402
- }
438
+ return mapWorklet(payload as any);
403
439
  });
404
440
  }
405
441
 
package/LICENSE DELETED
@@ -1,20 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Szymon Kapala
4
- Permission is hereby granted, free of charge, to any person obtaining a copy
5
- of this software and associated documentation files (the "Software"), to deal
6
- in the Software without restriction, including without limitation the rights
7
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- copies of the Software, and to permit persons to whom the Software is
9
- furnished to do so, subject to the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be included in all
12
- copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- SOFTWARE.
package/README.md DELETED
@@ -1,151 +0,0 @@
1
- <a href="https://margelo.com">
2
- <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="./docs/static/img/banner-nitro-modules-dark.png" />
4
- <source media="(prefers-color-scheme: light)" srcset="./docs/static/img/banner-nitro-modules-light.png" />
5
- <img alt="Nitro Modules" src="./docs/static/img/banner-nitro-modules-light.png" />
6
- </picture>
7
- </a>
8
-
9
- <br />
10
-
11
- **react-native-nitro-fetch** is a general purpose network fetching library for React Native. It can be used as a drop-in replacement for the built-in `fetch(...)` method, as well as provide additional features like prefetching and workletized mappers.
12
-
13
- ## Features
14
-
15
- - 🔧 Drop-in replacement for the built-in `fetch(...)` method
16
- - ⚡️ Fast HTTP stack using [Cronet](https://chromium.googlesource.com/chromium/src/+/lkgr/components/cronet/README.md) on Android, and [URLSession](https://developer.apple.com/documentation/Foundation/URLSession) on iOS
17
- - 💪 Supports [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2), [QUIC](https://www.chromium.org/quic/), [Brotli](https://github.com/google/brotli), and disk cache
18
- - ⏰ Prefetching on app-startup for even faster initialization
19
- - 🧵 Worklet support for parallel data mapping without blocking the JS Thread
20
- - 🔥 Powered by [Nitro Modules](https://github.com/mrousavy/nitro)
21
-
22
- ## Installation
23
-
24
- ```sh
25
- npm i react-native-nitro-fetch react-native-nitro-modules
26
- ```
27
-
28
- > [Nitro Modules](https://github.com/mrousavy/nitro) requires react-native 0.75+ or higher
29
-
30
- ## Usage
31
-
32
- To simply fetch data, import the `fetch(...)` method from `react-native-nitro-fetch`:
33
-
34
- ```ts
35
- import { fetch } from 'react-native-nitro-fetch'
36
-
37
- const res = await fetch('https://httpbin.org/get')
38
- const json = await res.json()
39
- ```
40
-
41
- This can be used as a drop-in-replacement for the built-in `fetch(...)` method.
42
-
43
- ### Prefetching in JS
44
-
45
- You can prefetch a URL in JS, which keeps the result cached for the next actual `fetch(...)` call - this can be used shortly before navigating to a new screen to have results hot & ready:
46
-
47
- ```ts
48
- import { prefetch } from 'react-native-nitro-fetch'
49
-
50
- await prefetch('https://httpbin.org/uuid', {
51
- headers: { prefetchKey: 'uuid' }
52
- })
53
- ```
54
-
55
- Then, on the new screen that was navigated to:
56
-
57
- ```ts
58
- import { fetch } from 'react-native-nitro-fetch'
59
-
60
- const res = await fetch('https://httpbin.org/uuid', {
61
- headers: { prefetchKey: 'uuid' }
62
- })
63
- console.log('prefetched header:', res.headers.get('nitroPrefetched'))
64
- ```
65
-
66
- ### Prefetching for the next app launch
67
-
68
- Prefetching data on app launch (or _process start_) will make it hot & ready once your JS code actually runs. Call `prefetchOnAppStart(...)` to enqueue a prefetch for the **next** app start:
69
-
70
- ```ts
71
- import { prefetchOnAppStart } from 'react-native-nitro-fetch'
72
-
73
- await prefetchOnAppStart('https://httpbin.org/uuid', {
74
- prefetchKey: 'uuid'
75
- })
76
- ```
77
-
78
- Then, once the app opens the next time, a call to `fetch(...)` might resolve faster since it will contain already cached results:
79
-
80
- ```ts
81
- import { fetch } from 'react-native-nitro-fetch'
82
-
83
- const res = await fetch('https://httpbin.org/uuid', {
84
- headers: { prefetchKey: 'uuid' }
85
- })
86
- console.log('prefetched header:', res.headers.get('nitroPrefetched'))
87
- ```
88
-
89
- In our tests, prefetching alone yielded a **~220 ms** faster TTI (time-to-interactive) time! 🤯
90
-
91
- ### Worklet Mapping
92
-
93
- Since Nitro Fetch is a [Nitro Module](https://nitro.margelo.com), it can be used from Worklets.
94
- This can be useful to parse data without blocking the main JS-Thread:
95
-
96
- ```ts
97
- import { nitroFetchOnWorklet } from 'react-native-nitro-fetch'
98
-
99
- const data = await nitroFetchOnWorklet(
100
- 'https://httpbin.org/get',
101
- undefined,
102
- (payload) => {
103
- 'worklet'
104
- return JSON.parse(payload.bodyString ?? '{}')
105
- }
106
- )
107
- ```
108
-
109
- ## Project Status
110
-
111
- Nitro Fetch is currently in an alpha stage. You can adopt it in production, but keep in mind that the library and it's API is subject to change.
112
-
113
- ## Limitations & Alternatives
114
-
115
- - [HTTP streaming](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) is not yet supported. As an alternative, use Expo's [expo-fetch](https://docs.expo.dev/versions/latest/sdk/expo/). Streaming is on the roadmap.
116
- - [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) are not supported. For high‑performance sockets and binary streams, consider using [react-native-fast-io](https://github.com/callstackincubator/react-native-fast-io) by our friends at Callstack.
117
-
118
- ## Documentation
119
-
120
- - [Getting Started](docs/getting-started.md)
121
- - [API Reference](docs/api.md)
122
- - [Android Details](docs/android.md)
123
- - [iOS Details](docs/ios.md)
124
- - [Prefetch & Auto-Prefetch](docs/prefetch.md)
125
- - [Worklets](docs/worklets.md)
126
- - [Troubleshooting](docs/troubleshooting.md)
127
- - [Cronet (Android) notes](docs/cronet-android.md)
128
- - [Cronet (iOS) notes](docs/cronet-ios.md)
129
-
130
- ## Margelo
131
-
132
- Nitro Fetch is built with ❤️ by Margelo.
133
- We build fast and beautiful apps. Contact us at [margelo.com](https://margelo.com) for high-end consultancy services.
134
-
135
- ## Contributing
136
-
137
- - Development workflow: `CONTRIBUTING.md#development-workflow`
138
- - Sending a pull request: `CONTRIBUTING.md#sending-a-pull-request`
139
- - Code of conduct: `CODE_OF_CONDUCT.md`
140
-
141
- ## Authors
142
-
143
- - [Szymon Kapala](https://github.com/Szymon20000)
144
- - [Alex Shumihin](https://github.com/pioner92)
145
- - [Ronald Goedeke](https://github.com/ronickg)
146
- - [Marc Rousavy](https://github.com/mrousavy)
147
-
148
- ## License
149
-
150
- MIT
151
-