react-native-nitro-fetch 0.1.2 → 0.1.4

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