react-native-nitro-fetch 0.1.6 → 0.2.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.
Files changed (155) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +3 -1
  3. package/android/src/main/java/com/margelo/nitro/nitrofetch/CronetExtensions.kt +31 -0
  4. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroCronet.kt +89 -0
  5. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +115 -28
  6. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroUrlRequest.kt +35 -0
  7. package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroUrlRequestBuilder.kt +181 -0
  8. package/ios/HybridNitroCronet.swift +31 -0
  9. package/ios/HybridUrlRequest.swift +41 -0
  10. package/ios/HybridUrlRequestBuilder.swift +255 -0
  11. package/ios/NitroAutoPrefetcher.swift +3 -1
  12. package/ios/NitroFetchClient.swift +87 -5
  13. package/ios/URLSessionExtensions.swift +36 -0
  14. package/lib/module/NitroCronet.nitro.js +4 -0
  15. package/lib/module/NitroCronet.nitro.js.map +1 -0
  16. package/lib/module/NitroInstances.js +1 -0
  17. package/lib/module/NitroInstances.js.map +1 -1
  18. package/lib/module/fetch.js +179 -9
  19. package/lib/module/fetch.js.map +1 -1
  20. package/lib/typescript/src/NitroCronet.nitro.d.ts +53 -0
  21. package/lib/typescript/src/NitroCronet.nitro.d.ts.map +1 -0
  22. package/lib/typescript/src/NitroFetch.nitro.d.ts +10 -0
  23. package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -1
  24. package/lib/typescript/src/NitroInstances.d.ts +3 -1
  25. package/lib/typescript/src/NitroInstances.d.ts.map +1 -1
  26. package/lib/typescript/src/fetch.d.ts +5 -2
  27. package/lib/typescript/src/fetch.d.ts.map +1 -1
  28. package/lib/typescript/src/index.d.ts +1 -1
  29. package/lib/typescript/src/index.d.ts.map +1 -1
  30. package/nitro.json +34 -6
  31. package/nitrogen/generated/android/c++/JFunc_void_UrlResponseInfo.hpp +82 -0
  32. package/nitrogen/generated/android/c++/JFunc_void_UrlResponseInfo_std__shared_ptr_ArrayBuffer__double.hpp +84 -0
  33. package/nitrogen/generated/android/c++/JFunc_void_UrlResponseInfo_std__string.hpp +82 -0
  34. package/nitrogen/generated/android/c++/JFunc_void_std__optional_UrlResponseInfo_.hpp +83 -0
  35. package/nitrogen/generated/android/c++/JFunc_void_std__optional_UrlResponseInfo__RequestException.hpp +85 -0
  36. package/nitrogen/generated/android/c++/JHttpHeader.hpp +61 -0
  37. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +23 -16
  38. package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +20 -21
  39. package/nitrogen/generated/android/c++/JHybridNitroCronetSpec.cpp +57 -0
  40. package/nitrogen/generated/android/c++/JHybridNitroCronetSpec.hpp +63 -0
  41. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +32 -16
  42. package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +21 -21
  43. package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.cpp +22 -15
  44. package/nitrogen/generated/android/c++/JHybridNitroFetchSpec.hpp +20 -21
  45. package/nitrogen/generated/android/c++/JHybridUrlRequestBuilderSpec.cpp +123 -0
  46. package/nitrogen/generated/android/c++/JHybridUrlRequestBuilderSpec.hpp +73 -0
  47. package/nitrogen/generated/android/c++/JHybridUrlRequestSpec.cpp +69 -0
  48. package/nitrogen/generated/android/c++/JHybridUrlRequestSpec.hpp +67 -0
  49. package/nitrogen/generated/android/c++/JNitroFormDataPart.hpp +74 -0
  50. package/nitrogen/generated/android/c++/JNitroHeader.hpp +7 -3
  51. package/nitrogen/generated/android/c++/JNitroRequest.hpp +39 -6
  52. package/nitrogen/generated/android/c++/JNitroRequestMethod.hpp +9 -10
  53. package/nitrogen/generated/android/c++/JNitroResponse.hpp +9 -4
  54. package/nitrogen/generated/android/c++/JRequestException.hpp +57 -0
  55. package/nitrogen/generated/android/c++/JUrlResponseInfo.hpp +146 -0
  56. package/nitrogen/generated/android/c++/JVariant_ArrayBuffer_String.cpp +26 -0
  57. package/nitrogen/generated/android/c++/JVariant_ArrayBuffer_String.hpp +70 -0
  58. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Func_void_UrlResponseInfo.kt +80 -0
  59. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Func_void_UrlResponseInfo_std__shared_ptr_ArrayBuffer__double.kt +80 -0
  60. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Func_void_UrlResponseInfo_std__string.kt +80 -0
  61. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Func_void_std__optional_UrlResponseInfo_.kt +80 -0
  62. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Func_void_std__optional_UrlResponseInfo__RequestException.kt +80 -0
  63. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HttpHeader.kt +41 -0
  64. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +18 -16
  65. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroCronetSpec.kt +54 -0
  66. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +23 -16
  67. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchSpec.kt +18 -16
  68. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridUrlRequestBuilderSpec.kt +125 -0
  69. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridUrlRequestSpec.kt +70 -0
  70. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroFormDataPart.kt +50 -0
  71. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroHeader.kt +19 -10
  72. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +40 -25
  73. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequestMethod.kt +3 -1
  74. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroResponse.kt +39 -30
  75. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/RequestException.kt +38 -0
  76. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/UrlResponseInfo.kt +65 -0
  77. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/Variant_ArrayBuffer_String.kt +53 -0
  78. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/nitrofetchOnLoad.kt +1 -1
  79. package/nitrogen/generated/android/nitrofetch+autolinking.cmake +8 -1
  80. package/nitrogen/generated/android/nitrofetch+autolinking.gradle +1 -1
  81. package/nitrogen/generated/android/nitrofetchOnLoad.cpp +85 -33
  82. package/nitrogen/generated/android/nitrofetchOnLoad.hpp +14 -5
  83. package/nitrogen/generated/ios/NitroFetch+autolinking.rb +2 -2
  84. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +102 -10
  85. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +359 -24
  86. package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +32 -1
  87. package/nitrogen/generated/ios/NitroFetchAutolinking.mm +9 -1
  88. package/nitrogen/generated/ios/NitroFetchAutolinking.swift +29 -22
  89. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +1 -1
  90. package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +10 -1
  91. package/nitrogen/generated/ios/c++/HybridNitroCronetSpecSwift.cpp +11 -0
  92. package/nitrogen/generated/ios/c++/HybridNitroCronetSpecSwift.hpp +85 -0
  93. package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.cpp +1 -1
  94. package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +22 -4
  95. package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.cpp +1 -1
  96. package/nitrogen/generated/ios/c++/HybridNitroFetchSpecSwift.hpp +10 -1
  97. package/nitrogen/generated/ios/c++/HybridUrlRequestBuilderSpecSwift.cpp +11 -0
  98. package/nitrogen/generated/ios/c++/HybridUrlRequestBuilderSpecSwift.hpp +163 -0
  99. package/nitrogen/generated/ios/c++/HybridUrlRequestSpecSwift.cpp +11 -0
  100. package/nitrogen/generated/ios/c++/HybridUrlRequestSpecSwift.hpp +106 -0
  101. package/nitrogen/generated/ios/swift/Func_void.swift +1 -2
  102. package/nitrogen/generated/ios/swift/Func_void_NitroResponse.swift +1 -2
  103. package/nitrogen/generated/ios/swift/Func_void_UrlResponseInfo.swift +46 -0
  104. package/nitrogen/generated/ios/swift/Func_void_UrlResponseInfo_std__shared_ptr_ArrayBuffer__double.swift +46 -0
  105. package/nitrogen/generated/ios/swift/Func_void_UrlResponseInfo_std__string.swift +46 -0
  106. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -2
  107. package/nitrogen/generated/ios/swift/Func_void_std__optional_UrlResponseInfo_.swift +46 -0
  108. package/nitrogen/generated/ios/swift/Func_void_std__optional_UrlResponseInfo__RequestException.swift +46 -0
  109. package/nitrogen/generated/ios/swift/HttpHeader.swift +34 -0
  110. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +10 -4
  111. package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +18 -3
  112. package/nitrogen/generated/ios/swift/HybridNitroCronetSpec.swift +55 -0
  113. package/nitrogen/generated/ios/swift/HybridNitroCronetSpec_cxx.swift +141 -0
  114. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +11 -4
  115. package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +29 -3
  116. package/nitrogen/generated/ios/swift/HybridNitroFetchSpec.swift +10 -4
  117. package/nitrogen/generated/ios/swift/HybridNitroFetchSpec_cxx.swift +18 -3
  118. package/nitrogen/generated/ios/swift/HybridUrlRequestBuilderSpec.swift +65 -0
  119. package/nitrogen/generated/ios/swift/HybridUrlRequestBuilderSpec_cxx.swift +305 -0
  120. package/nitrogen/generated/ios/swift/HybridUrlRequestSpec.swift +59 -0
  121. package/nitrogen/generated/ios/swift/HybridUrlRequestSpec_cxx.swift +182 -0
  122. package/nitrogen/generated/ios/swift/NitroFormDataPart.swift +101 -0
  123. package/nitrogen/generated/ios/swift/NitroHeader.swift +5 -17
  124. package/nitrogen/generated/ios/swift/NitroRequest.swift +111 -121
  125. package/nitrogen/generated/ios/swift/NitroRequestMethod.swift +1 -1
  126. package/nitrogen/generated/ios/swift/NitroResponse.swift +40 -97
  127. package/nitrogen/generated/ios/swift/RequestException.swift +29 -0
  128. package/nitrogen/generated/ios/swift/UrlResponseInfo.swift +118 -0
  129. package/nitrogen/generated/ios/swift/Variant_ArrayBuffer_String.swift +18 -0
  130. package/nitrogen/generated/shared/c++/HttpHeader.hpp +87 -0
  131. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +1 -1
  132. package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +1 -1
  133. package/nitrogen/generated/shared/c++/HybridNitroCronetSpec.cpp +21 -0
  134. package/nitrogen/generated/shared/c++/HybridNitroCronetSpec.hpp +65 -0
  135. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +2 -1
  136. package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +3 -1
  137. package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.cpp +1 -1
  138. package/nitrogen/generated/shared/c++/HybridNitroFetchSpec.hpp +1 -1
  139. package/nitrogen/generated/shared/c++/HybridUrlRequestBuilderSpec.cpp +31 -0
  140. package/nitrogen/generated/shared/c++/HybridUrlRequestBuilderSpec.hpp +85 -0
  141. package/nitrogen/generated/shared/c++/HybridUrlRequestSpec.cpp +25 -0
  142. package/nitrogen/generated/shared/c++/HybridUrlRequestSpec.hpp +66 -0
  143. package/nitrogen/generated/shared/c++/NitroFormDataPart.hpp +100 -0
  144. package/nitrogen/generated/shared/c++/NitroHeader.hpp +24 -8
  145. package/nitrogen/generated/shared/c++/NitroRequest.hpp +51 -24
  146. package/nitrogen/generated/shared/c++/NitroRequestMethod.hpp +1 -1
  147. package/nitrogen/generated/shared/c++/NitroResponse.hpp +42 -26
  148. package/nitrogen/generated/shared/c++/RequestException.hpp +83 -0
  149. package/nitrogen/generated/shared/c++/UrlResponseInfo.hpp +123 -0
  150. package/package.json +10 -10
  151. package/src/NitroCronet.nitro.ts +72 -0
  152. package/src/NitroFetch.nitro.ts +28 -6
  153. package/src/NitroInstances.ts +4 -0
  154. package/src/fetch.ts +214 -13
  155. package/src/index.tsx +1 -1
