react-native-nitro-fetch 1.3.1 → 1.3.3

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 (40) hide show
  1. package/NitroFetch.podspec +1 -3
  2. package/README.md +38 -0
  3. package/android/build.gradle +12 -0
  4. package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +148 -55
  5. package/android/src/main/java/com/margelo/nitro/nitrofetch/DevToolsReporterImpl.kt +27 -36
  6. package/ios/NitroAutoPrefetcher.swift +149 -53
  7. package/ios/NitroDevToolsReporter.mm +37 -31
  8. package/lib/module/CurlGenerator.js.map +2 -1
  9. package/lib/module/Headers.js.map +2 -1
  10. package/lib/module/HermesProfiler.js.map +1 -1
  11. package/lib/module/NetworkInspector.js +1 -1
  12. package/lib/module/NetworkInspector.js.map +2 -1
  13. package/lib/module/NitroCronet.nitro.js.map +2 -1
  14. package/lib/module/NitroFetch.nitro.js.map +1 -2
  15. package/lib/module/NitroInstances.js.map +2 -1
  16. package/lib/module/Response.js.map +1 -2
  17. package/lib/module/fetch.js +8 -10
  18. package/lib/module/fetch.js.map +2 -1
  19. package/lib/module/index.js.map +2 -2
  20. package/lib/module/index.web.js +2 -1
  21. package/lib/module/tokenRefresh.js.map +2 -1
  22. package/lib/module/utf8.js.map +1 -2
  23. package/lib/typescript/src/fetch.d.ts.map +1 -1
  24. package/lib/typescript/src/tokenRefresh.d.ts +14 -0
  25. package/lib/typescript/src/tokenRefresh.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/CurlGenerator.js +23 -26
  28. package/src/Headers.js +108 -116
  29. package/src/HermesProfiler.js +16 -18
  30. package/src/NetworkInspector.js +171 -179
  31. package/src/NitroInstances.js +2 -1
  32. package/src/Request.js +167 -164
  33. package/src/Response.js +244 -242
  34. package/src/fetch.js +708 -693
  35. package/src/fetch.ts +15 -16
  36. package/src/index.js +17 -2
  37. package/src/index.web.js +69 -67
  38. package/src/tokenRefresh.js +75 -77
  39. package/src/tokenRefresh.ts +16 -0
  40. package/src/utf8.js +28 -27
package/src/Headers.js CHANGED
@@ -1,127 +1,119 @@
1
1
  function normalizeName(name) {
2
- return name.toLowerCase();
2
+ return name.toLowerCase();
3
3
  }
