UNPKG

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