1import { Platform } from 'expo-modules-core'; 2import qs from 'qs'; 3 4export type Headers = Record<string, string> & { 5 'Content-Type': string; 6 Authorization?: string; 7 Accept?: string; 8}; 9 10export type FetchRequest = { 11 headers?: Headers; 12 body?: Record<string, string>; 13 dataType?: string; 14 method?: string; 15}; 16 17// TODO(Bacon): pending react-native-adapter publish after sdk 38 18const isDOMAvailable = 19 Platform.OS === 'web' && 20 typeof window !== 'undefined' && 21 !!window.document?.createElement && 22 typeof URL !== 'undefined'; 23 24export async function requestAsync<T>(requestUrl: string, fetchRequest: FetchRequest): Promise<T> { 25 if (Platform.OS === 'web' && !isDOMAvailable) { 26 // @ts-ignore 27 return; 28 } 29 const url = new URL(requestUrl); 30 31 const request: Omit<RequestInit, 'headers'> & { headers: HeadersInit } = { 32 method: fetchRequest.method, 33 mode: 'cors', 34 headers: {}, 35 }; 36 37 const isJsonDataType = fetchRequest.dataType?.toLowerCase() === 'json'; 38 39 if (fetchRequest.headers) { 40 for (const i in fetchRequest.headers) { 41 if (i in fetchRequest.headers) { 42 request.headers[i] = fetchRequest.headers[i] as string; 43 } 44 } 45 } 46 47 if (fetchRequest.body) { 48 if (fetchRequest.method?.toUpperCase() === 'POST') { 49 request.body = qs.stringify(fetchRequest.body); 50 } else { 51 for (const key of Object.keys(fetchRequest.body)) { 52 url.searchParams.append(key, fetchRequest.body[key]); 53 } 54 } 55 } 56 57 if (isJsonDataType && !('Accept' in request.headers)) { 58 // NOTE: Github authentication will return XML if this includes the standard `*/*` 59 request.headers['Accept'] = 'application/json, text/javascript; q=0.01'; 60 } 61 62 // Fix a problem with React Native `URL` causing a trailing slash to be added. 63 const correctedUrl = url.toString().replace(/\/$/, ''); 64 65 const response = await fetch(correctedUrl, request); 66 67 const contentType = response.headers.get('content-type'); 68 if (isJsonDataType || contentType?.includes('application/json')) { 69 return response.json(); 70 } 71 // @ts-ignore: Type 'string' is not assignable to type 'T'. 72 return response.text(); 73} 74