package/src/fetch.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import 'web-streams-polyfill/polyfill';
1
2
  import type {
2
3
  NitroFetch as NitroFetchModule,
4
+ NitroFormDataPart,
3
5
  NitroHeader,
4
6
  NitroRequest,
5
7
  NitroResponse,
@@ -7,6 +9,7 @@ import type {
7
9
  import {
8
10
  boxedNitroFetch,
9
11
  NitroFetch as NitroFetchSingleton,
12
+ NitroCronetSingleton,
10
13
  } from './NitroInstances';
11
14
  import { NativeStorage as NativeStorageSingleton } from './NitroInstances';
12
15
 
@@ -65,18 +68,73 @@ function headersToPairs(headers?: HeadersInit): NitroHeader[] | undefined {
65
68
  return pairs;
66
69
  }
67
70
 
68
- function normalizeBody(
69
- body: BodyInit | null | undefined
70
- ): { bodyString?: string; bodyBytes?: ArrayBuffer } | undefined {
71
+ function serializeFormData(fd: FormData): NitroFormDataPart[] {
72
+ const parts: NitroFormDataPart[] = [];
73
+
74
+ if (typeof (fd as any).getParts === 'function') {
75
+ const rnParts: any[] = (fd as any).getParts();
76
+ for (const part of rnParts) {
77
+ if (part.string !== undefined) {
78
+ parts.push({ name: part.fieldName, value: String(part.string) });
79
+ } else if (part.uri) {
80
+ parts.push({
81
+ name: part.fieldName,
82
+ fileUri: part.uri,
83
+ fileName: part.fileName ?? part.name ?? 'file',
84
+ mimeType: part.type ?? 'application/octet-stream',
85
+ });
86
+ }
87
+ }
88
+ return parts;
89
+ }
90
+
91
+ fd.forEach((value: any, key: string) => {
92
+ if (typeof value === 'string') {
93
+ parts.push({ name: key, value });
94
+ } else if (value && typeof value === 'object') {
95
+ parts.push({
96
+ name: key,
97
+ fileUri: value.uri ?? value.fileUri,
98
+ fileName: value.name ?? value.fileName ?? 'file',
99
+ mimeType: value.type ?? value.mimeType ?? 'application/octet-stream',
100
+ });
101
+ }
102
+ });
103
+ return parts;
104
+ }
105
+
106
+ function isFormData(body: unknown): body is FormData {
107
+ if (typeof FormData !== 'undefined' && body instanceof FormData) return true;
108
+ if (
109
+ body &&
110
+ typeof body === 'object' &&
111
+ typeof (body as any).append === 'function' &&
112
+ typeof (body as any).getParts === 'function'
113
+ ) {
114
+ return true;
115
+ }
116
+ return false;
117
+ }
118
+
119
+ function normalizeBody(body: BodyInit | null | undefined):
120
+ | {
121
+ bodyString?: string;
122
+ bodyBytes?: ArrayBuffer;
123
+ bodyFormData?: NitroFormDataPart[];
124
+ }
125
+ | undefined {
71
126
  'worklet';
72
127
  if (body == null) return undefined;
73
128
  if (typeof body === 'string') return { bodyString: body };
129
+
130
+ if (isFormData(body)) {
131
+ return { bodyFormData: serializeFormData(body as FormData) };
132
+ }
74
133
  if (body instanceof URLSearchParams) return { bodyString: body.toString() };
75
134
  if (typeof ArrayBuffer !== 'undefined' && body instanceof ArrayBuffer)
76
135
  return { bodyBytes: body };
77
136
  if (ArrayBuffer.isView(body)) {
78
137
  const view = body as ArrayBufferView;
79
- // Pass a copy/slice of the underlying bytes without base64
80
138
  return {
81
139
  //@ts-ignore
82
140
  bodyBytes: view.buffer.slice(
@@ -85,7 +143,6 @@ function normalizeBody(
85
143
  ),
86
144
  };
87
145
  }
88
- // TODO: Blob/FormData support can be added later
89
146
  throw new Error('Unsupported body type for nitro fetch');
90
147
  }
91
148
 
@@ -136,8 +193,8 @@ function buildNitroRequest(
136
193
  method: (method?.toUpperCase() as any) ?? 'GET',
137
194
  headers,
138
195
  bodyString: normalized?.bodyString,
139
- // Only include bodyBytes when provided to avoid signaling upload data unintentionally
140
196
  bodyBytes: undefined as any,
197
+ bodyFormData: normalized?.bodyFormData,
141
198
  followRedirects: true,
142
199
  };
143
200
  }
@@ -224,7 +281,6 @@ function normalizeBodyPure(
224
281
 
225
282
  if (ArrayBuffer.isView(body)) {
226
283
  const view = body as ArrayBufferView;
227
- // Pass a copy/slice of the underlying bytes without base64
228
284
  return {
229
285
  //@ts-ignore
230
286
  bodyBytes: view.buffer.slice(
@@ -233,8 +289,9 @@ function normalizeBodyPure(
233
289
  ),
234
290
  };
235
291
  }
236
- // TODO: Blob/FormData support can be added later
237
- throw new Error('Unsupported body type for nitro fetch');
292
+ throw new Error(
293
+ 'Unsupported body type for nitro fetch worklet (FormData is not available in worklets)'
294
+ );
238
295
  }
239
296
  // Pure JS version of buildNitroRequest that doesnt use anything that breaks worklets
240
297
  export function buildNitroRequestPure(
@@ -282,10 +339,23 @@ export function buildNitroRequestPure(
282
339
  };
283
340
  }
284
341
 
342
+ function createAbortError(): Error {
343
+ const err = new Error('The operation was aborted.');
344
+ err.name = 'AbortError';
345
+ return err;
346
+ }
347
+
285
348
  async function nitroFetchRaw(
286
349
  input: RequestInfo | URL,
287
350
  init?: RequestInit
288
351
  ): Promise<NitroResponse> {
352
+ const signal = init?.signal as AbortSignal | undefined | null;
353
+
354
+ // Fast-abort: reject synchronously before any bridge work.
355
+ if (signal?.aborted) {
356
+ throw createAbortError();
357
+ }
358
+
289
359
  const hasNative =
290
360
  typeof (NitroFetchHybrid as any)?.createClient === 'function';
291
361
  if (!hasNative) {
@@ -309,11 +379,46 @@ async function nitroFetchRaw(
309
379
  }
310
380
 
311
381
  const req = buildNitroRequest(input, init);
382
+
383
+ // Only allocate a requestId when a signal is present — zero overhead otherwise.
384
+ const requestId = signal ? String(Math.random()) : undefined;
385
+ if (requestId) req.requestId = requestId;
386
+
312
387
  ensureClient();
313
388
  if (!client || typeof (client as any).request !== 'function')
314
389
  throw new Error('NitroFetch client not available');
315
- const res: NitroResponse = await client.request(req);
316
- return res;
390
+
391
+ // Wire up the abort listener with { once: true } so it auto-removes
392
+ // after firing, avoiding a dangling reference to the client closure.
393
+ let abortListener: (() => void) | undefined;
394
+ if (signal && requestId) {
395
+ abortListener = () => {
396
+ try {
397
+ client!.cancelRequest(requestId);
398
+ } catch {
399
+ // Client may already be torn down — swallow.
400
+ }
401
+ };
402
+ signal.addEventListener('abort', abortListener, { once: true });
403
+ }
404
+
405
+ try {
406
+ const res: NitroResponse = await client.request(req);
407
+ return res;
408
+ } catch (e) {
409
+ // If the signal was aborted (either before or during the request),
410
+ // surface a spec-compliant AbortError regardless of what native threw.
411
+ if (signal?.aborted) {
412
+ throw createAbortError();
413
+ }
414
+ throw e;
415
+ } finally {
416
+ // Idempotent cleanup — removeEventListener is a no-op if the listener
417
+ // already fired (thanks to { once: true }) or was never added.
418
+ if (signal && abortListener) {
419
+ signal.removeEventListener('abort', abortListener);
420
+ }
421
+ }
317
422
  }
318
423
 
319
424
  // Simple Headers-like class that supports get() method
@@ -353,10 +458,102 @@ class NitroHeaders {
353
458
  }
354
459
  }
355
460
 
356
- export async function nitroFetch(
461
+ async function nitroStreamFetch(
357
462
  input: RequestInfo | URL,
358
463
  init?: RequestInit
359
464
  ): Promise<Response> {
465
+ const url = typeof input === 'string' ? input : String(input);
466
+ const method = init?.method?.toUpperCase() ?? 'GET';
467
+ const headers = headersToPairs(init?.headers);
468
+
469
+ const builder = NitroCronetSingleton.newUrlRequestBuilder(url);
470
+ builder.setHttpMethod(method);
471
+ headers?.forEach((h) => builder.addHeader(h.key, h.value));
472
+
473
+ const body = init?.body;
474
+ if (body != null) {
475
+ if (typeof body === 'string') builder.setUploadBody(body);
476
+ else if (body instanceof ArrayBuffer) builder.setUploadBody(body);
477
+ }
478
+
479
+ return new Promise((resolveResponse, rejectResponse) => {
480
+ let streamController: ReadableStreamDefaultController<Uint8Array>;
481
+
482
+ const stream = new ReadableStream<Uint8Array>({
483
+ start(controller) {
484
+ streamController = controller;
485
+ },
486
+ });
487
+
488
+ let responseResolved = false;
489
+
490
+ builder.onResponseStarted((info) => {
491
+ if (responseResolved) return;
492
+ responseResolved = true;
493
+ const status = info.httpStatusCode;
494
+ const responseHeaders = new NitroHeaders(
495
+ Object.entries(info.allHeaders).map(([key, value]) => ({ key, value }))
496
+ );
497
+ // Use a plain object — React Native's Response constructor does not
498
+ // propagate a ReadableStream to .body, so we set it explicitly.
499
+ const response: any = {
500
+ url: info.url,
501
+ ok: status >= 200 && status < 300,
502
+ status,
503
+ statusText: info.httpStatusText,
504
+ headers: responseHeaders,
505
+ redirected: false,
506
+ body: stream,
507
+ bodyUsed: false,
508
+ };
509
+ resolveResponse(response as Response);
510
+ // Android/Cronet: kick off the first buffer read.
511
+ // iOS/URLSession handles reading automatically so this is a no-op there.
512
+ request.read();
513
+ });
514
+
515
+ builder.onReadCompleted((_info, byteBuffer, bytesRead) => {
516
+ const chunk = new Uint8Array(byteBuffer, 0, bytesRead).slice();
517
+ streamController.enqueue(chunk);
518
+ if (!request.isDone()) {
519
+ request.read();
520
+ }
521
+ });
522
+
523
+ builder.onSucceeded(() => streamController.close());
524
+
525
+ builder.onFailed((_info, error) => {
526
+ const err = new Error(error.message);
527
+ if (!responseResolved) {
528
+ responseResolved = true;
529
+ rejectResponse(err);
530
+ } else {
531
+ streamController.error(err);
532
+ }
533
+ });
534
+
535
+ builder.onCanceled(() => {
536
+ const err = createAbortError();
537
+ if (!responseResolved) {
538
+ responseResolved = true;
539
+ rejectResponse(err);
540
+ } else {
541
+ streamController.error(err);
542
+ }
543
+ });
544
+
545
+ const request = builder.build();
546
+ request.start();
547
+ });
548
+ }
549
+
550
+ export async function nitroFetch(
551
+ input: RequestInfo | URL,
552
+ init?: RequestInit & { stream?: boolean }
553
+ ): Promise<Response> {
554
+ if ((init as any)?.stream === true) {
555
+ return nitroStreamFetch(input, init);
556
+ }
360
557
  const res = await nitroFetchRaw(input, init);
361
558
 
362
559
  const headersObj = new NitroHeaders(res.headers);
@@ -598,4 +795,8 @@ export async function nitroFetchOnWorklet<T>(
598
795
  export const x = ensureWorkletRuntime();
599
796
  export const y = getWorklets();
600
797
 
601
- export type { NitroRequest, NitroResponse } from './NitroFetch.nitro';
798
+ export type {
799
+ NitroFormDataPart,
800
+ NitroRequest,
801
+ NitroResponse,
802
+ } from './NitroFetch.nitro';
package/src/index.tsx CHANGED
@@ -6,7 +6,7 @@ export {
6
6
  removeFromAutoPrefetch,
7
7
  removeAllFromAutoprefetch,
8
8
  } from './fetch';
9
- export type { NitroRequest, NitroResponse } from './fetch';
9
+ export type { NitroFormDataPart, NitroRequest, NitroResponse } from './fetch';
10
10
  export { NitroFetch } from './NitroInstances';
11
11
  import './fetch';
12
12