18d307f52SEvan Baconimport { 28d307f52SEvan Bacon cacheExchange, 38d307f52SEvan Bacon Client, 48d307f52SEvan Bacon CombinedError as GraphqlError, 58d307f52SEvan Bacon createClient as createUrqlClient, 68d307f52SEvan Bacon dedupExchange, 78d307f52SEvan Bacon fetchExchange, 88d307f52SEvan Bacon OperationContext, 98d307f52SEvan Bacon OperationResult, 108d307f52SEvan Bacon PromisifiedSource, 118d307f52SEvan Bacon TypedDocumentNode, 128d307f52SEvan Bacon} from '@urql/core'; 138d307f52SEvan Baconimport { retryExchange } from '@urql/exchange-retry'; 148d307f52SEvan Baconimport { DocumentNode } from 'graphql'; 158d307f52SEvan Baconimport fetch from 'node-fetch'; 168d307f52SEvan Bacon 178d307f52SEvan Baconimport * as Log from '../../log'; 188d307f52SEvan Baconimport { getExpoApiBaseUrl } from '../endpoint'; 19*8c8eefe0SEvan Baconimport { wrapFetchWithOffline } from '../rest/wrapFetchWithOffline'; 20*8c8eefe0SEvan Baconimport { wrapFetchWithProxy } from '../rest/wrapFetchWithProxy'; 218d307f52SEvan Baconimport UserSettings from '../user/UserSettings'; 228d307f52SEvan Bacon 238d307f52SEvan Bacontype AccessTokenHeaders = { 248d307f52SEvan Bacon authorization: string; 258d307f52SEvan Bacon}; 268d307f52SEvan Bacon 278d307f52SEvan Bacontype SessionHeaders = { 288d307f52SEvan Bacon 'expo-session': string; 298d307f52SEvan Bacon}; 308d307f52SEvan Bacon 318d307f52SEvan Baconexport const graphqlClient = createUrqlClient({ 328d307f52SEvan Bacon url: getExpoApiBaseUrl() + '/graphql', 338d307f52SEvan Bacon exchanges: [ 348d307f52SEvan Bacon dedupExchange, 358d307f52SEvan Bacon cacheExchange, 368d307f52SEvan Bacon retryExchange({ 378d307f52SEvan Bacon maxDelayMs: 4000, 388d307f52SEvan Bacon retryIf: (err) => 398d307f52SEvan Bacon !!(err && (err.networkError || err.graphQLErrors.some((e) => e?.extensions?.isTransient))), 408d307f52SEvan Bacon }), 418d307f52SEvan Bacon fetchExchange, 428d307f52SEvan Bacon ], 4329975bfdSEvan Bacon // @ts-ignore Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'. 44*8c8eefe0SEvan Bacon fetch: wrapFetchWithOffline(wrapFetchWithProxy(fetch)), 458d307f52SEvan Bacon fetchOptions: (): { headers?: AccessTokenHeaders | SessionHeaders } => { 468d307f52SEvan Bacon const token = UserSettings.getAccessToken(); 478d307f52SEvan Bacon if (token) { 488d307f52SEvan Bacon return { 498d307f52SEvan Bacon headers: { 508d307f52SEvan Bacon authorization: `Bearer ${token}`, 518d307f52SEvan Bacon }, 528d307f52SEvan Bacon }; 538d307f52SEvan Bacon } 548d307f52SEvan Bacon const sessionSecret = UserSettings.getSession()?.sessionSecret; 558d307f52SEvan Bacon if (sessionSecret) { 568d307f52SEvan Bacon return { 578d307f52SEvan Bacon headers: { 588d307f52SEvan Bacon 'expo-session': sessionSecret, 598d307f52SEvan Bacon }, 608d307f52SEvan Bacon }; 618d307f52SEvan Bacon } 628d307f52SEvan Bacon return {}; 638d307f52SEvan Bacon }, 648d307f52SEvan Bacon}) as StricterClient; 658d307f52SEvan Bacon 668d307f52SEvan Bacon/* Please specify additionalTypenames in your Graphql queries */ 678d307f52SEvan Baconexport interface StricterClient extends Client { 688d307f52SEvan Bacon // eslint-disable-next-line @typescript-eslint/ban-types 698d307f52SEvan Bacon query<Data = any, Variables extends object = {}>( 708d307f52SEvan Bacon query: DocumentNode | TypedDocumentNode<Data, Variables> | string, 718d307f52SEvan Bacon variables: Variables | undefined, 728d307f52SEvan Bacon context: Partial<OperationContext> & { additionalTypenames: string[] } 738d307f52SEvan Bacon ): PromisifiedSource<OperationResult<Data, Variables>>; 748d307f52SEvan Bacon} 758d307f52SEvan Bacon 768d307f52SEvan Baconexport async function withErrorHandlingAsync<T>(promise: Promise<OperationResult<T>>): Promise<T> { 778d307f52SEvan Bacon const { data, error } = await promise; 788d307f52SEvan Bacon 798d307f52SEvan Bacon if (error) { 808d307f52SEvan Bacon if (error.graphQLErrors.some((e) => e?.extensions?.isTransient)) { 818d307f52SEvan Bacon Log.error(`We've encountered a transient error, please try again shortly.`); 828d307f52SEvan Bacon } 838d307f52SEvan Bacon throw error; 848d307f52SEvan Bacon } 858d307f52SEvan Bacon 868d307f52SEvan Bacon // Check for a malformed response. This only checks the root query's existence. It doesn't affect 878d307f52SEvan Bacon // returning responses with an empty result set. 888d307f52SEvan Bacon if (!data) { 898d307f52SEvan Bacon throw new Error('Returned query result data is null!'); 908d307f52SEvan Bacon } 918d307f52SEvan Bacon 928d307f52SEvan Bacon return data; 938d307f52SEvan Bacon} 948d307f52SEvan Bacon 958d307f52SEvan Baconexport { GraphqlError }; 96