@aws-amplify/api-graphql 3.3.2-unstable.f47d472.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/internals/package.json +8 -0
- package/lib/GraphQLAPI.d.ts +5 -61
- package/lib/GraphQLAPI.js +9 -370
- package/lib/GraphQLAPI.js.map +1 -1
- package/lib/internals/InternalGraphQLAPI.d.ts +81 -0
- package/lib/internals/InternalGraphQLAPI.js +400 -0
- package/lib/internals/InternalGraphQLAPI.js.map +1 -0
- package/lib/internals/index.d.ts +1 -0
- package/lib/internals/index.js +8 -0
- package/lib/internals/index.js.map +1 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js.map +1 -1
- package/lib-esm/GraphQLAPI.d.ts +5 -61
- package/lib-esm/GraphQLAPI.js +11 -372
- package/lib-esm/GraphQLAPI.js.map +1 -1
- package/lib-esm/internals/InternalGraphQLAPI.d.ts +81 -0
- package/lib-esm/internals/InternalGraphQLAPI.js +398 -0
- package/lib-esm/internals/InternalGraphQLAPI.js.map +1 -0
- package/lib-esm/internals/index.d.ts +1 -0
- package/lib-esm/internals/index.js +4 -0
- package/lib-esm/internals/index.js.map +1 -0
- package/lib-esm/types/index.d.ts +3 -0
- package/lib-esm/types/index.js.map +1 -1
- package/package.json +10 -9
- package/src/GraphQLAPI.ts +7 -411
- package/src/internals/InternalGraphQLAPI.ts +449 -0
- package/src/internals/index.ts +6 -0
- package/src/types/index.ts +6 -2
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import {
|
|
4
|
+
DocumentNode,
|
|
5
|
+
OperationDefinitionNode,
|
|
6
|
+
print,
|
|
7
|
+
parse,
|
|
8
|
+
GraphQLError,
|
|
9
|
+
OperationTypeNode,
|
|
10
|
+
} from 'graphql';
|
|
11
|
+
import Observable from 'zen-observable-ts';
|
|
12
|
+
import {
|
|
13
|
+
Amplify,
|
|
14
|
+
ConsoleLogger as Logger,
|
|
15
|
+
Credentials,
|
|
16
|
+
CustomUserAgentDetails,
|
|
17
|
+
getAmplifyUserAgentString,
|
|
18
|
+
INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER,
|
|
19
|
+
} from '@aws-amplify/core';
|
|
20
|
+
import { InternalPubSub } from '@aws-amplify/pubsub/internals';
|
|
21
|
+
import { Auth } from '@aws-amplify/auth';
|
|
22
|
+
import { Cache } from '@aws-amplify/cache';
|
|
23
|
+
import {
|
|
24
|
+
GraphQLAuthError,
|
|
25
|
+
GraphQLOptions,
|
|
26
|
+
GraphQLResult,
|
|
27
|
+
GraphQLOperation,
|
|
28
|
+
} from '../types';
|
|
29
|
+
import { RestClient } from '@aws-amplify/api-rest';
|
|
30
|
+
const USER_AGENT_HEADER = 'x-amz-user-agent';
|
|
31
|
+
|
|
32
|
+
const logger = new Logger('GraphQLAPI');
|
|
33
|
+
|
|
34
|
+
export const graphqlOperation = (
|
|
35
|
+
query,
|
|
36
|
+
variables = {},
|
|
37
|
+
authToken?: string
|
|
38
|
+
) => ({
|
|
39
|
+
query,
|
|
40
|
+
variables,
|
|
41
|
+
authToken,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Export Cloud Logic APIs
|
|
46
|
+
*/
|
|
47
|
+
export class InternalGraphQLAPIClass {
|
|
48
|
+
/**
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
private _options;
|
|
52
|
+
private _api = null;
|
|
53
|
+
|
|
54
|
+
Auth = Auth;
|
|
55
|
+
Cache = Cache;
|
|
56
|
+
Credentials = Credentials;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialize GraphQL API with AWS configuration
|
|
60
|
+
* @param {Object} options - Configuration object for API
|
|
61
|
+
*/
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this._options = options;
|
|
64
|
+
logger.debug('API Options', this._options);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public getModuleName() {
|
|
68
|
+
return 'InternalGraphQLAPI';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Configure API
|
|
73
|
+
* @param {Object} config - Configuration of the API
|
|
74
|
+
* @return {Object} - The current configuration
|
|
75
|
+
*/
|
|
76
|
+
configure(options) {
|
|
77
|
+
const { API = {}, ...otherOptions } = options || {};
|
|
78
|
+
let opt = { ...otherOptions, ...API };
|
|
79
|
+
logger.debug('configure GraphQL API', { opt });
|
|
80
|
+
|
|
81
|
+
if (opt['aws_project_region']) {
|
|
82
|
+
opt = Object.assign({}, opt, {
|
|
83
|
+
region: opt['aws_project_region'],
|
|
84
|
+
header: {},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
typeof opt.graphql_headers !== 'undefined' &&
|
|
90
|
+
typeof opt.graphql_headers !== 'function'
|
|
91
|
+
) {
|
|
92
|
+
logger.warn('graphql_headers should be a function');
|
|
93
|
+
opt.graphql_headers = undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this._options = Object.assign({}, this._options, opt);
|
|
97
|
+
|
|
98
|
+
this.createInstance();
|
|
99
|
+
|
|
100
|
+
return this._options;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create an instance of API for the library
|
|
105
|
+
* @return - A promise of true if Success
|
|
106
|
+
*/
|
|
107
|
+
createInstance() {
|
|
108
|
+
logger.debug('create Rest instance');
|
|
109
|
+
if (this._options) {
|
|
110
|
+
this._api = new RestClient(this._options);
|
|
111
|
+
// Share instance Credentials with client for SSR
|
|
112
|
+
this._api.Credentials = this.Credentials;
|
|
113
|
+
|
|
114
|
+
return true;
|
|
115
|
+
} else {
|
|
116
|
+
return Promise.reject('API not configured');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async _headerBasedAuth(
|
|
121
|
+
defaultAuthenticationType?,
|
|
122
|
+
additionalHeaders: { [key: string]: string } = {}
|
|
123
|
+
) {
|
|
124
|
+
const { aws_appsync_authenticationType, aws_appsync_apiKey: apiKey } =
|
|
125
|
+
this._options;
|
|
126
|
+
const authenticationType =
|
|
127
|
+
defaultAuthenticationType || aws_appsync_authenticationType || 'AWS_IAM';
|
|
128
|
+
let headers = {};
|
|
129
|
+
|
|
130
|
+
switch (authenticationType) {
|
|
131
|
+
case 'API_KEY':
|
|
132
|
+
if (!apiKey) {
|
|
133
|
+
throw new Error(GraphQLAuthError.NO_API_KEY);
|
|
134
|
+
}
|
|
135
|
+
headers = {
|
|
136
|
+
Authorization: null,
|
|
137
|
+
'X-Api-Key': apiKey,
|
|
138
|
+
};
|
|
139
|
+
break;
|
|
140
|
+
case 'AWS_IAM':
|
|
141
|
+
const credentialsOK = await this._ensureCredentials();
|
|
142
|
+
if (!credentialsOK) {
|
|
143
|
+
throw new Error(GraphQLAuthError.NO_CREDENTIALS);
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
case 'OPENID_CONNECT':
|
|
147
|
+
try {
|
|
148
|
+
let token;
|
|
149
|
+
// backwards compatibility
|
|
150
|
+
const federatedInfo = await Cache.getItem('federatedInfo');
|
|
151
|
+
if (federatedInfo) {
|
|
152
|
+
token = federatedInfo.token;
|
|
153
|
+
} else {
|
|
154
|
+
const currentUser = await Auth.currentAuthenticatedUser();
|
|
155
|
+
if (currentUser) {
|
|
156
|
+
token = currentUser.token;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!token) {
|
|
160
|
+
throw new Error(GraphQLAuthError.NO_FEDERATED_JWT);
|
|
161
|
+
}
|
|
162
|
+
headers = {
|
|
163
|
+
Authorization: token,
|
|
164
|
+
};
|
|
165
|
+
} catch (e) {
|
|
166
|
+
throw new Error(GraphQLAuthError.NO_CURRENT_USER);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
case 'AMAZON_COGNITO_USER_POOLS':
|
|
170
|
+
try {
|
|
171
|
+
const session = await this.Auth.currentSession();
|
|
172
|
+
headers = {
|
|
173
|
+
Authorization: session.getAccessToken().getJwtToken(),
|
|
174
|
+
};
|
|
175
|
+
} catch (e) {
|
|
176
|
+
throw new Error(GraphQLAuthError.NO_CURRENT_USER);
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
case 'AWS_LAMBDA':
|
|
180
|
+
if (!additionalHeaders.Authorization) {
|
|
181
|
+
throw new Error(GraphQLAuthError.NO_AUTH_TOKEN);
|
|
182
|
+
}
|
|
183
|
+
headers = {
|
|
184
|
+
Authorization: additionalHeaders.Authorization,
|
|
185
|
+
};
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
headers = {
|
|
189
|
+
Authorization: null,
|
|
190
|
+
};
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return headers;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* to get the operation type
|
|
199
|
+
* @param operation
|
|
200
|
+
*/
|
|
201
|
+
getGraphqlOperationType(operation: GraphQLOperation): OperationTypeNode {
|
|
202
|
+
const doc = parse(operation);
|
|
203
|
+
const definitions =
|
|
204
|
+
doc.definitions as ReadonlyArray<OperationDefinitionNode>;
|
|
205
|
+
const [{ operation: operationType }] = definitions;
|
|
206
|
+
|
|
207
|
+
return operationType;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Executes a GraphQL operation
|
|
212
|
+
*
|
|
213
|
+
* @param options - GraphQL Options
|
|
214
|
+
* @param [additionalHeaders] - headers to merge in after any `graphql_headers` set in the config
|
|
215
|
+
* @returns An Observable if the query is a subscription query, else a promise of the graphql result.
|
|
216
|
+
*/
|
|
217
|
+
graphql<T = any>(
|
|
218
|
+
{ query: paramQuery, variables = {}, authMode, authToken }: GraphQLOptions,
|
|
219
|
+
additionalHeaders?: { [key: string]: string },
|
|
220
|
+
customUserAgentDetails?: CustomUserAgentDetails
|
|
221
|
+
): Observable<GraphQLResult<T>> | Promise<GraphQLResult<T>> {
|
|
222
|
+
const query =
|
|
223
|
+
typeof paramQuery === 'string'
|
|
224
|
+
? parse(paramQuery)
|
|
225
|
+
: parse(print(paramQuery));
|
|
226
|
+
|
|
227
|
+
const [operationDef = {}] = query.definitions.filter(
|
|
228
|
+
def => def.kind === 'OperationDefinition'
|
|
229
|
+
);
|
|
230
|
+
const { operation: operationType } =
|
|
231
|
+
operationDef as OperationDefinitionNode;
|
|
232
|
+
|
|
233
|
+
const headers = additionalHeaders || {};
|
|
234
|
+
|
|
235
|
+
// if an authorization header is set, have the explicit authToken take precedence
|
|
236
|
+
if (authToken) {
|
|
237
|
+
headers.Authorization = authToken;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
switch (operationType) {
|
|
241
|
+
case 'query':
|
|
242
|
+
case 'mutation':
|
|
243
|
+
this.createInstanceIfNotCreated();
|
|
244
|
+
const cancellableToken = this._api.getCancellableToken();
|
|
245
|
+
const initParams = {
|
|
246
|
+
cancellableToken,
|
|
247
|
+
withCredentials: this._options.withCredentials,
|
|
248
|
+
};
|
|
249
|
+
const responsePromise = this._graphql<T>(
|
|
250
|
+
{ query, variables, authMode },
|
|
251
|
+
headers,
|
|
252
|
+
initParams,
|
|
253
|
+
customUserAgentDetails
|
|
254
|
+
);
|
|
255
|
+
this._api.updateRequestToBeCancellable(
|
|
256
|
+
responsePromise,
|
|
257
|
+
cancellableToken
|
|
258
|
+
);
|
|
259
|
+
return responsePromise;
|
|
260
|
+
case 'subscription':
|
|
261
|
+
return this._graphqlSubscribe(
|
|
262
|
+
{ query, variables, authMode },
|
|
263
|
+
headers,
|
|
264
|
+
customUserAgentDetails
|
|
265
|
+
);
|
|
266
|
+
default:
|
|
267
|
+
throw new Error(`invalid operation type: ${operationType}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private async _graphql<T = any>(
|
|
272
|
+
{ query, variables, authMode }: GraphQLOptions,
|
|
273
|
+
additionalHeaders = {},
|
|
274
|
+
initParams = {},
|
|
275
|
+
customUserAgentDetails?: CustomUserAgentDetails
|
|
276
|
+
): Promise<GraphQLResult<T>> {
|
|
277
|
+
this.createInstanceIfNotCreated();
|
|
278
|
+
const {
|
|
279
|
+
aws_appsync_region: region,
|
|
280
|
+
aws_appsync_graphqlEndpoint: appSyncGraphqlEndpoint,
|
|
281
|
+
graphql_headers = () => ({}),
|
|
282
|
+
graphql_endpoint: customGraphqlEndpoint,
|
|
283
|
+
graphql_endpoint_iam_region: customEndpointRegion,
|
|
284
|
+
} = this._options;
|
|
285
|
+
|
|
286
|
+
const headers = {
|
|
287
|
+
...(!customGraphqlEndpoint &&
|
|
288
|
+
(await this._headerBasedAuth(authMode, additionalHeaders))),
|
|
289
|
+
...(customGraphqlEndpoint &&
|
|
290
|
+
(customEndpointRegion
|
|
291
|
+
? await this._headerBasedAuth(authMode, additionalHeaders)
|
|
292
|
+
: { Authorization: null })),
|
|
293
|
+
...(await graphql_headers({ query, variables })),
|
|
294
|
+
...additionalHeaders,
|
|
295
|
+
...(!customGraphqlEndpoint && {
|
|
296
|
+
[USER_AGENT_HEADER]: getAmplifyUserAgentString(customUserAgentDetails),
|
|
297
|
+
}),
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const body = {
|
|
301
|
+
query: print(query as DocumentNode),
|
|
302
|
+
variables,
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const init = Object.assign(
|
|
306
|
+
{
|
|
307
|
+
headers,
|
|
308
|
+
body,
|
|
309
|
+
signerServiceInfo: {
|
|
310
|
+
service: !customGraphqlEndpoint ? 'appsync' : 'execute-api',
|
|
311
|
+
region: !customGraphqlEndpoint ? region : customEndpointRegion,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
initParams
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const endpoint = customGraphqlEndpoint || appSyncGraphqlEndpoint;
|
|
318
|
+
|
|
319
|
+
if (!endpoint) {
|
|
320
|
+
const error = new GraphQLError('No graphql endpoint provided.');
|
|
321
|
+
|
|
322
|
+
throw {
|
|
323
|
+
data: {},
|
|
324
|
+
errors: [error],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let response;
|
|
329
|
+
try {
|
|
330
|
+
response = await this._api.post(endpoint, init);
|
|
331
|
+
} catch (err) {
|
|
332
|
+
// If the exception is because user intentionally
|
|
333
|
+
// cancelled the request, do not modify the exception
|
|
334
|
+
// so that clients can identify the exception correctly.
|
|
335
|
+
if (this._api.isCancel(err)) {
|
|
336
|
+
throw err;
|
|
337
|
+
}
|
|
338
|
+
response = {
|
|
339
|
+
data: {},
|
|
340
|
+
errors: [new GraphQLError(err.message, null, null, null, null, err)],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const { errors } = response;
|
|
345
|
+
|
|
346
|
+
if (errors && errors.length) {
|
|
347
|
+
throw response;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return response;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async createInstanceIfNotCreated() {
|
|
354
|
+
if (!this._api) {
|
|
355
|
+
await this.createInstance();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Checks to see if an error thrown is from an api request cancellation
|
|
361
|
+
* @param {any} error - Any error
|
|
362
|
+
* @return {boolean} - A boolean indicating if the error was from an api request cancellation
|
|
363
|
+
*/
|
|
364
|
+
isCancel(error) {
|
|
365
|
+
return this._api.isCancel(error);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Cancels an inflight request. Only applicable for graphql queries and mutations
|
|
370
|
+
* @param {any} request - request to cancel
|
|
371
|
+
* @return {boolean} - A boolean indicating if the request was cancelled
|
|
372
|
+
*/
|
|
373
|
+
cancel(request: Promise<any>, message?: string) {
|
|
374
|
+
return this._api.cancel(request, message);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Check if the request has a corresponding cancel token in the WeakMap.
|
|
379
|
+
* @params request - The request promise
|
|
380
|
+
* @return if the request has a corresponding cancel token.
|
|
381
|
+
*/
|
|
382
|
+
hasCancelToken(request: Promise<any>) {
|
|
383
|
+
return this._api.hasCancelToken(request);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private _graphqlSubscribe(
|
|
387
|
+
{
|
|
388
|
+
query,
|
|
389
|
+
variables,
|
|
390
|
+
authMode: defaultAuthenticationType,
|
|
391
|
+
authToken,
|
|
392
|
+
}: GraphQLOptions,
|
|
393
|
+
additionalHeaders = {},
|
|
394
|
+
customUserAgentDetails?: CustomUserAgentDetails
|
|
395
|
+
): Observable<any> {
|
|
396
|
+
const {
|
|
397
|
+
aws_appsync_region: region,
|
|
398
|
+
aws_appsync_graphqlEndpoint: appSyncGraphqlEndpoint,
|
|
399
|
+
aws_appsync_authenticationType,
|
|
400
|
+
aws_appsync_apiKey: apiKey,
|
|
401
|
+
graphql_headers = () => ({}),
|
|
402
|
+
} = this._options;
|
|
403
|
+
const authenticationType =
|
|
404
|
+
defaultAuthenticationType || aws_appsync_authenticationType || 'AWS_IAM';
|
|
405
|
+
|
|
406
|
+
if (InternalPubSub && typeof InternalPubSub.subscribe === 'function') {
|
|
407
|
+
return InternalPubSub.subscribe(
|
|
408
|
+
'',
|
|
409
|
+
{
|
|
410
|
+
provider: INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER,
|
|
411
|
+
appSyncGraphqlEndpoint,
|
|
412
|
+
authenticationType,
|
|
413
|
+
apiKey,
|
|
414
|
+
query: print(query as DocumentNode),
|
|
415
|
+
region,
|
|
416
|
+
variables,
|
|
417
|
+
graphql_headers,
|
|
418
|
+
additionalHeaders,
|
|
419
|
+
authToken,
|
|
420
|
+
},
|
|
421
|
+
customUserAgentDetails
|
|
422
|
+
);
|
|
423
|
+
} else {
|
|
424
|
+
logger.debug('No pubsub module applied for subscription');
|
|
425
|
+
throw new Error('No pubsub module applied for subscription');
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
_ensureCredentials() {
|
|
433
|
+
return this.Credentials.get()
|
|
434
|
+
.then(credentials => {
|
|
435
|
+
if (!credentials) return false;
|
|
436
|
+
const cred = this.Credentials.shear(credentials);
|
|
437
|
+
logger.debug('set credentials for api', cred);
|
|
438
|
+
|
|
439
|
+
return true;
|
|
440
|
+
})
|
|
441
|
+
.catch(err => {
|
|
442
|
+
logger.warn('ensure credentials error', err);
|
|
443
|
+
return false;
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export const InternalGraphQLAPI = new InternalGraphQLAPIClass(null);
|
|
449
|
+
Amplify.register(InternalGraphQLAPI);
|
package/src/types/index.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { Source, DocumentNode, GraphQLError } from 'graphql';
|
|
3
|
+
import { Source, DocumentNode, GraphQLError, OperationTypeNode } from 'graphql';
|
|
4
4
|
export { OperationTypeNode } from 'graphql';
|
|
5
5
|
import { GRAPHQL_AUTH_MODE } from '@aws-amplify/auth';
|
|
6
6
|
export { GRAPHQL_AUTH_MODE };
|
|
7
|
+
import { CustomUserAgentDetails } from '@aws-amplify/core';
|
|
7
8
|
|
|
8
9
|
export interface GraphQLOptions {
|
|
9
10
|
query: string | DocumentNode;
|
|
10
11
|
variables?: object;
|
|
11
12
|
authMode?: keyof typeof GRAPHQL_AUTH_MODE;
|
|
12
13
|
authToken?: string;
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated This property should not be used
|
|
16
|
+
*/
|
|
17
|
+
userAgentSuffix?: string; // TODO: remove in v6
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export interface GraphQLResult<T = object> {
|