UNPKG

5.92 kBPlain TextView Raw
1/* necessary for backward compat */
2export * from 'apollo-client';
3export * from 'apollo-link';
4export * from 'apollo-cache-inmemory';
5
6import { Operation, ApolloLink, Observable } from 'apollo-link';
7import { HttpLink, UriFunction } from 'apollo-link-http';
8import { onError, ErrorLink } from 'apollo-link-error';
9import { ApolloCache } from 'apollo-cache';
10import { InMemoryCache, CacheResolverMap } from 'apollo-cache-inmemory';
11import gql from 'graphql-tag';
12import ApolloClient, {
13 Resolvers,
14 LocalStateFragmentMatcher,
15} from 'apollo-client';
16import { DocumentNode } from 'graphql';
17import { invariant } from 'ts-invariant';
18
19export { gql, HttpLink };
20
21type ClientStateConfig = {
22 cache?: ApolloCache<any>;
23 defaults?: Record<string, any>;
24 resolvers?: Resolvers | Resolvers[];
25 typeDefs?: string | string[] | DocumentNode | DocumentNode[];
26 fragmentMatcher?: LocalStateFragmentMatcher;
27};
28
29export interface PresetConfig {
30 request?: (operation: Operation) => Promise<void> | void;
31 uri?: string | UriFunction;
32 credentials?: string;
33 headers?: any;
34 fetch?: WindowOrWorkerGlobalScope['fetch'];
35 fetchOptions?: HttpLink.Options;
36 clientState?: ClientStateConfig;
37 onError?: ErrorLink.ErrorHandler;
38 cacheRedirects?: CacheResolverMap;
39 cache?: ApolloCache<any>;
40 name?: string;
41 version?: string;
42 resolvers?: Resolvers | Resolvers[];
43 typeDefs?: string | string[] | DocumentNode | DocumentNode[];
44 fragmentMatcher?: LocalStateFragmentMatcher;
45 assumeImmutableResults?: boolean;
46}
47
48// Yes, these are the exact same as the `PresetConfig` interface. We're
49// defining these again so they can be used to verify that valid config
50// options are being used in the `DefaultClient` constructor, for clients
51// that aren't using Typescript. This duplication is unfortunate, and at
52// some point can likely be adjusted so these items are inferred from
53// the `PresetConfig` interface using a Typescript transform at compilation
54// time. Unfortunately, TS transforms with rollup don't appear to be quite
55// working properly, so this will have to be re-visited at some point.
56// For now, when updating the properties of the `PresetConfig` interface,
57// please also update this constant.
58const PRESET_CONFIG_KEYS = [
59 'request',
60 'uri',
61 'credentials',
62 'headers',
63 'fetch',
64 'fetchOptions',
65 'clientState',
66 'onError',
67 'cacheRedirects',
68 'cache',
69 'name',
70 'version',
71 'resolvers',
72 'typeDefs',
73 'fragmentMatcher',
74];
75
76export default class DefaultClient<TCache> extends ApolloClient<TCache> {
77 constructor(config: PresetConfig = {}) {
78 if (config) {
79 const diff = Object.keys(config).filter(
80 key => PRESET_CONFIG_KEYS.indexOf(key) === -1,
81 );
82
83 if (diff.length > 0) {
84 invariant.warn(
85 'ApolloBoost was initialized with unsupported options: ' +
86 `${diff.join(' ')}`,
87 );
88 }
89 }
90
91 const {
92 request,
93 uri,
94 credentials,
95 headers,
96 fetch,
97 fetchOptions,
98 clientState,
99 cacheRedirects,
100 onError: errorCallback,
101 name,
102 version,
103 resolvers,
104 typeDefs,
105 fragmentMatcher,
106 } = config;
107
108 let { cache } = config;
109
110 invariant(
111 !cache || !cacheRedirects,
112 'Incompatible cache configuration. When not providing `cache`, ' +
113 'configure the provided instance with `cacheRedirects` instead.',
114 );
115
116 if (!cache) {
117 cache = cacheRedirects
118 ? new InMemoryCache({ cacheRedirects })
119 : new InMemoryCache();
120 }
121
122 const errorLink = errorCallback
123 ? onError(errorCallback)
124 : onError(({ graphQLErrors, networkError }) => {
125 if (graphQLErrors) {
126 graphQLErrors.forEach(({ message, locations, path }) =>
127 // tslint:disable-next-line
128 invariant.warn(
129 `[GraphQL error]: Message: ${message}, Location: ` +
130 `${locations}, Path: ${path}`,
131 ),
132 );
133 }
134 if (networkError) {
135 // tslint:disable-next-line
136 invariant.warn(`[Network error]: ${networkError}`);
137 }
138 });
139
140 const requestHandler = request
141 ? new ApolloLink(
142 (operation, forward) =>
143 new Observable(observer => {
144 let handle: any;
145 Promise.resolve(operation)
146 .then(oper => request(oper))
147 .then(() => {
148 handle = forward(operation).subscribe({
149 next: observer.next.bind(observer),
150 error: observer.error.bind(observer),
151 complete: observer.complete.bind(observer),
152 });
153 })
154 .catch(observer.error.bind(observer));
155
156 return () => {
157 if (handle) {
158 handle.unsubscribe();
159 }
160 };
161 }),
162 )
163 : false;
164
165 const httpLink = new HttpLink({
166 uri: uri || '/graphql',
167 fetch,
168 fetchOptions: fetchOptions || {},
169 credentials: credentials || 'same-origin',
170 headers: headers || {},
171 });
172
173 const link = ApolloLink.from([errorLink, requestHandler, httpLink].filter(
174 x => !!x,
175 ) as ApolloLink[]);
176
177 let activeResolvers = resolvers;
178 let activeTypeDefs = typeDefs;
179 let activeFragmentMatcher = fragmentMatcher;
180 if (clientState) {
181 if (clientState.defaults) {
182 cache.writeData({
183 data: clientState.defaults,
184 });
185 }
186 activeResolvers = clientState.resolvers;
187 activeTypeDefs = clientState.typeDefs;
188 activeFragmentMatcher = clientState.fragmentMatcher;
189 }
190
191 // super hacky, we will fix the types eventually
192 super({
193 cache,
194 link,
195 name,
196 version,
197 resolvers: activeResolvers,
198 typeDefs: activeTypeDefs,
199 fragmentMatcher: activeFragmentMatcher,
200 } as any);
201 }
202}