import { ApolloLink , Observable , Operation , NextLink } from 'apollo-link' ; import { DefinitionNode , OperationDefinitionNode } from 'graphql' ; const DEFAULT_TIMEOUT = 30000 ; const REQUEST_TIMEOUT_ERROR_CODE = 408 ; /**
 * Aborts the request if the timeout expires before the response is received.
 */ export type TimeoutError = Error & { statusCode : number ; } ; export const throwTimeoutError = ( message ) => { const error = new Error ( message ) as TimeoutError ; error . name = 'RequestTimeoutError' ; error . statusCode = REQUEST_TIMEOUT_ERROR_CODE ; return error ; } ; export default class TimeoutLink extends ApolloLink { private timeout : number ; constructor ( timeout = DEFAULT_TIMEOUT ) { super ( ) ; this . timeout = timeout ; } public request ( operation , forward ? ) { let controller ; let ctxTimeout ; ctxTimeout = operation . getContext ( ) . timeout || null ; if ( ( ctxTimeout as number ) <= 0 ) { ctxTimeout = null ; } if ( typeof AbortController !== 'undefined' ) { const context = operation . getContext ( ) ; let fetchOptions = context . fetchOptions || { } ; controller = fetchOptions . controller || new AbortController ( ) ; fetchOptions = { ... fetchOptions , controller , signal : controller . signal } ; operation . setContext ( { fetchOptions } ) ; } if ( forward ) { const chainObservable = forward ( operation ) ; const operationType = ( ( operation . query . definitions as ReadonlyArray < DefinitionNode > ) . find ( ( def ) => def . kind === 'OperationDefinition' ) as OperationDefinitionNode ) . operation ; if ( this . timeout <= 0 || operationType === 'subscription' ) { return chainObservable ; } const localObservable = new Observable ( ( observer ) => { let timer ; const subscription = chainObservable . subscribe ( ( result ) => { clearTimeout ( timer ) ; observer . next ( result ) ; observer . complete ( ) ; } , ( error ) => { clearTimeout ( timer ) ; observer . error ( error ) ; observer . complete ( ) ; } ) ; timer = setTimeout ( ( ) => { if ( controller ) { controller . abort ( ) ; } observer . error ( throwTimeoutError ( `Timeout exceeded ` + `for ${ operationType } "${ operation . operationName }"` ) ) ; subscription . unsubscribe ( ) ; } , ctxTimeout || this . timeout ) ; return ( ) => { clearTimeout ( timer ) ; subscription . unsubscribe ( ) ; } ; } ) ; return localObservable ; } else { return null ; } } }