1- import { type Directory , File } from 'expo-file-system'
1+ import { type Directory , File , Paths } from 'expo-file-system'
22import type { EmptyRequestBody } from '../../types/EmptyRequestBody'
33import type { FileRequestBody } from '../../types/FileRequestBody'
44import type { Json } from '../../types/Json'
55import type { JsonRequestBody } from '../../types/JsonRequestBody'
66import type { QueryParameter } from '../../types/QueryParameter'
77import type { QueryParameters } from '../../types/QueryParameters'
88import type { RequestInterface } from '../../types/RequestInterface'
9+ import type { UuidGeneratorInterface } from '../../types/UuidGeneratorInterface'
910
1011/**
1112 * Allows HTTP/S requests to be made for JSON and files relative to a base URL.
@@ -23,12 +24,15 @@ export class Request implements RequestInterface {
2324 * otherwise, the returned string (e.g.
2425 * "BEARER 1234") is taken as the
2526 * Authorization header.
27+ * @param fetch Expo's implementation of fetch().
28+ * @param uuidGenerator A generator of UUIDs.
2629 */
2730 constructor (
2831 baseUrl : string ,
2932 private readonly timeoutMilliseconds : number ,
3033 private readonly authorizationHeaderFactory : ( ) => null | string ,
31- private readonly fetch : GlobalFetch [ 'fetch' ]
34+ private readonly fetch : GlobalFetch [ 'fetch' ] ,
35+ private readonly uuidGenerator : UuidGeneratorInterface
3236 ) {
3337 if ( ! / ^ [ a - z ] + : \/ \/ / . test ( baseUrl ) ) {
3438 baseUrl = `https://${ baseUrl } `
@@ -151,14 +155,24 @@ export class Request implements RequestInterface {
151155 case 'empty' :
152156 return null
153157
154- case 'file' :
155- return new File ( ...requestBody . fileUri )
158+ case 'file' : {
159+ const original = new File ( ...requestBody . fileUri )
160+ const temporaryCopy = new File ( Paths . cache , `${ this . uuidGenerator . generate ( ) } .bin` )
161+ original . copy ( temporaryCopy )
162+ return temporaryCopy
163+ }
156164
157165 case 'json' :
158166 return JSON . stringify ( requestBody . value )
159167 }
160168 }
161169
170+ private cleanUpBodyBody ( body : null | BodyInit ) : void {
171+ if ( body instanceof File ) {
172+ body . delete ( )
173+ }
174+ }
175+
162176 async withoutResponse < T extends string > (
163177 method : string ,
164178 route : string ,
@@ -169,17 +183,25 @@ export class Request implements RequestInterface {
169183 ) : Promise < T > {
170184 return await this . withTimeout ( abortSignal , async ( signal ) => {
171185 const url = this . constructUrl ( route , queryParameters )
172-
173- const response = await this . fetch ( url , {
174- signal,
175- method,
176- headers : {
177- ...this . commonHeaders ( ) ,
178- ...this . requestBodyHeaders ( requestBody ) ,
179- Accept : 'application/json' // If we do not do this, Laravel will redirect to / in the event of an error, hiding the returned validation error.
180- } ,
181- body : this . requestBodyBody ( requestBody )
182- } )
186+ let body : null | BodyInit = null
187+ let response : Response
188+
189+ try {
190+ body = this . requestBodyBody ( requestBody )
191+
192+ response = await this . fetch ( url , {
193+ signal,
194+ method,
195+ headers : {
196+ ...this . commonHeaders ( ) ,
197+ ...this . requestBodyHeaders ( requestBody ) ,
198+ Accept : 'application/json' // If we do not do this, Laravel will redirect to / in the event of an error, hiding the returned validation error.
199+ } ,
200+ body
201+ } )
202+ } finally {
203+ this . cleanUpBodyBody ( body )
204+ }
183205
184206 this . checkStatusCode ( method , url , response . status , expectedStatusCodes )
185207
@@ -206,17 +228,25 @@ export class Request implements RequestInterface {
206228 > {
207229 return await this . withTimeout ( abortSignal , async ( signal ) => {
208230 const url = this . constructUrl ( route , queryParameters )
209-
210- const response = await this . fetch ( url , {
211- signal,
212- method,
213- headers : {
214- ...this . commonHeaders ( ) ,
215- ...this . requestBodyHeaders ( requestBody ) ,
216- Accept : 'application/json'
217- } ,
218- body : this . requestBodyBody ( requestBody )
219- } )
231+ let body : null | BodyInit = null
232+ let response : Response
233+
234+ try {
235+ body = this . requestBodyBody ( requestBody )
236+
237+ response = await this . fetch ( url , {
238+ signal,
239+ method,
240+ headers : {
241+ ...this . commonHeaders ( ) ,
242+ ...this . requestBodyHeaders ( requestBody ) ,
243+ Accept : 'application/json'
244+ } ,
245+ body
246+ } )
247+ } finally {
248+ this . cleanUpBodyBody ( body )
249+ }
220250
221251 this . checkStatusCode ( method , url , response . status , expectedStatusCodes as readonly string [ ] )
222252
0 commit comments