react-native-nitro-fetch 0.3.0-beta.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitroFetch.podspec +8 -0
- package/android/build.gradle +3 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +46 -2
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchPackage.kt +2 -2
- package/ios/NitroFetchClient.swift +36 -1
- package/lib/module/CurlGenerator.js +28 -0
- package/lib/module/CurlGenerator.js.map +1 -0
- package/lib/module/Headers.js +95 -0
- package/lib/module/Headers.js.map +1 -0
- package/lib/module/HermesProfiler.js +28 -0
- package/lib/module/HermesProfiler.js.map +1 -0
- package/lib/module/NetworkInspector.js +184 -0
- package/lib/module/NetworkInspector.js.map +1 -0
- package/lib/module/Request.js +120 -0
- package/lib/module/Request.js.map +1 -0
- package/lib/module/Response.js +236 -0
- package/lib/module/Response.js.map +1 -0
- package/lib/module/fetch.js +143 -56
- package/lib/module/fetch.js.map +1 -1
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utf8.js +29 -0
- package/lib/module/utf8.js.map +1 -0
- package/lib/typescript/src/CurlGenerator.d.ts +13 -0
- package/lib/typescript/src/CurlGenerator.d.ts.map +1 -0
- package/lib/typescript/src/Headers.d.ts +19 -0
- package/lib/typescript/src/Headers.d.ts.map +1 -0
- package/lib/typescript/src/HermesProfiler.d.ts +6 -0
- package/lib/typescript/src/HermesProfiler.d.ts.map +1 -0
- package/lib/typescript/src/NetworkInspector.d.ts +96 -0
- package/lib/typescript/src/NetworkInspector.d.ts.map +1 -0
- package/lib/typescript/src/Request.d.ts +48 -0
- package/lib/typescript/src/Request.d.ts.map +1 -0
- package/lib/typescript/src/Response.d.ts +56 -0
- package/lib/typescript/src/Response.d.ts.map +1 -0
- package/lib/typescript/src/fetch.d.ts +11 -3
- package/lib/typescript/src/fetch.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +13 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utf8.d.ts +3 -0
- package/lib/typescript/src/utf8.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/CurlGenerator.ts +44 -0
- package/src/Headers.ts +127 -0
- package/src/HermesProfiler.ts +37 -0
- package/src/NetworkInspector.ts +278 -0
- package/src/Request.ts +187 -0
- package/src/Response.ts +335 -0
- package/src/fetch.ts +186 -75
- package/src/index.tsx +22 -1
- package/src/utf8.ts +40 -0
package/src/Response.ts
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { NitroHeaders } from './Headers';
|
|
2
|
+
import { stringToUTF8, utf8ToString } from './utf8';
|
|
3
|
+
import type { NitroHeader } from './NitroFetch.nitro';
|
|
4
|
+
|
|
5
|
+
export type ResponseType =
|
|
6
|
+
| 'basic'
|
|
7
|
+
| 'cors'
|
|
8
|
+
| 'default'
|
|
9
|
+
| 'error'
|
|
10
|
+
| 'opaque'
|
|
11
|
+
| 'opaqueredirect';
|
|
12
|
+
|
|
13
|
+
export interface NitroResponseInit {
|
|
14
|
+
url: string;
|
|
15
|
+
status: number;
|
|
16
|
+
statusText: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
redirected: boolean;
|
|
19
|
+
headers: NitroHeader[] | NitroHeaders;
|
|
20
|
+
bodyBytes?: ArrayBuffer;
|
|
21
|
+
bodyString?: string;
|
|
22
|
+
body?: ReadableStream<Uint8Array>;
|
|
23
|
+
type?: ResponseType;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type BodyInit =
|
|
27
|
+
| string
|
|
28
|
+
| Blob
|
|
29
|
+
| ArrayBuffer
|
|
30
|
+
| ArrayBufferView
|
|
31
|
+
| ReadableStream<Uint8Array>
|
|
32
|
+
| URLSearchParams
|
|
33
|
+
| null;
|
|
34
|
+
|
|
35
|
+
export interface ResponseInit {
|
|
36
|
+
status?: number;
|
|
37
|
+
statusText?: string;
|
|
38
|
+
headers?: HeadersInit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isNitroResponseInit(arg: any): arg is NitroResponseInit {
|
|
42
|
+
return (
|
|
43
|
+
arg != null &&
|
|
44
|
+
typeof arg === 'object' &&
|
|
45
|
+
'url' in arg &&
|
|
46
|
+
'status' in arg &&
|
|
47
|
+
'ok' in arg
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class NitroResponse {
|
|
52
|
+
readonly url: string;
|
|
53
|
+
readonly ok: boolean;
|
|
54
|
+
readonly status: number;
|
|
55
|
+
readonly statusText: string;
|
|
56
|
+
readonly redirected: boolean;
|
|
57
|
+
readonly headers: NitroHeaders;
|
|
58
|
+
readonly type: ResponseType;
|
|
59
|
+
|
|
60
|
+
private _bodyBytes: ArrayBuffer | undefined;
|
|
61
|
+
private _bodyString: string | undefined;
|
|
62
|
+
private _bodyStream: ReadableStream<Uint8Array> | undefined;
|
|
63
|
+
private _bodyUsed: boolean = false;
|
|
64
|
+
|
|
65
|
+
constructor(body?: BodyInit | null, init?: ResponseInit);
|
|
66
|
+
constructor(init: NitroResponseInit);
|
|
67
|
+
constructor(
|
|
68
|
+
bodyOrInit?: BodyInit | NitroResponseInit | null,
|
|
69
|
+
init?: ResponseInit
|
|
70
|
+
) {
|
|
71
|
+
if (isNitroResponseInit(bodyOrInit)) {
|
|
72
|
+
// Internal constructor path
|
|
73
|
+
const nitroInit = bodyOrInit;
|
|
74
|
+
this.url = nitroInit.url;
|
|
75
|
+
this.ok = nitroInit.ok;
|
|
76
|
+
this.status = nitroInit.status;
|
|
77
|
+
this.statusText = nitroInit.statusText;
|
|
78
|
+
this.redirected = nitroInit.redirected;
|
|
79
|
+
this.type = nitroInit.type ?? 'basic';
|
|
80
|
+
|
|
81
|
+
if (nitroInit.headers instanceof NitroHeaders) {
|
|
82
|
+
this.headers = nitroInit.headers;
|
|
83
|
+
} else {
|
|
84
|
+
this.headers = new NitroHeaders(nitroInit.headers);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this._bodyBytes = nitroInit.bodyBytes;
|
|
88
|
+
this._bodyString = nitroInit.bodyString;
|
|
89
|
+
this._bodyStream = nitroInit.body;
|
|
90
|
+
} else {
|
|
91
|
+
// Public constructor: new Response(body?, init?)
|
|
92
|
+
const body = bodyOrInit as BodyInit | null | undefined;
|
|
93
|
+
this.status = init?.status ?? 200;
|
|
94
|
+
this.statusText = init?.statusText ?? '';
|
|
95
|
+
this.ok = this.status >= 200 && this.status < 300;
|
|
96
|
+
this.url = '';
|
|
97
|
+
this.redirected = false;
|
|
98
|
+
this.type = 'default';
|
|
99
|
+
this.headers = new NitroHeaders(init?.headers as any);
|
|
100
|
+
|
|
101
|
+
if (body == null) {
|
|
102
|
+
// no body
|
|
103
|
+
} else if (typeof body === 'string') {
|
|
104
|
+
this._bodyString = body;
|
|
105
|
+
} else if (body instanceof ArrayBuffer) {
|
|
106
|
+
this._bodyBytes = body;
|
|
107
|
+
} else if (ArrayBuffer.isView(body)) {
|
|
108
|
+
const view = body as ArrayBufferView;
|
|
109
|
+
this._bodyBytes = (view.buffer as ArrayBuffer).slice(
|
|
110
|
+
view.byteOffset,
|
|
111
|
+
view.byteOffset + view.byteLength
|
|
112
|
+
);
|
|
113
|
+
} else if (
|
|
114
|
+
typeof ReadableStream !== 'undefined' &&
|
|
115
|
+
body instanceof ReadableStream
|
|
116
|
+
) {
|
|
117
|
+
this._bodyStream = body;
|
|
118
|
+
} else if (
|
|
119
|
+
typeof URLSearchParams !== 'undefined' &&
|
|
120
|
+
body instanceof URLSearchParams
|
|
121
|
+
) {
|
|
122
|
+
this._bodyString = body.toString();
|
|
123
|
+
if (!this.headers.has('content-type')) {
|
|
124
|
+
this.headers.set(
|
|
125
|
+
'content-type',
|
|
126
|
+
'application/x-www-form-urlencoded;charset=UTF-8'
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
} else if (typeof Blob !== 'undefined' && body instanceof Blob) {
|
|
130
|
+
// Store as string — RN Blobs are string-backed
|
|
131
|
+
this._bodyString = '';
|
|
132
|
+
this._bodyStream = body.stream?.() as
|
|
133
|
+
| ReadableStream<Uint8Array>
|
|
134
|
+
| undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
get bodyUsed(): boolean {
|
|
140
|
+
return this._bodyUsed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
get body(): ReadableStream<Uint8Array> | null {
|
|
144
|
+
if (this._bodyStream) return this._bodyStream;
|
|
145
|
+
const bytes = this._getBodyBytes();
|
|
146
|
+
if (!bytes) return null;
|
|
147
|
+
return new ReadableStream<Uint8Array>({
|
|
148
|
+
start(controller) {
|
|
149
|
+
controller.enqueue(new Uint8Array(bytes));
|
|
150
|
+
controller.close();
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private _throwIfBodyUsed(): void {
|
|
156
|
+
if (this._bodyUsed) {
|
|
157
|
+
throw new TypeError('Body has already been consumed.');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private _getBodyBytes(): ArrayBuffer | undefined {
|
|
162
|
+
if (this._bodyBytes) return this._bodyBytes;
|
|
163
|
+
if (this._bodyString != null) {
|
|
164
|
+
const encoded = stringToUTF8(this._bodyString);
|
|
165
|
+
return (encoded.buffer as ArrayBuffer).slice(
|
|
166
|
+
encoded.byteOffset,
|
|
167
|
+
encoded.byteOffset + encoded.byteLength
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private _getBodyString(): string {
|
|
174
|
+
if (this._bodyString != null) return this._bodyString;
|
|
175
|
+
if (this._bodyBytes) {
|
|
176
|
+
return utf8ToString(new Uint8Array(this._bodyBytes));
|
|
177
|
+
}
|
|
178
|
+
return '';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async text(): Promise<string> {
|
|
182
|
+
this._throwIfBodyUsed();
|
|
183
|
+
this._bodyUsed = true;
|
|
184
|
+
if (this._bodyStream && !this._bodyBytes && this._bodyString == null) {
|
|
185
|
+
const reader = this._bodyStream.getReader();
|
|
186
|
+
const chunks: Uint8Array[] = [];
|
|
187
|
+
while (true) {
|
|
188
|
+
const { done, value } = await reader.read();
|
|
189
|
+
if (done) break;
|
|
190
|
+
if (value) chunks.push(value);
|
|
191
|
+
}
|
|
192
|
+
// Concatenate chunks
|
|
193
|
+
let totalLen = 0;
|
|
194
|
+
for (const c of chunks) totalLen += c.byteLength;
|
|
195
|
+
const merged = new Uint8Array(totalLen);
|
|
196
|
+
let offset = 0;
|
|
197
|
+
for (const c of chunks) {
|
|
198
|
+
merged.set(c, offset);
|
|
199
|
+
offset += c.byteLength;
|
|
200
|
+
}
|
|
201
|
+
return utf8ToString(merged);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return this._getBodyString();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async json(): Promise<any> {
|
|
208
|
+
this._throwIfBodyUsed();
|
|
209
|
+
this._bodyUsed = true;
|
|
210
|
+
const t = this._getBodyString();
|
|
211
|
+
return JSON.parse(t || '{}');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async arrayBuffer(): Promise<ArrayBuffer> {
|
|
215
|
+
this._throwIfBodyUsed();
|
|
216
|
+
this._bodyUsed = true;
|
|
217
|
+
if (this._bodyStream && !this._bodyBytes && this._bodyString == null) {
|
|
218
|
+
const reader = this._bodyStream.getReader();
|
|
219
|
+
const chunks: Uint8Array[] = [];
|
|
220
|
+
while (true) {
|
|
221
|
+
const { done, value } = await reader.read();
|
|
222
|
+
if (done) break;
|
|
223
|
+
if (value) chunks.push(value);
|
|
224
|
+
}
|
|
225
|
+
let totalLen = 0;
|
|
226
|
+
for (const c of chunks) totalLen += c.byteLength;
|
|
227
|
+
const merged = new Uint8Array(totalLen);
|
|
228
|
+
let offset = 0;
|
|
229
|
+
for (const c of chunks) {
|
|
230
|
+
merged.set(c, offset);
|
|
231
|
+
offset += c.byteLength;
|
|
232
|
+
}
|
|
233
|
+
return merged.buffer.slice(
|
|
234
|
+
merged.byteOffset,
|
|
235
|
+
merged.byteOffset + merged.byteLength
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return this._getBodyBytes() ?? new ArrayBuffer(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async blob(): Promise<Blob> {
|
|
243
|
+
this._throwIfBodyUsed();
|
|
244
|
+
this._bodyUsed = true;
|
|
245
|
+
// RN's Blob doesn't support ArrayBuffer/ArrayBufferView — use string body
|
|
246
|
+
const bodyStr = this._getBodyString();
|
|
247
|
+
const contentType = this.headers.get('content-type') ?? '';
|
|
248
|
+
return new Blob([bodyStr], { type: contentType });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async bytes(): Promise<Uint8Array> {
|
|
252
|
+
this._throwIfBodyUsed();
|
|
253
|
+
this._bodyUsed = true;
|
|
254
|
+
const buffer = this._getBodyBytes() ?? new ArrayBuffer(0);
|
|
255
|
+
return new Uint8Array(buffer);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
clone(): NitroResponse {
|
|
259
|
+
if (this._bodyUsed) {
|
|
260
|
+
throw new TypeError('Cannot clone a Response whose body has been used.');
|
|
261
|
+
}
|
|
262
|
+
return new NitroResponse({
|
|
263
|
+
url: this.url,
|
|
264
|
+
status: this.status,
|
|
265
|
+
statusText: this.statusText,
|
|
266
|
+
ok: this.ok,
|
|
267
|
+
redirected: this.redirected,
|
|
268
|
+
headers: this.headers,
|
|
269
|
+
bodyBytes: this._bodyBytes,
|
|
270
|
+
bodyString: this._bodyString,
|
|
271
|
+
type: this.type,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async formData(): Promise<never> {
|
|
276
|
+
throw new TypeError('formData() is not supported in NitroResponse');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// --- Static methods ---
|
|
280
|
+
|
|
281
|
+
static error(): NitroResponse {
|
|
282
|
+
return new NitroResponse({
|
|
283
|
+
url: '',
|
|
284
|
+
status: 0,
|
|
285
|
+
statusText: '',
|
|
286
|
+
ok: false,
|
|
287
|
+
redirected: false,
|
|
288
|
+
headers: [],
|
|
289
|
+
type: 'error',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static json(
|
|
294
|
+
data: unknown,
|
|
295
|
+
init?: {
|
|
296
|
+
status?: number;
|
|
297
|
+
statusText?: string;
|
|
298
|
+
headers?: Record<string, string> | [string, string][];
|
|
299
|
+
}
|
|
300
|
+
): NitroResponse {
|
|
301
|
+
const body = JSON.stringify(data);
|
|
302
|
+
const headers = new NitroHeaders(init?.headers as any);
|
|
303
|
+
if (!headers.has('content-type')) {
|
|
304
|
+
headers.set('content-type', 'application/json');
|
|
305
|
+
}
|
|
306
|
+
return new NitroResponse({
|
|
307
|
+
url: '',
|
|
308
|
+
status: init?.status ?? 200,
|
|
309
|
+
statusText: init?.statusText ?? '',
|
|
310
|
+
ok: (init?.status ?? 200) >= 200 && (init?.status ?? 200) < 300,
|
|
311
|
+
redirected: false,
|
|
312
|
+
headers,
|
|
313
|
+
bodyString: body,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
static redirect(url: string, status: number = 302): NitroResponse {
|
|
318
|
+
const validStatuses = [301, 302, 303, 307, 308];
|
|
319
|
+
if (!validStatuses.includes(status)) {
|
|
320
|
+
throw new RangeError(
|
|
321
|
+
`Invalid redirect status: ${status}. Must be one of ${validStatuses.join(', ')}`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
const headers = new NitroHeaders();
|
|
325
|
+
headers.set('location', url);
|
|
326
|
+
return new NitroResponse({
|
|
327
|
+
url: '',
|
|
328
|
+
status,
|
|
329
|
+
statusText: '',
|
|
330
|
+
ok: false,
|
|
331
|
+
redirected: false,
|
|
332
|
+
headers,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|