4
4
  export class NitroHeaders {
5
- _map;
6
- constructor(init) {
7
- this._map = new Map();
8
- if (!init)
9
- return;
10
- if (init instanceof NitroHeaders) {
11
- init._map.forEach((values, key) => {
12
- this._map.set(key, [...values]);
13
- });
5
+ _map;
6
+ constructor(init) {
7
+ this._map = new Map();
8
+ if (!init) return;
9
+ if (init instanceof NitroHeaders) {
10
+ init._map.forEach((values, key) => {
11
+ this._map.set(key, [...values]);
12
+ });
13
+ } else if (
14
+ typeof init === 'object' &&
15
+ !Array.isArray(init) &&
16
+ typeof init.forEach === 'function' &&
17
+ typeof init.get === 'function'
18
+ ) {
19
+ // Headers-like object (standard Headers or duck-typed)
20
+ init.forEach((value, key) => {
21
+ this._map.set(normalizeName(key), [value]);
22
+ });
23
+ } else if (Array.isArray(init)) {
24
+ for (const entry of init) {
25
+ if (Array.isArray(entry) && entry.length >= 2) {
26
+ // [string, string] tuple
27
+ const key = normalizeName(String(entry[0]));
28
+ const value = String(entry[1]);
29
+ const existing = this._map.get(key);
30
+ if (existing) existing.push(value);
31
+ else this._map.set(key, [value]);
32
+ } else if (
33
+ entry &&
34
+ typeof entry === 'object' &&
35
+ 'key' in entry &&
36
+ 'value' in entry
37
+ ) {
38
+ // NitroHeader object
39
+ const key = normalizeName(entry.key);
40
+ const value = entry.value;
41
+ const existing = this._map.get(key);
42
+ if (existing) existing.push(value);
43
+ else this._map.set(key, [value]);
14
44
  }
15
- else if (typeof init === 'object' &&
16
- !Array.isArray(init) &&
17
- typeof init.forEach === 'function' &&
18
- typeof init.get === 'function') {
19
- // Headers-like object (standard Headers or duck-typed)
20
- init.forEach((value, key) => {
21
- this._map.set(normalizeName(key), [value]);
22
- });
45
+ }
46
+ } else if (typeof init === 'object' && init !== null) {
47
+ const keys = Object.keys(init);
48
+ for (let i = 0; i < keys.length; i++) {
49
+ const k = keys[i];
50
+ const v = init[k];
51
+ if (v !== undefined) {
52
+ this._map.set(normalizeName(k), [String(v)]);
23
53
  }
24
- else if (Array.isArray(init)) {
25
- for (const entry of init) {
26
- if (Array.isArray(entry) && entry.length >= 2) {
27
- // [string, string] tuple
28
- const key = normalizeName(String(entry[0]));
29
- const value = String(entry[1]);
30
- const existing = this._map.get(key);
31
- if (existing)
32
- existing.push(value);
33
- else
34
- this._map.set(key, [value]);
35
- }
36
- else if (entry &&
37
- typeof entry === 'object' &&
38
- 'key' in entry &&
39
- 'value' in entry) {
40
- // NitroHeader object
41
- const key = normalizeName(entry.key);
42
- const value = entry.value;
43
- const existing = this._map.get(key);
44
- if (existing)
45
- existing.push(value);
46
- else
47
- this._map.set(key, [value]);
48
- }
49
- }
50
- }
51
- else if (typeof init === 'object' && init !== null) {
52
- const keys = Object.keys(init);
53
- for (let i = 0; i < keys.length; i++) {
54
- const k = keys[i];
55
- const v = init[k];
56
- if (v !== undefined) {
57
- this._map.set(normalizeName(k), [String(v)]);
58
- }
59
- }
60
- }
61
- }
62
- append(name, value) {
63
- const key = normalizeName(name);
64
- const existing = this._map.get(key);
65
- if (existing)
66
- existing.push(value);
67
- else
68
- this._map.set(key, [value]);
69
- }
70
- delete(name) {
71
- this._map.delete(normalizeName(name));
54
+ }
72
55
  }
73
- get(name) {
74
- const values = this._map.get(normalizeName(name));
75
- if (!values || values.length === 0)
76
- return null;
77
- return values.join(', ');
56
+ }
57
+ append(name, value) {
58
+ const key = normalizeName(name);
59
+ const existing = this._map.get(key);
60
+ if (existing) existing.push(value);
61
+ else this._map.set(key, [value]);
62
+ }
63
+ delete(name) {
64
+ this._map.delete(normalizeName(name));
65
+ }
66
+ get(name) {
67
+ const values = this._map.get(normalizeName(name));
68
+ if (!values || values.length === 0) return null;
69
+ return values.join(', ');
70
+ }
71
+ getSetCookie() {
72
+ return this._map.get('set-cookie') ?? [];
73
+ }
74
+ has(name) {
75
+ return this._map.has(normalizeName(name));
76
+ }
77
+ set(name, value) {
78
+ this._map.set(normalizeName(name), [value]);
79
+ }
80
+ forEach(callback, thisArg) {
81
+ const sortedKeys = Array.from(this._map.keys()).sort();
82
+ for (const key of sortedKeys) {
83
+ callback.call(thisArg, this._map.get(key).join(', '), key, this);
78
84
  }
79
- getSetCookie() {
80
- return this._map.get('set-cookie') ?? [];
85
+ }
86
+ entries() {
87
+ const map = this._map;
88
+ const sortedKeys = Array.from(map.keys()).sort();
89
+ function* gen() {
90
+ for (const key of sortedKeys) {
91
+ yield [key, map.get(key).join(', ')];
92
+ }
81
93
  }
82
- has(name) {
83
- return this._map.has(normalizeName(name));
84
- }
85
- set(name, value) {
86
- this._map.set(normalizeName(name), [value]);
87
- }
88
- forEach(callback, thisArg) {
89
- const sortedKeys = Array.from(this._map.keys()).sort();
90
- for (const key of sortedKeys) {
91
- callback.call(thisArg, this._map.get(key).join(', '), key, this);
92
- }
93
- }
94
- entries() {
95
- const map = this._map;
96
- const sortedKeys = Array.from(map.keys()).sort();
97
- function* gen() {
98
- for (const key of sortedKeys) {
99
- yield [key, map.get(key).join(', ')];
100
- }
101
- }
102
- return gen();
103
- }
104
- keys() {
105
- const map = this._map;
106
- const sortedKeys = Array.from(map.keys()).sort();
107
- function* gen() {
108
- for (const key of sortedKeys) {
109
- yield key;
110
- }
111
- }
112
- return gen();
113
- }
114
- values() {
115
- const map = this._map;
116
- const sortedKeys = Array.from(map.keys()).sort();
117
- function* gen() {
118
- for (const key of sortedKeys) {
119
- yield map.get(key).join(', ');
120
- }
121
- }
122
- return gen();
94
+ return gen();
95
+ }
96
+ keys() {
97
+ const map = this._map;
98
+ const sortedKeys = Array.from(map.keys()).sort();
99
+ function* gen() {
100
+ for (const key of sortedKeys) {
101
+ yield key;
102
+ }
123
103
  }
124
- [Symbol.iterator]() {
125
- return this.entries();
104
+ return gen();
105
+ }
106
+ values() {
107
+ const map = this._map;
108
+ const sortedKeys = Array.from(map.keys()).sort();
109
+ function* gen() {
110
+ for (const key of sortedKeys) {
111
+ yield map.get(key).join(', ');
112
+ }
126
113
  }
114
+ return gen();
115
+ }
116
+ [Symbol.iterator]() {
117
+ return this.entries();
118
+ }
127
119
  }
@@ -1,22 +1,20 @@
1
1
  export async function profileFetch(fn, outputPath) {
2
- const hermes = global.HermesInternal;
3
- if (!hermes) {
4
- const result = await fn();
5
- return { result };
6
- }
7
- const path = outputPath ?? `/tmp/nitrofetch-profile-${Date.now()}.cpuprofile`;
8
- hermes.enableSamplingProfiler();
2
+ const hermes = global.HermesInternal;
3
+ if (!hermes) {
4
+ const result = await fn();
5
+ return { result };
6
+ }
7
+ const path = outputPath ?? `/tmp/nitrofetch-profile-${Date.now()}.cpuprofile`;
8
+ hermes.enableSamplingProfiler();
9
+ try {
10
+ const result = await fn();
11
+ return { result, profilePath: path };
12
+ } finally {
13
+ hermes.disableSamplingProfiler();
9
14
  try {
10
- const result = await fn();
11
- return { result, profilePath: path };
12
- }
13
- finally {
14
- hermes.disableSamplingProfiler();
15
- try {
16
- hermes.dumpSamplingProfiler(path);
17
- }
18
- catch {
19
- // Profile dump may fail on some platforms
20
- }
15
+ hermes.dumpSamplingProfiler(path);
16
+ } catch {
17
+ // Profile dump may fail on some platforms
21
18
  }
19
+ }
22
20
  }
@@ -1,183 +1,175 @@
1
1
  import { generateCurl } from './CurlGenerator';
2
2
  class NetworkInspectorImpl {
3
- _enabled = false;
4
- _entries = [];
5
- _maxEntries = 500;
6
- _maxBodyCapture = 4096;
7
- _listeners = new Set();
8
- enable(options) {
9
- this._enabled = true;
10
- if (options?.maxEntries != null)
11
- this._maxEntries = options.maxEntries;
12
- if (options?.maxBodyCapture != null)
13
- this._maxBodyCapture = options.maxBodyCapture;
14
- }
15
- disable() {
16
- this._enabled = false;
17
- }
18
- isEnabled() {
19
- return this._enabled;
20
- }
21
- getEntries() {
22
- return this._entries;
23
- }
24
- getHttpEntries() {
25
- return this._entries.filter((e) => e.type === 'http');
26
- }
27
- getWebSocketEntries() {
28
- return this._entries.filter((e) => e.type === 'websocket');
29
- }
30
- getEntry(id) {
31
- return this._entries.find((e) => e.id === id);
32
- }
33
- clear() {
34
- this._entries = [];
35
- }
36
- onEntry(callback) {
37
- this._listeners.add(callback);
38
- return () => {
39
- this._listeners.delete(callback);
40
- };
41
- }
42
- _notify(entry) {
43
- for (const cb of this._listeners) {
44
- try {
45
- cb(entry);
46
- }
47
- catch {
48
- // swallow listener errors
49
- }
50
- }
51
- }
52
- _trimEntries() {
53
- if (this._entries.length > this._maxEntries) {
54
- this._entries.shift();
55
- }
56
- }
57
- // --- HTTP recording ---
58
- _recordStart(id, url, method, headers, body) {
59
- if (!this._enabled)
60
- return;
61
- const bodySize = body ? body.length : 0;
62
- const entry = {
63
- id,
64
- type: 'http',
65
- url,
66
- method,
67
- requestHeaders: headers.map((h) => ({ key: h.key, value: h.value })),
68
- requestBody: body ? body.slice(0, this._maxBodyCapture) : undefined,
69
- requestBodySize: bodySize,
70
- status: 0,
71
- statusText: '',
72
- responseHeaders: [],
73
- responseBodySize: 0,
74
- startTime: performance.now(),
75
- endTime: 0,
76
- duration: 0,
77
- curl: generateCurl({ url, method, headers, body }),
78
- };
79
- this._entries.push(entry);
80
- this._trimEntries();
81
- }
82
- _recordEnd(id, status, statusText, headers, bodySize, error, responseBody) {
83
- if (!this._enabled)
84
- return;
85
- const entry = this._entries.find((e) => e.id === id && e.type === 'http');
86
- if (!entry)
87
- return;
88
- entry.status = status;
89
- entry.statusText = statusText;
90
- entry.responseHeaders = headers.map((h) => ({
91
- key: h.key,
92
- value: h.value,
93
- }));
94
- entry.responseBodySize = bodySize;
95
- entry.endTime = performance.now();
96
- entry.duration = entry.endTime - entry.startTime;
97
- if (error)
98
- entry.error = error;
99
- if (responseBody != null) {
100
- entry.responseBody = responseBody.slice(0, this._maxBodyCapture);
101
- }
102
- this._notify(entry);
103
- }
104
- // --- WebSocket recording ---
105
- _recordWsOpen(id, url, protocols, headers) {
106
- if (!this._enabled)
107
- return;
108
- const entry = {
109
- id,
110
- type: 'websocket',
111
- url,
112
- protocols,
113
- requestHeaders: headers.map((h) => ({ key: h.key, value: h.value })),
114
- startTime: performance.now(),
115
- endTime: 0,
116
- duration: 0,
117
- readyState: 'CONNECTING',
118
- messages: [],
119
- messagesSent: 0,
120
- messagesReceived: 0,
121
- bytesSent: 0,
122
- bytesReceived: 0,
123
- };
124
- this._entries.push(entry);
125
- this._trimEntries();
126
- this._notify(entry);
127
- }
128
- _recordWsConnected(id) {
129
- if (!this._enabled)
130
- return;
131
- const entry = this._entries.find((e) => e.id === id && e.type === 'websocket');
132
- if (!entry)
133
- return;
134
- entry.readyState = 'OPEN';
135
- this._notify(entry);
136
- }
137
- _recordWsMessage(id, direction, data, size, isBinary) {
138
- if (!this._enabled)
139
- return;
140
- const entry = this._entries.find((e) => e.id === id && e.type === 'websocket');
141
- if (!entry)
142
- return;
143
- entry.messages.push({
144
- direction,
145
- data: data.slice(0, this._maxBodyCapture),
146
- size,
147
- isBinary,
148
- timestamp: performance.now(),
149
- });
150
- if (direction === 'sent') {
151
- entry.messagesSent++;
152
- entry.bytesSent += size;
153
- }
154
- else {
155
- entry.messagesReceived++;
156
- entry.bytesReceived += size;
157
- }
158
- this._notify(entry);
159
- }
160
- _recordWsClose(id, code, reason) {
161
- if (!this._enabled)
162
- return;
163
- const entry = this._entries.find((e) => e.id === id && e.type === 'websocket');
164
- if (!entry)
165
- return;
166
- entry.readyState = 'CLOSED';
167
- entry.closeCode = code;
168
- entry.closeReason = reason;
169
- entry.endTime = performance.now();
170
- entry.duration = entry.endTime - entry.startTime;
171
- this._notify(entry);
172
- }
173
- _recordWsError(id, error) {
174
- if (!this._enabled)
175
- return;
176
- const entry = this._entries.find((e) => e.id === id && e.type === 'websocket');
177
- if (!entry)
178
- return;
179
- entry.error = error;
180
- this._notify(entry);
181
- }
3
+ _enabled = false;
4
+ _entries = [];
5
+ _maxEntries = 500;
6
+ _maxBodyCapture = 4096;
7
+ _listeners = new Set();
8
+ enable(options) {
9
+ this._enabled = true;
10
+ if (options?.maxEntries != null) this._maxEntries = options.maxEntries;
11
+ if (options?.maxBodyCapture != null)
12
+ this._maxBodyCapture = options.maxBodyCapture;
13
+ }
14
+ disable() {
15
+ this._enabled = false;
16
+ }
17
+ isEnabled() {
18
+ return this._enabled;
19
+ }
20
+ getEntries() {
21
+ return this._entries;
22
+ }
23
+ getHttpEntries() {
24
+ return this._entries.filter((e) => e.type === 'http');
25
+ }
26
+ getWebSocketEntries() {
27
+ return this._entries.filter((e) => e.type === 'websocket');
28
+ }
29
+ getEntry(id) {
30
+ return this._entries.find((e) => e.id === id);
31
+ }
32
+ clear() {
33
+ this._entries = [];
34
+ }
35
+ onEntry(callback) {
36
+ this._listeners.add(callback);
37
+ return () => {
38
+ this._listeners.delete(callback);
39
+ };
40
+ }
41
+ _notify(entry) {
42
+ for (const cb of this._listeners) {
43
+ try {
44
+ cb(entry);
45
+ } catch {
46
+ // swallow listener errors
47
+ }
48
+ }
49
+ }
50
+ _trimEntries() {
51
+ if (this._entries.length > this._maxEntries) {
52
+ this._entries.shift();
53
+ }
54
+ }
55
+ // --- HTTP recording ---
56
+ _recordStart(id, url, method, headers, body) {
57
+ if (!this._enabled) return;
58
+ const bodySize = body ? body.length : 0;
59
+ const entry = {
60
+ id,
61
+ type: 'http',
62
+ url,
63
+ method,
64
+ requestHeaders: headers.map((h) => ({ key: h.key, value: h.value })),
65
+ requestBody: body ? body.slice(0, this._maxBodyCapture) : undefined,
66
+ requestBodySize: bodySize,
67
+ status: 0,
68
+ statusText: '',
69
+ responseHeaders: [],
70
+ responseBodySize: 0,
71
+ startTime: performance.now(),
72
+ endTime: 0,
73
+ duration: 0,
74
+ curl: generateCurl({ url, method, headers, body }),
75
+ };
76
+ this._entries.push(entry);
77
+ this._trimEntries();
78
+ }
79
+ _recordEnd(id, status, statusText, headers, bodySize, error, responseBody) {
80
+ if (!this._enabled) return;
81
+ const entry = this._entries.find((e) => e.id === id && e.type === 'http');
82
+ if (!entry) return;
83
+ entry.status = status;
84
+ entry.statusText = statusText;
85
+ entry.responseHeaders = headers.map((h) => ({
86
+ key: h.key,
87
+ value: h.value,
88
+ }));
89
+ entry.responseBodySize = bodySize;
90
+ entry.endTime = performance.now();
91
+ entry.duration = entry.endTime - entry.startTime;
92
+ if (error) entry.error = error;
93
+ if (responseBody != null) {
94
+ entry.responseBody = responseBody.slice(0, this._maxBodyCapture);
95
+ }
96
+ this._notify(entry);
97
+ }
98
+ // --- WebSocket recording ---
99
+ _recordWsOpen(id, url, protocols, headers) {
100
+ if (!this._enabled) return;
101
+ const entry = {
102
+ id,
103
+ type: 'websocket',
104
+ url,
105
+ protocols,
106
+ requestHeaders: headers.map((h) => ({ key: h.key, value: h.value })),
107
+ startTime: performance.now(),
108
+ endTime: 0,
109
+ duration: 0,
110
+ readyState: 'CONNECTING',
111
+ messages: [],
112
+ messagesSent: 0,
113
+ messagesReceived: 0,
114
+ bytesSent: 0,
115
+ bytesReceived: 0,
116
+ };
117
+ this._entries.push(entry);
118
+ this._trimEntries();
119
+ this._notify(entry);
120
+ }
121
+ _recordWsConnected(id) {
122
+ if (!this._enabled) return;
123
+ const entry = this._entries.find(
124
+ (e) => e.id === id && e.type === 'websocket'
125
+ );
126
+ if (!entry) return;
127
+ entry.readyState = 'OPEN';
128
+ this._notify(entry);
129
+ }
130
+ _recordWsMessage(id, direction, data, size, isBinary) {
131
+ if (!this._enabled) return;
132
+ const entry = this._entries.find(
133
+ (e) => e.id === id && e.type === 'websocket'
134
+ );
135
+ if (!entry) return;
136
+ entry.messages.push({
137
+ direction,
138
+ data: data.slice(0, this._maxBodyCapture),
139
+ size,
140
+ isBinary,
141
+ timestamp: performance.now(),
142
+ });
143
+ if (direction === 'sent') {
144
+ entry.messagesSent++;
145
+ entry.bytesSent += size;
146
+ } else {
147
+ entry.messagesReceived++;
148
+ entry.bytesReceived += size;
149
+ }
150
+ this._notify(entry);
151
+ }
152
+ _recordWsClose(id, code, reason) {
153
+ if (!this._enabled) return;
154
+ const entry = this._entries.find(
155
+ (e) => e.id === id && e.type === 'websocket'
156
+ );
157
+ if (!entry) return;
158
+ entry.readyState = 'CLOSED';
159
+ entry.closeCode = code;
160
+ entry.closeReason = reason;
161
+ entry.endTime = performance.now();
162
+ entry.duration = entry.endTime - entry.startTime;
163
+ this._notify(entry);
164
+ }
165
+ _recordWsError(id, error) {
166
+ if (!this._enabled) return;
167
+ const entry = this._entries.find(
168
+ (e) => e.id === id && e.type === 'websocket'
169
+ );
170
+ if (!entry) return;
171
+ entry.error = error;
172
+ this._notify(entry);
173
+ }
182
174
  }
183
175
  export const NetworkInspector = new NetworkInspectorImpl();
@@ -3,4 +3,5 @@ import { NitroModules } from 'react-native-nitro-modules';
3
3
  export const NitroFetch = NitroModules.createHybridObject('NitroFetch');
4
4
  export const NativeStorage = NitroModules.createHybridObject('NativeStorage');
5
5
  export const boxedNitroFetch = NitroModules.box(NitroFetch);
6
- export const NitroCronetSingleton = NitroModules.createHybridObject('NitroCronet');
6
+ export const NitroCronetSingleton =
7
+ NitroModules.createHybridObject('NitroCronet');