import { ApolloProvider } from "@vue/apollo-option" ; import { createApolloClient , CreateClientOptions , } from "vue-cli-plugin-apollo/graphql-client" ; import { Store } from "vuex" ; import { defaultDataIdFromObject , IntrospectionFragmentMatcher , } from "apollo-cache-inmemory" ; import Bottleneck from "bottleneck" ; import { captureException } from "@sentry/browser" ; import introspectionResult from "@/graphql/generated/introspection-result" ; import { createResolvers } from "@/graphql/resolvers" ; import { EpisodeListEpisodes , ListEntry } from "@/graphql/generated/types" ; import { userStore } from "@/lib/user" ; import { getEpisodeCacheKey , isNil , isOfTypename } from "@/utils" ; import { getAnilistRequestsUntilLimiting , setAnilistRequests } from "@/state/app" ; const httpEndpoint = "https://graphql.anilist.co" ; const dataIdFromObject = ( obj ) => { if ( isOfTypename < EpisodeListEpisodes > ( obj , "Episode" ) ) { return getEpisodeCacheKey ( obj ) ; } if ( isOfTypename < ListEntry > ( obj , "ListEntry" ) ) { return `ListEntry:${ obj . mediaId }` ; } if ( obj . __typename ) { if ( obj . id !== undefined ) { return `${ obj . __typename }:${ obj . id }` ; } if ( obj . _id !== undefined ) { return `${ obj . __typename }:${ obj . _id }` ; } } return defaultDataIdFromObject ( obj ) ; } ; const fragmentMatcher = new IntrospectionFragmentMatcher ( { introspectionQueryResultData : introspectionResult , } ) ; const limiter = new Bottleneck ( { reservoir : 85 , reservoirRefreshAmount : 85 , reservoirRefreshInterval : 60 * 1000 , } ) ; const limitedFetch = limiter . wrap ( window . fetch ) ; const createOptions = ( store ) : CreateClientOptions => ( { httpEndpoint , persisting : false , ssr : false , websocketsOnly : false , wsEndpoint : null , getAuth : ( ) => userStore . get ( "anilist.token" ) , httpLinkOptions : { fetch : limitedFetch , } , inMemoryCacheOptions : { dataIdFromObject , fragmentMatcher , } , resolvers : createResolvers ( store ) , } ) ; export const createProvider = ( store ) => { const { apolloClient , wsClient } = createApolloClient ( createOptions ( store ) ) ; apolloClient . wsClient = wsClient ; setInterval ( async ( ) => { const requests = ( await limiter . currentReservoir ( ) ) || 60 ; const savedRequests = getAnilistRequestsUntilLimiting ( store ) ; if ( requests === savedRequests ) return ; setAnilistRequests ( store , requests ) ; } , 1000 ) ; const apolloProvider = new ApolloProvider ( { defaultClient : apolloClient , defaultOptions : { $query : { fetchPolicy : "cache-first" , } , } , async errorHandler ( { networkError } ) { if ( ! isNil ( networkError ) && ( networkError as any ) ?. statusCode === 429 ) { const currentReservoir = ( await limiter . currentReservoir ( ) ) || 60 ; await limiter . incrementReservoir ( - currentReservoir ) ; setAnilistRequests ( store , ( await limiter . currentReservoir ( ) ) || 0 ) ; return ; } if ( process . env . NODE_ENV === "production" ) { if ( isNil ( networkError ) ) return ; return captureException ( networkError ) ; } console . error ( networkError ) ; } , } ) ; return apolloProvider ; } ;