@graphql-tools/executor 0.0.2 → 0.0.3-alpha-20221031181646-cd48ed2f
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/cjs/execution/execute.js +654 -109
- package/cjs/execution/flattenAsyncIterable.js +91 -0
- package/cjs/execution/invariant.js +9 -0
- package/cjs/execution/promiseForObject.js +21 -0
- package/esm/execution/execute.js +651 -108
- package/esm/execution/flattenAsyncIterable.js +87 -0
- package/esm/execution/invariant.js +5 -0
- package/esm/execution/promiseForObject.js +17 -0
- package/package.json +2 -2
- package/typings/execution/execute.d.cts +168 -23
- package/typings/execution/execute.d.ts +168 -23
- package/typings/execution/flattenAsyncIterable.d.cts +7 -0
- package/typings/execution/flattenAsyncIterable.d.ts +7 -0
- package/typings/execution/invariant.d.cts +1 -0
- package/typings/execution/invariant.d.ts +1 -0
- package/typings/execution/promiseForObject.d.cts +12 -0
- package/typings/execution/promiseForObject.d.ts +12 -0
- package/cjs/execution/subscribe.js +0 -158
- package/esm/execution/subscribe.js +0 -153
- package/typings/execution/subscribe.d.cts +0 -59
- package/typings/execution/subscribe.d.ts +0 -59
package/cjs/execution/execute.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getFieldDef = exports.createSourceEventStream = exports.subscribe = exports.defaultFieldResolver = exports.defaultTypeResolver = exports.buildResolveInfo = exports.buildExecutionContext = exports.assertValidExecutionArguments = exports.executeSync = exports.execute = void 0;
|
|
3
|
+
exports.getFieldDef = exports.createSourceEventStream = exports.experimentalSubscribeIncrementally = exports.subscribe = exports.defaultFieldResolver = exports.defaultTypeResolver = exports.buildResolveInfo = exports.buildExecutionContext = exports.assertValidExecutionArguments = exports.executeSync = exports.experimentalExecuteIncrementally = exports.execute = void 0;
|
|
4
4
|
const graphql_1 = require("graphql");
|
|
5
5
|
const utils_1 = require("@graphql-tools/utils");
|
|
6
6
|
const values_js_1 = require("./values.js");
|
|
7
|
+
const promiseForObject_js_1 = require("./promiseForObject.js");
|
|
8
|
+
const flattenAsyncIterable_js_1 = require("./flattenAsyncIterable.js");
|
|
9
|
+
const invariant_js_1 = require("./invariant.js");
|
|
10
|
+
/**
|
|
11
|
+
* A memoized collection of relevant subfields with regard to the return
|
|
12
|
+
* type. Memoizing ensures the subfields are not repeatedly calculated, which
|
|
13
|
+
* saves overhead when resolving lists of values.
|
|
14
|
+
*/
|
|
15
|
+
const collectSubfields = (0, utils_1.memoize3)((exeContext, returnType, fieldNodes) => (0, utils_1.collectSubFields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes));
|
|
16
|
+
const UNEXPECTED_MULTIPLE_PAYLOADS = 'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)';
|
|
7
17
|
/**
|
|
8
18
|
* Implements the "Executing requests" section of the GraphQL specification.
|
|
9
19
|
*
|
|
@@ -13,8 +23,44 @@ const values_js_1 = require("./values.js");
|
|
|
13
23
|
*
|
|
14
24
|
* If the arguments to this function do not result in a legal execution context,
|
|
15
25
|
* a GraphQLError will be thrown immediately explaining the invalid input.
|
|
26
|
+
*
|
|
27
|
+
* This function does not support incremental delivery (`@defer` and `@stream`).
|
|
28
|
+
* If an operation which would defer or stream data is executed with this
|
|
29
|
+
* function, it will throw or resolve to an object containing an error instead.
|
|
30
|
+
* Use `experimentalExecuteIncrementally` if you want to support incremental
|
|
31
|
+
* delivery.
|
|
16
32
|
*/
|
|
17
33
|
function execute(args) {
|
|
34
|
+
const result = experimentalExecuteIncrementally(args);
|
|
35
|
+
if (!(0, utils_1.isPromise)(result)) {
|
|
36
|
+
if ('initialResult' in result) {
|
|
37
|
+
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
return result.then(incrementalResult => {
|
|
42
|
+
if ('initialResult' in incrementalResult) {
|
|
43
|
+
return {
|
|
44
|
+
errors: [(0, utils_1.createGraphQLError)(UNEXPECTED_MULTIPLE_PAYLOADS)],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return incrementalResult;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
exports.execute = execute;
|
|
51
|
+
/**
|
|
52
|
+
* Implements the "Executing requests" section of the GraphQL specification,
|
|
53
|
+
* including `@defer` and `@stream` as proposed in
|
|
54
|
+
* https://github.com/graphql/graphql-spec/pull/742
|
|
55
|
+
*
|
|
56
|
+
* This function returns a Promise of an ExperimentalIncrementalExecutionResults
|
|
57
|
+
* object. This object either consists of a single ExecutionResult, or an
|
|
58
|
+
* object containing an `initialResult` and a stream of `subsequentResults`.
|
|
59
|
+
*
|
|
60
|
+
* If the arguments to this function do not result in a legal execution context,
|
|
61
|
+
* a GraphQLError will be thrown immediately explaining the invalid input.
|
|
62
|
+
*/
|
|
63
|
+
function experimentalExecuteIncrementally(args) {
|
|
18
64
|
// If a valid execution context cannot be created due to incorrect arguments,
|
|
19
65
|
// a "Response" with only errors is returned.
|
|
20
66
|
const exeContext = buildExecutionContext(args);
|
|
@@ -24,7 +70,7 @@ function execute(args) {
|
|
|
24
70
|
}
|
|
25
71
|
return executeImpl(exeContext);
|
|
26
72
|
}
|
|
27
|
-
exports.
|
|
73
|
+
exports.experimentalExecuteIncrementally = experimentalExecuteIncrementally;
|
|
28
74
|
function executeImpl(exeContext) {
|
|
29
75
|
// Return a Promise that will eventually resolve to the data described by
|
|
30
76
|
// The "Response" section of the GraphQL specification.
|
|
@@ -40,12 +86,34 @@ function executeImpl(exeContext) {
|
|
|
40
86
|
try {
|
|
41
87
|
const result = executeOperation(exeContext);
|
|
42
88
|
if ((0, utils_1.isPromise)(result)) {
|
|
43
|
-
return result.then(data =>
|
|
89
|
+
return result.then(data => {
|
|
90
|
+
const initialResult = buildResponse(data, exeContext.errors);
|
|
91
|
+
if (exeContext.subsequentPayloads.size > 0) {
|
|
92
|
+
return {
|
|
93
|
+
initialResult: {
|
|
94
|
+
...initialResult,
|
|
95
|
+
hasNext: true,
|
|
96
|
+
},
|
|
97
|
+
subsequentResults: yieldSubsequentPayloads(exeContext),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return initialResult;
|
|
101
|
+
}, error => {
|
|
44
102
|
exeContext.errors.push(error);
|
|
45
103
|
return buildResponse(null, exeContext.errors);
|
|
46
104
|
});
|
|
47
105
|
}
|
|
48
|
-
|
|
106
|
+
const initialResult = buildResponse(result, exeContext.errors);
|
|
107
|
+
if (exeContext.subsequentPayloads.size > 0) {
|
|
108
|
+
return {
|
|
109
|
+
initialResult: {
|
|
110
|
+
...initialResult,
|
|
111
|
+
hasNext: true,
|
|
112
|
+
},
|
|
113
|
+
subsequentResults: yieldSubsequentPayloads(exeContext),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return initialResult;
|
|
49
117
|
}
|
|
50
118
|
catch (error) {
|
|
51
119
|
exeContext.errors.push(error);
|
|
@@ -58,9 +126,9 @@ function executeImpl(exeContext) {
|
|
|
58
126
|
* that all field resolvers are also synchronous.
|
|
59
127
|
*/
|
|
60
128
|
function executeSync(args) {
|
|
61
|
-
const result =
|
|
129
|
+
const result = experimentalExecuteIncrementally(args);
|
|
62
130
|
// Assert that the execution was synchronous.
|
|
63
|
-
if ((0, utils_1.isPromise)(result)) {
|
|
131
|
+
if ((0, utils_1.isPromise)(result) || 'initialResult' in result) {
|
|
64
132
|
throw new Error('GraphQL execution failed to complete synchronously.');
|
|
65
133
|
}
|
|
66
134
|
return result;
|
|
@@ -148,6 +216,7 @@ function buildExecutionContext(args) {
|
|
|
148
216
|
fieldResolver: fieldResolver !== null && fieldResolver !== void 0 ? fieldResolver : exports.defaultFieldResolver,
|
|
149
217
|
typeResolver: typeResolver !== null && typeResolver !== void 0 ? typeResolver : exports.defaultTypeResolver,
|
|
150
218
|
subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : exports.defaultFieldResolver,
|
|
219
|
+
subsequentPayloads: new Set(),
|
|
151
220
|
errors: [],
|
|
152
221
|
};
|
|
153
222
|
}
|
|
@@ -156,6 +225,7 @@ function buildPerEventExecutionContext(exeContext, payload) {
|
|
|
156
225
|
return {
|
|
157
226
|
...exeContext,
|
|
158
227
|
rootValue: payload,
|
|
228
|
+
subsequentPayloads: new Set(),
|
|
159
229
|
errors: [],
|
|
160
230
|
};
|
|
161
231
|
}
|
|
@@ -165,26 +235,32 @@ function buildPerEventExecutionContext(exeContext, payload) {
|
|
|
165
235
|
function executeOperation(exeContext) {
|
|
166
236
|
const { operation, schema, fragments, variableValues, rootValue } = exeContext;
|
|
167
237
|
const rootType = (0, utils_1.getDefinedRootType)(schema, operation.operation, [operation]);
|
|
168
|
-
|
|
238
|
+
if (rootType == null) {
|
|
239
|
+
(0, utils_1.createGraphQLError)(`Schema is not configured to execute ${operation.operation} operation.`, {
|
|
240
|
+
nodes: operation,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const { fields: rootFields, patches } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
|
|
169
244
|
const path = undefined;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
245
|
+
let result;
|
|
246
|
+
if (operation.operation === 'mutation') {
|
|
247
|
+
result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
result = executeFields(exeContext, rootType, rootValue, path, rootFields);
|
|
251
|
+
}
|
|
252
|
+
for (const patch of patches) {
|
|
253
|
+
const { label, fields: patchFields } = patch;
|
|
254
|
+
executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
181
257
|
}
|
|
182
258
|
/**
|
|
183
259
|
* Implements the "Executing selection sets" section of the spec
|
|
184
260
|
* for fields that must be executed serially.
|
|
185
261
|
*/
|
|
186
262
|
function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) {
|
|
187
|
-
return (0, utils_1.promiseReduce)(fields
|
|
263
|
+
return (0, utils_1.promiseReduce)(fields, (results, [responseName, fieldNodes]) => {
|
|
188
264
|
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name);
|
|
189
265
|
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath);
|
|
190
266
|
if (result === undefined) {
|
|
@@ -204,19 +280,30 @@ function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields
|
|
|
204
280
|
* Implements the "Executing selection sets" section of the spec
|
|
205
281
|
* for fields that may be executed in parallel.
|
|
206
282
|
*/
|
|
207
|
-
function executeFields(exeContext, parentType, sourceValue, path, fields) {
|
|
283
|
+
function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) {
|
|
208
284
|
const results = Object.create(null);
|
|
209
285
|
let containsPromise = false;
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
286
|
+
try {
|
|
287
|
+
for (const [responseName, fieldNodes] of fields) {
|
|
288
|
+
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name);
|
|
289
|
+
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath, asyncPayloadRecord);
|
|
290
|
+
if (result !== undefined) {
|
|
291
|
+
results[responseName] = result;
|
|
292
|
+
if ((0, utils_1.isPromise)(result)) {
|
|
293
|
+
containsPromise = true;
|
|
294
|
+
}
|
|
217
295
|
}
|
|
218
296
|
}
|
|
219
297
|
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
if (containsPromise) {
|
|
300
|
+
// Ensure that any promises returned by other fields are handled, as they may also reject.
|
|
301
|
+
return (0, promiseForObject_js_1.promiseForObject)(results).finally(() => {
|
|
302
|
+
throw error;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
220
307
|
// If there are no promises, we can just return the object
|
|
221
308
|
if (!containsPromise) {
|
|
222
309
|
return results;
|
|
@@ -224,13 +311,7 @@ function executeFields(exeContext, parentType, sourceValue, path, fields) {
|
|
|
224
311
|
// Otherwise, results is a map from field name to the result of resolving that
|
|
225
312
|
// field, which is possibly a promise. Return a promise that will return this
|
|
226
313
|
// same map, but with any promises replaced with the values they resolved to.
|
|
227
|
-
return
|
|
228
|
-
const resolvedObject = Object.create(null);
|
|
229
|
-
for (const [i, key] of Object.keys(results).entries()) {
|
|
230
|
-
resolvedObject[key] = resolvedValues[i];
|
|
231
|
-
}
|
|
232
|
-
return resolvedObject;
|
|
233
|
-
});
|
|
314
|
+
return (0, promiseForObject_js_1.promiseForObject)(results);
|
|
234
315
|
}
|
|
235
316
|
/**
|
|
236
317
|
* Implements the "Executing fields" section of the spec
|
|
@@ -238,14 +319,15 @@ function executeFields(exeContext, parentType, sourceValue, path, fields) {
|
|
|
238
319
|
* calling its resolve function, then calls completeValue to complete promises,
|
|
239
320
|
* serialize scalars, or execute the sub-selection-set for objects.
|
|
240
321
|
*/
|
|
241
|
-
function executeField(exeContext, parentType, source, fieldNodes, path) {
|
|
242
|
-
var _a;
|
|
322
|
+
function executeField(exeContext, parentType, source, fieldNodes, path, asyncPayloadRecord) {
|
|
323
|
+
var _a, _b;
|
|
324
|
+
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors;
|
|
243
325
|
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]);
|
|
244
326
|
if (!fieldDef) {
|
|
245
327
|
return;
|
|
246
328
|
}
|
|
247
329
|
const returnType = fieldDef.type;
|
|
248
|
-
const resolveFn = (
|
|
330
|
+
const resolveFn = (_b = fieldDef.resolve) !== null && _b !== void 0 ? _b : exeContext.fieldResolver;
|
|
249
331
|
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path);
|
|
250
332
|
// Get the resolve function, regardless of if its result is normal or abrupt (error).
|
|
251
333
|
try {
|
|
@@ -260,24 +342,28 @@ function executeField(exeContext, parentType, source, fieldNodes, path) {
|
|
|
260
342
|
const result = resolveFn(source, args, contextValue, info);
|
|
261
343
|
let completed;
|
|
262
344
|
if ((0, utils_1.isPromise)(result)) {
|
|
263
|
-
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved));
|
|
345
|
+
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved, asyncPayloadRecord));
|
|
264
346
|
}
|
|
265
347
|
else {
|
|
266
|
-
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result);
|
|
348
|
+
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
|
|
267
349
|
}
|
|
268
350
|
if ((0, utils_1.isPromise)(completed)) {
|
|
269
351
|
// Note: we don't rely on a `catch` method, but we do expect "thenable"
|
|
270
352
|
// to take a second callback for the error case.
|
|
271
353
|
return completed.then(undefined, rawError => {
|
|
272
354
|
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path));
|
|
273
|
-
|
|
355
|
+
const handledError = handleFieldError(error, returnType, errors);
|
|
356
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
357
|
+
return handledError;
|
|
274
358
|
});
|
|
275
359
|
}
|
|
276
360
|
return completed;
|
|
277
361
|
}
|
|
278
362
|
catch (rawError) {
|
|
279
363
|
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path));
|
|
280
|
-
|
|
364
|
+
const handledError = handleFieldError(error, returnType, errors);
|
|
365
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
366
|
+
return handledError;
|
|
281
367
|
}
|
|
282
368
|
}
|
|
283
369
|
/**
|
|
@@ -301,7 +387,7 @@ function buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path) {
|
|
|
301
387
|
};
|
|
302
388
|
}
|
|
303
389
|
exports.buildResolveInfo = buildResolveInfo;
|
|
304
|
-
function handleFieldError(error, returnType,
|
|
390
|
+
function handleFieldError(error, returnType, errors) {
|
|
305
391
|
// If the field type is non-nullable, then it is resolved without any
|
|
306
392
|
// protection from errors, however it still properly locates the error.
|
|
307
393
|
if ((0, graphql_1.isNonNullType)(returnType)) {
|
|
@@ -309,7 +395,7 @@ function handleFieldError(error, returnType, exeContext) {
|
|
|
309
395
|
}
|
|
310
396
|
// Otherwise, error protection is applied, logging the error and resolving
|
|
311
397
|
// a null value for this field if one is encountered.
|
|
312
|
-
|
|
398
|
+
errors.push(error);
|
|
313
399
|
return null;
|
|
314
400
|
}
|
|
315
401
|
/**
|
|
@@ -333,7 +419,7 @@ function handleFieldError(error, returnType, exeContext) {
|
|
|
333
419
|
* Otherwise, the field type expects a sub-selection set, and will complete the
|
|
334
420
|
* value by executing all sub-selections.
|
|
335
421
|
*/
|
|
336
|
-
function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
422
|
+
function completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
|
|
337
423
|
// If result is an Error, throw a located error.
|
|
338
424
|
if (result instanceof Error) {
|
|
339
425
|
throw result;
|
|
@@ -341,7 +427,7 @@ function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
|
341
427
|
// If field type is NonNull, complete for inner type, and throw field error
|
|
342
428
|
// if result is null.
|
|
343
429
|
if ((0, graphql_1.isNonNullType)(returnType)) {
|
|
344
|
-
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result);
|
|
430
|
+
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result, asyncPayloadRecord);
|
|
345
431
|
if (completed === null) {
|
|
346
432
|
throw new Error(`Cannot return null for non-nullable field ${info.parentType.name}.${info.fieldName}.`);
|
|
347
433
|
}
|
|
@@ -353,7 +439,7 @@ function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
|
353
439
|
}
|
|
354
440
|
// If field type is List, complete each item in the list with the inner type
|
|
355
441
|
if ((0, graphql_1.isListType)(returnType)) {
|
|
356
|
-
return completeListValue(exeContext, returnType, fieldNodes, info, path, result);
|
|
442
|
+
return completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
|
|
357
443
|
}
|
|
358
444
|
// If field type is a leaf type, Scalar or Enum, serialize to a valid value,
|
|
359
445
|
// returning null if serialization is not possible.
|
|
@@ -363,51 +449,74 @@ function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
|
363
449
|
// If field type is an abstract type, Interface or Union, determine the
|
|
364
450
|
// runtime Object type and complete for that type.
|
|
365
451
|
if ((0, graphql_1.isAbstractType)(returnType)) {
|
|
366
|
-
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result);
|
|
452
|
+
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
|
|
367
453
|
}
|
|
368
454
|
// If field type is Object, execute and complete all sub-selections.
|
|
369
455
|
if ((0, graphql_1.isObjectType)(returnType)) {
|
|
370
|
-
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result);
|
|
456
|
+
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
|
|
371
457
|
}
|
|
372
458
|
/* c8 ignore next 6 */
|
|
373
459
|
// Not reachable, all possible output types have been considered.
|
|
374
460
|
console.assert(false, 'Cannot complete value of unexpected output type: ' + (0, utils_1.inspect)(returnType));
|
|
375
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Returns an object containing the `@stream` arguments if a field should be
|
|
464
|
+
* streamed based on the experimental flag, stream directive present and
|
|
465
|
+
* not disabled by the "if" argument.
|
|
466
|
+
*/
|
|
467
|
+
function getStreamValues(exeContext, fieldNodes, path) {
|
|
468
|
+
// do not stream inner lists of multi-dimensional lists
|
|
469
|
+
if (typeof path.key === 'number') {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
// validation only allows equivalent streams on multiple fields, so it is
|
|
473
|
+
// safe to only check the first fieldNode for the stream directive
|
|
474
|
+
const stream = (0, graphql_1.getDirectiveValues)(utils_1.GraphQLStreamDirective, fieldNodes[0], exeContext.variableValues);
|
|
475
|
+
if (!stream) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (stream['if'] === false) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
(0, invariant_js_1.invariant)(typeof stream['initialCount'] === 'number', 'initialCount must be a number');
|
|
482
|
+
(0, invariant_js_1.invariant)(stream['initialCount'] >= 0, 'initialCount must be a positive integer');
|
|
483
|
+
return {
|
|
484
|
+
initialCount: stream['initialCount'],
|
|
485
|
+
label: typeof stream['label'] === 'string' ? stream['label'] : undefined,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
376
488
|
/**
|
|
377
489
|
* Complete a async iterator value by completing the result and calling
|
|
378
490
|
* recursively until all the results are completed.
|
|
379
491
|
*/
|
|
380
|
-
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator) {
|
|
492
|
+
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord) {
|
|
493
|
+
var _a;
|
|
494
|
+
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors;
|
|
495
|
+
const stream = getStreamValues(exeContext, fieldNodes, path);
|
|
381
496
|
let containsPromise = false;
|
|
382
497
|
const completedResults = [];
|
|
383
498
|
let index = 0;
|
|
384
499
|
while (true) {
|
|
385
|
-
|
|
500
|
+
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
|
|
501
|
+
executeStreamIterator(index, iterator, exeContext, fieldNodes, info, itemType, path, stream.label, asyncPayloadRecord);
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
const itemPath = (0, utils_1.addPath)(path, index, undefined);
|
|
505
|
+
let iteration;
|
|
386
506
|
try {
|
|
387
|
-
|
|
388
|
-
if (done) {
|
|
507
|
+
iteration = await iterator.next();
|
|
508
|
+
if (iteration.done) {
|
|
389
509
|
break;
|
|
390
510
|
}
|
|
391
|
-
try {
|
|
392
|
-
// TODO can the error checking logic be consolidated with completeListValue?
|
|
393
|
-
const completedItem = completeValue(exeContext, itemType, fieldNodes, info, fieldPath, value);
|
|
394
|
-
if ((0, utils_1.isPromise)(completedItem)) {
|
|
395
|
-
containsPromise = true;
|
|
396
|
-
}
|
|
397
|
-
completedResults.push(completedItem);
|
|
398
|
-
}
|
|
399
|
-
catch (rawError) {
|
|
400
|
-
completedResults.push(null);
|
|
401
|
-
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(fieldPath));
|
|
402
|
-
handleFieldError(error, itemType, exeContext);
|
|
403
|
-
}
|
|
404
511
|
}
|
|
405
512
|
catch (rawError) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
handleFieldError(error, itemType, exeContext);
|
|
513
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
514
|
+
completedResults.push(handleFieldError(error, itemType, errors));
|
|
409
515
|
break;
|
|
410
516
|
}
|
|
517
|
+
if (completeListItemValue(iteration.value, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
|
|
518
|
+
containsPromise = true;
|
|
519
|
+
}
|
|
411
520
|
index += 1;
|
|
412
521
|
}
|
|
413
522
|
return containsPromise ? Promise.all(completedResults) : completedResults;
|
|
@@ -416,48 +525,75 @@ async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info
|
|
|
416
525
|
* Complete a list value by completing each item in the list with the
|
|
417
526
|
* inner type
|
|
418
527
|
*/
|
|
419
|
-
function completeListValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
528
|
+
function completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
|
|
529
|
+
var _a;
|
|
420
530
|
const itemType = returnType.ofType;
|
|
531
|
+
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors;
|
|
421
532
|
if ((0, utils_1.isAsyncIterable)(result)) {
|
|
422
533
|
const iterator = result[Symbol.asyncIterator]();
|
|
423
|
-
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator);
|
|
534
|
+
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord);
|
|
424
535
|
}
|
|
425
536
|
if (!(0, utils_1.isIterableObject)(result)) {
|
|
426
537
|
throw (0, utils_1.createGraphQLError)(`Expected Iterable, but did not find one for field "${info.parentType.name}.${info.fieldName}".`);
|
|
427
538
|
}
|
|
539
|
+
const stream = getStreamValues(exeContext, fieldNodes, path);
|
|
428
540
|
// This is specified as a simple map, however we're optimizing the path
|
|
429
541
|
// where the list contains no Promises by avoiding creating another Promise.
|
|
430
542
|
let containsPromise = false;
|
|
431
|
-
|
|
543
|
+
let previousAsyncPayloadRecord = asyncPayloadRecord;
|
|
544
|
+
const completedResults = [];
|
|
545
|
+
let index = 0;
|
|
546
|
+
for (const item of result) {
|
|
432
547
|
// No need to modify the info object containing the path,
|
|
433
548
|
// since from here on it is not ever accessed by resolver functions.
|
|
434
549
|
const itemPath = (0, utils_1.addPath)(path, index, undefined);
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item);
|
|
442
|
-
}
|
|
443
|
-
if ((0, utils_1.isPromise)(completedItem)) {
|
|
444
|
-
containsPromise = true;
|
|
445
|
-
// Note: we don't rely on a `catch` method, but we do expect "thenable"
|
|
446
|
-
// to take a second callback for the error case.
|
|
447
|
-
return completedItem.then(undefined, rawError => {
|
|
448
|
-
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
449
|
-
return handleFieldError(error, itemType, exeContext);
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
return completedItem;
|
|
550
|
+
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
|
|
551
|
+
previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord);
|
|
552
|
+
index++;
|
|
553
|
+
continue;
|
|
453
554
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return handleFieldError(error, itemType, exeContext);
|
|
555
|
+
if (completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
|
|
556
|
+
containsPromise = true;
|
|
457
557
|
}
|
|
458
|
-
|
|
558
|
+
index++;
|
|
559
|
+
}
|
|
459
560
|
return containsPromise ? Promise.all(completedResults) : completedResults;
|
|
460
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* Complete a list item value by adding it to the completed results.
|
|
564
|
+
*
|
|
565
|
+
* Returns true if the value is a Promise.
|
|
566
|
+
*/
|
|
567
|
+
function completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord) {
|
|
568
|
+
try {
|
|
569
|
+
let completedItem;
|
|
570
|
+
if ((0, utils_1.isPromise)(item)) {
|
|
571
|
+
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord));
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord);
|
|
575
|
+
}
|
|
576
|
+
if ((0, utils_1.isPromise)(completedItem)) {
|
|
577
|
+
// Note: we don't rely on a `catch` method, but we do expect "thenable"
|
|
578
|
+
// to take a second callback for the error case.
|
|
579
|
+
completedResults.push(completedItem.then(undefined, rawError => {
|
|
580
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
581
|
+
const handledError = handleFieldError(error, itemType, errors);
|
|
582
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
583
|
+
return handledError;
|
|
584
|
+
}));
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
completedResults.push(completedItem);
|
|
588
|
+
}
|
|
589
|
+
catch (rawError) {
|
|
590
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
591
|
+
const handledError = handleFieldError(error, itemType, errors);
|
|
592
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
593
|
+
completedResults.push(handledError);
|
|
594
|
+
}
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
461
597
|
/**
|
|
462
598
|
* Complete a Scalar or Enum by serializing to a valid value, returning
|
|
463
599
|
* null if serialization is not possible.
|
|
@@ -474,15 +610,15 @@ function completeLeafValue(returnType, result) {
|
|
|
474
610
|
* Complete a value of an abstract type by determining the runtime object type
|
|
475
611
|
* of that value, then complete the value for that type.
|
|
476
612
|
*/
|
|
477
|
-
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
613
|
+
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
|
|
478
614
|
var _a;
|
|
479
615
|
const resolveTypeFn = (_a = returnType.resolveType) !== null && _a !== void 0 ? _a : exeContext.typeResolver;
|
|
480
616
|
const contextValue = exeContext.contextValue;
|
|
481
617
|
const runtimeType = resolveTypeFn(result, contextValue, info, returnType);
|
|
482
618
|
if ((0, utils_1.isPromise)(runtimeType)) {
|
|
483
|
-
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result));
|
|
619
|
+
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord));
|
|
484
620
|
}
|
|
485
|
-
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result);
|
|
621
|
+
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord);
|
|
486
622
|
}
|
|
487
623
|
function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) {
|
|
488
624
|
if (runtimeTypeName == null) {
|
|
@@ -512,9 +648,7 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNo
|
|
|
512
648
|
/**
|
|
513
649
|
* Complete an Object value by executing all sub-selections.
|
|
514
650
|
*/
|
|
515
|
-
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result) {
|
|
516
|
-
// Collect sub-fields to execute to complete this value.
|
|
517
|
-
const subFieldNodes = (0, utils_1.collectSubFields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes);
|
|
651
|
+
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
|
|
518
652
|
// If there is an isTypeOf predicate function, call it with the
|
|
519
653
|
// current result. If isTypeOf returns false, then raise an error rather
|
|
520
654
|
// than continuing execution.
|
|
@@ -525,20 +659,30 @@ function completeObjectValue(exeContext, returnType, fieldNodes, info, path, res
|
|
|
525
659
|
if (!resolvedIsTypeOf) {
|
|
526
660
|
throw invalidReturnTypeError(returnType, result, fieldNodes);
|
|
527
661
|
}
|
|
528
|
-
return
|
|
662
|
+
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
|
|
529
663
|
});
|
|
530
664
|
}
|
|
531
665
|
if (!isTypeOf) {
|
|
532
666
|
throw invalidReturnTypeError(returnType, result, fieldNodes);
|
|
533
667
|
}
|
|
534
668
|
}
|
|
535
|
-
return
|
|
669
|
+
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
|
|
536
670
|
}
|
|
537
671
|
function invalidReturnTypeError(returnType, result, fieldNodes) {
|
|
538
672
|
return (0, utils_1.createGraphQLError)(`Expected value of type "${returnType.name}" but got: ${(0, utils_1.inspect)(result)}.`, {
|
|
539
673
|
nodes: fieldNodes,
|
|
540
674
|
});
|
|
541
675
|
}
|
|
676
|
+
function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord) {
|
|
677
|
+
// Collect sub-fields to execute to complete this value.
|
|
678
|
+
const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes);
|
|
679
|
+
const subFields = executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord);
|
|
680
|
+
for (const subPatch of subPatches) {
|
|
681
|
+
const { label, fields: subPatchFieldNodes } = subPatch;
|
|
682
|
+
executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord);
|
|
683
|
+
}
|
|
684
|
+
return subFields;
|
|
685
|
+
}
|
|
542
686
|
/**
|
|
543
687
|
* If a resolveType function is not given, then a default resolve behavior is
|
|
544
688
|
* used which attempts two strategies:
|
|
@@ -606,19 +750,78 @@ exports.defaultFieldResolver = defaultFieldResolver;
|
|
|
606
750
|
* is not an async iterable.
|
|
607
751
|
*
|
|
608
752
|
* If the client-provided arguments to this function do not result in a
|
|
609
|
-
* compliant subscription, a GraphQL Response (ExecutionResult) with
|
|
610
|
-
*
|
|
753
|
+
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive
|
|
754
|
+
* errors and no data will be returned.
|
|
611
755
|
*
|
|
612
|
-
* If the source stream could not be created due to faulty subscription
|
|
613
|
-
*
|
|
756
|
+
* If the source stream could not be created due to faulty subscription resolver
|
|
757
|
+
* logic or underlying systems, the promise will resolve to a single
|
|
614
758
|
* ExecutionResult containing `errors` and no `data`.
|
|
615
759
|
*
|
|
616
760
|
* If the operation succeeded, the promise resolves to an AsyncIterator, which
|
|
617
761
|
* yields a stream of ExecutionResults representing the response stream.
|
|
618
762
|
*
|
|
619
|
-
*
|
|
763
|
+
* This function does not support incremental delivery (`@defer` and `@stream`).
|
|
764
|
+
* If an operation which would defer or stream data is executed with this
|
|
765
|
+
* function, each `InitialIncrementalExecutionResult` and
|
|
766
|
+
* `SubsequentIncrementalExecutionResult` in the result stream will be replaced
|
|
767
|
+
* with an `ExecutionResult` with a single error stating that defer/stream is
|
|
768
|
+
* not supported. Use `experimentalSubscribeIncrementally` if you want to
|
|
769
|
+
* support incremental delivery.
|
|
770
|
+
*
|
|
771
|
+
* Accepts an object with named arguments.
|
|
620
772
|
*/
|
|
621
773
|
function subscribe(args) {
|
|
774
|
+
const maybePromise = experimentalSubscribeIncrementally(args);
|
|
775
|
+
if ((0, utils_1.isPromise)(maybePromise)) {
|
|
776
|
+
return maybePromise.then(resultOrIterable => (0, utils_1.isAsyncIterable)(resultOrIterable)
|
|
777
|
+
? (0, utils_1.mapAsyncIterator)(resultOrIterable, ensureSingleExecutionResult)
|
|
778
|
+
: resultOrIterable);
|
|
779
|
+
}
|
|
780
|
+
return (0, utils_1.isAsyncIterable)(maybePromise) ? (0, utils_1.mapAsyncIterator)(maybePromise, ensureSingleExecutionResult) : maybePromise;
|
|
781
|
+
}
|
|
782
|
+
exports.subscribe = subscribe;
|
|
783
|
+
function ensureSingleExecutionResult(result) {
|
|
784
|
+
if ('hasNext' in result) {
|
|
785
|
+
return {
|
|
786
|
+
errors: [(0, utils_1.createGraphQLError)(UNEXPECTED_MULTIPLE_PAYLOADS)],
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
return result;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Implements the "Subscribe" algorithm described in the GraphQL specification,
|
|
793
|
+
* including `@defer` and `@stream` as proposed in
|
|
794
|
+
* https://github.com/graphql/graphql-spec/pull/742
|
|
795
|
+
*
|
|
796
|
+
* Returns a Promise which resolves to either an AsyncIterator (if successful)
|
|
797
|
+
* or an ExecutionResult (error). The promise will be rejected if the schema or
|
|
798
|
+
* other arguments to this function are invalid, or if the resolved event stream
|
|
799
|
+
* is not an async iterable.
|
|
800
|
+
*
|
|
801
|
+
* If the client-provided arguments to this function do not result in a
|
|
802
|
+
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive
|
|
803
|
+
* errors and no data will be returned.
|
|
804
|
+
*
|
|
805
|
+
* If the source stream could not be created due to faulty subscription resolver
|
|
806
|
+
* logic or underlying systems, the promise will resolve to a single
|
|
807
|
+
* ExecutionResult containing `errors` and no `data`.
|
|
808
|
+
*
|
|
809
|
+
* If the operation succeeded, the promise resolves to an AsyncIterator, which
|
|
810
|
+
* yields a stream of result representing the response stream.
|
|
811
|
+
*
|
|
812
|
+
* Each result may be an ExecutionResult with no `hasNext` (if executing the
|
|
813
|
+
* event did not use `@defer` or `@stream`), or an
|
|
814
|
+
* `InitialIncrementalExecutionResult` or `SubsequentIncrementalExecutionResult`
|
|
815
|
+
* (if executing the event used `@defer` or `@stream`). In the case of
|
|
816
|
+
* incremental execution results, each event produces a single
|
|
817
|
+
* `InitialIncrementalExecutionResult` followed by one or more
|
|
818
|
+
* `SubsequentIncrementalExecutionResult`s; all but the last have `hasNext: true`,
|
|
819
|
+
* and the last has `hasNext: false`. There is no interleaving between results
|
|
820
|
+
* generated from the same original event.
|
|
821
|
+
*
|
|
822
|
+
* Accepts an object with named arguments.
|
|
823
|
+
*/
|
|
824
|
+
function experimentalSubscribeIncrementally(args) {
|
|
622
825
|
// If a valid execution context cannot be created due to incorrect arguments,
|
|
623
826
|
// a "Response" with only errors is returned.
|
|
624
827
|
const exeContext = buildExecutionContext(args);
|
|
@@ -632,7 +835,16 @@ function subscribe(args) {
|
|
|
632
835
|
}
|
|
633
836
|
return mapSourceToResponse(exeContext, resultOrStream);
|
|
634
837
|
}
|
|
635
|
-
exports.
|
|
838
|
+
exports.experimentalSubscribeIncrementally = experimentalSubscribeIncrementally;
|
|
839
|
+
async function* ensureAsyncIterable(someExecutionResult) {
|
|
840
|
+
if ('initialResult' in someExecutionResult) {
|
|
841
|
+
yield someExecutionResult.initialResult;
|
|
842
|
+
yield* someExecutionResult.subsequentResults;
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
yield someExecutionResult;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
636
848
|
function mapSourceToResponse(exeContext, resultOrStream) {
|
|
637
849
|
if (!(0, utils_1.isAsyncIterable)(resultOrStream)) {
|
|
638
850
|
return resultOrStream;
|
|
@@ -643,7 +855,7 @@ function mapSourceToResponse(exeContext, resultOrStream) {
|
|
|
643
855
|
// the GraphQL specification. The `execute` function provides the
|
|
644
856
|
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
|
|
645
857
|
// "ExecuteQuery" algorithm, for which `execute` is also used.
|
|
646
|
-
return (0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), (payload) => executeImpl(buildPerEventExecutionContext(exeContext, payload)));
|
|
858
|
+
return (0, flattenAsyncIterable_js_1.flattenAsyncIterable)((0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload)))));
|
|
647
859
|
}
|
|
648
860
|
/**
|
|
649
861
|
* Implements the "CreateSourceEventStream" algorithm described in the
|
|
@@ -703,7 +915,7 @@ function executeSubscription(exeContext) {
|
|
|
703
915
|
if (rootType == null) {
|
|
704
916
|
throw (0, utils_1.createGraphQLError)('Schema is not configured to execute subscription operation.', { nodes: operation });
|
|
705
917
|
}
|
|
706
|
-
const rootFields = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
|
|
918
|
+
const { fields: rootFields } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
|
|
707
919
|
const [responseName, fieldNodes] = [...rootFields.entries()][0];
|
|
708
920
|
const fieldName = fieldNodes[0].name.value;
|
|
709
921
|
const fieldDef = getFieldDef(schema, rootType, fieldNodes[0]);
|
|
@@ -747,6 +959,339 @@ function assertEventStream(result) {
|
|
|
747
959
|
}
|
|
748
960
|
return result;
|
|
749
961
|
}
|
|
962
|
+
function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) {
|
|
963
|
+
const asyncPayloadRecord = new DeferredFragmentRecord({
|
|
964
|
+
label,
|
|
965
|
+
path,
|
|
966
|
+
parentContext,
|
|
967
|
+
exeContext,
|
|
968
|
+
});
|
|
969
|
+
let promiseOrData;
|
|
970
|
+
try {
|
|
971
|
+
promiseOrData = executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord);
|
|
972
|
+
if ((0, utils_1.isPromise)(promiseOrData)) {
|
|
973
|
+
promiseOrData = promiseOrData.then(null, e => {
|
|
974
|
+
asyncPayloadRecord.errors.push(e);
|
|
975
|
+
return null;
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
catch (e) {
|
|
980
|
+
asyncPayloadRecord.errors.push(e);
|
|
981
|
+
promiseOrData = null;
|
|
982
|
+
}
|
|
983
|
+
asyncPayloadRecord.addData(promiseOrData);
|
|
984
|
+
}
|
|
985
|
+
function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext) {
|
|
986
|
+
const asyncPayloadRecord = new StreamRecord({
|
|
987
|
+
label,
|
|
988
|
+
path: itemPath,
|
|
989
|
+
parentContext,
|
|
990
|
+
exeContext,
|
|
991
|
+
});
|
|
992
|
+
let completedItem;
|
|
993
|
+
try {
|
|
994
|
+
try {
|
|
995
|
+
if ((0, utils_1.isPromise)(item)) {
|
|
996
|
+
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord));
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord);
|
|
1000
|
+
}
|
|
1001
|
+
if ((0, utils_1.isPromise)(completedItem)) {
|
|
1002
|
+
// Note: we don't rely on a `catch` method, but we do expect "thenable"
|
|
1003
|
+
// to take a second callback for the error case.
|
|
1004
|
+
completedItem = completedItem.then(undefined, rawError => {
|
|
1005
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
1006
|
+
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
|
|
1007
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
1008
|
+
return handledError;
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
catch (rawError) {
|
|
1013
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
1014
|
+
completedItem = handleFieldError(error, itemType, asyncPayloadRecord.errors);
|
|
1015
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
catch (error) {
|
|
1019
|
+
asyncPayloadRecord.errors.push(error);
|
|
1020
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
1021
|
+
asyncPayloadRecord.addItems(null);
|
|
1022
|
+
return asyncPayloadRecord;
|
|
1023
|
+
}
|
|
1024
|
+
let completedItems;
|
|
1025
|
+
if ((0, utils_1.isPromise)(completedItem)) {
|
|
1026
|
+
completedItems = completedItem.then(value => [value], error => {
|
|
1027
|
+
asyncPayloadRecord.errors.push(error);
|
|
1028
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
1029
|
+
return null;
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
completedItems = [completedItem];
|
|
1034
|
+
}
|
|
1035
|
+
asyncPayloadRecord.addItems(completedItems);
|
|
1036
|
+
return asyncPayloadRecord;
|
|
1037
|
+
}
|
|
1038
|
+
async function executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath) {
|
|
1039
|
+
let item;
|
|
1040
|
+
try {
|
|
1041
|
+
const { value, done } = await iterator.next();
|
|
1042
|
+
if (done) {
|
|
1043
|
+
asyncPayloadRecord.setIsCompletedIterator();
|
|
1044
|
+
return { done, value: undefined };
|
|
1045
|
+
}
|
|
1046
|
+
item = value;
|
|
1047
|
+
}
|
|
1048
|
+
catch (rawError) {
|
|
1049
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
1050
|
+
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
|
|
1051
|
+
// don't continue if iterator throws
|
|
1052
|
+
return { done: true, value };
|
|
1053
|
+
}
|
|
1054
|
+
let completedItem;
|
|
1055
|
+
try {
|
|
1056
|
+
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord);
|
|
1057
|
+
if ((0, utils_1.isPromise)(completedItem)) {
|
|
1058
|
+
completedItem = completedItem.then(undefined, rawError => {
|
|
1059
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
1060
|
+
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors);
|
|
1061
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
1062
|
+
return handledError;
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return { done: false, value: completedItem };
|
|
1066
|
+
}
|
|
1067
|
+
catch (rawError) {
|
|
1068
|
+
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
|
|
1069
|
+
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors);
|
|
1070
|
+
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord);
|
|
1071
|
+
return { done: false, value };
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNodes, info, itemType, path, label, parentContext) {
|
|
1075
|
+
let index = initialIndex;
|
|
1076
|
+
let previousAsyncPayloadRecord = parentContext !== null && parentContext !== void 0 ? parentContext : undefined;
|
|
1077
|
+
while (true) {
|
|
1078
|
+
const itemPath = (0, utils_1.addPath)(path, index, undefined);
|
|
1079
|
+
const asyncPayloadRecord = new StreamRecord({
|
|
1080
|
+
label,
|
|
1081
|
+
path: itemPath,
|
|
1082
|
+
parentContext: previousAsyncPayloadRecord,
|
|
1083
|
+
iterator,
|
|
1084
|
+
exeContext,
|
|
1085
|
+
});
|
|
1086
|
+
let iteration;
|
|
1087
|
+
try {
|
|
1088
|
+
iteration = await executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath);
|
|
1089
|
+
}
|
|
1090
|
+
catch (error) {
|
|
1091
|
+
asyncPayloadRecord.errors.push(error);
|
|
1092
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
1093
|
+
asyncPayloadRecord.addItems(null);
|
|
1094
|
+
// entire stream has errored and bubbled upwards
|
|
1095
|
+
if (iterator === null || iterator === void 0 ? void 0 : iterator.return) {
|
|
1096
|
+
iterator.return().catch(() => {
|
|
1097
|
+
// ignore errors
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const { done, value: completedItem } = iteration;
|
|
1103
|
+
let completedItems;
|
|
1104
|
+
if ((0, utils_1.isPromise)(completedItem)) {
|
|
1105
|
+
completedItems = completedItem.then(value => [value], error => {
|
|
1106
|
+
asyncPayloadRecord.errors.push(error);
|
|
1107
|
+
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
|
|
1108
|
+
return null;
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
completedItems = [completedItem];
|
|
1113
|
+
}
|
|
1114
|
+
asyncPayloadRecord.addItems(completedItems);
|
|
1115
|
+
if (done) {
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
previousAsyncPayloadRecord = asyncPayloadRecord;
|
|
1119
|
+
index++;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) {
|
|
1123
|
+
const nullPathArray = (0, utils_1.pathToArray)(nullPath);
|
|
1124
|
+
exeContext.subsequentPayloads.forEach(asyncRecord => {
|
|
1125
|
+
var _a;
|
|
1126
|
+
if (asyncRecord === currentAsyncRecord) {
|
|
1127
|
+
// don't remove payload from where error originates
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
for (let i = 0; i < nullPathArray.length; i++) {
|
|
1131
|
+
if (asyncRecord.path[i] !== nullPathArray[i]) {
|
|
1132
|
+
// asyncRecord points to a path unaffected by this payload
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
// asyncRecord path points to nulled error field
|
|
1137
|
+
if (isStreamPayload(asyncRecord) && ((_a = asyncRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) {
|
|
1138
|
+
asyncRecord.iterator.return().catch(() => {
|
|
1139
|
+
// ignore error
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
exeContext.subsequentPayloads.delete(asyncRecord);
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
function getCompletedIncrementalResults(exeContext) {
|
|
1146
|
+
const incrementalResults = [];
|
|
1147
|
+
for (const asyncPayloadRecord of exeContext.subsequentPayloads) {
|
|
1148
|
+
const incrementalResult = {};
|
|
1149
|
+
if (!asyncPayloadRecord.isCompleted) {
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
exeContext.subsequentPayloads.delete(asyncPayloadRecord);
|
|
1153
|
+
if (isStreamPayload(asyncPayloadRecord)) {
|
|
1154
|
+
const items = asyncPayloadRecord.items;
|
|
1155
|
+
if (asyncPayloadRecord.isCompletedIterator) {
|
|
1156
|
+
// async iterable resolver just finished but there may be pending payloads
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
incrementalResult.items = items;
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
const data = asyncPayloadRecord.data;
|
|
1163
|
+
incrementalResult.data = data !== null && data !== void 0 ? data : null;
|
|
1164
|
+
}
|
|
1165
|
+
incrementalResult.path = asyncPayloadRecord.path;
|
|
1166
|
+
if (asyncPayloadRecord.label) {
|
|
1167
|
+
incrementalResult.label = asyncPayloadRecord.label;
|
|
1168
|
+
}
|
|
1169
|
+
if (asyncPayloadRecord.errors.length > 0) {
|
|
1170
|
+
incrementalResult.errors = asyncPayloadRecord.errors;
|
|
1171
|
+
}
|
|
1172
|
+
incrementalResults.push(incrementalResult);
|
|
1173
|
+
}
|
|
1174
|
+
return incrementalResults;
|
|
1175
|
+
}
|
|
1176
|
+
function yieldSubsequentPayloads(exeContext) {
|
|
1177
|
+
let isDone = false;
|
|
1178
|
+
async function next() {
|
|
1179
|
+
if (isDone) {
|
|
1180
|
+
return { value: undefined, done: true };
|
|
1181
|
+
}
|
|
1182
|
+
await Promise.race(Array.from(exeContext.subsequentPayloads).map(p => p.promise));
|
|
1183
|
+
if (isDone) {
|
|
1184
|
+
// a different call to next has exhausted all payloads
|
|
1185
|
+
return { value: undefined, done: true };
|
|
1186
|
+
}
|
|
1187
|
+
const incremental = getCompletedIncrementalResults(exeContext);
|
|
1188
|
+
const hasNext = exeContext.subsequentPayloads.size > 0;
|
|
1189
|
+
if (!incremental.length && hasNext) {
|
|
1190
|
+
return next();
|
|
1191
|
+
}
|
|
1192
|
+
if (!hasNext) {
|
|
1193
|
+
isDone = true;
|
|
1194
|
+
}
|
|
1195
|
+
return {
|
|
1196
|
+
value: incremental.length ? { incremental, hasNext } : { hasNext },
|
|
1197
|
+
done: false,
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
function returnStreamIterators() {
|
|
1201
|
+
const promises = [];
|
|
1202
|
+
exeContext.subsequentPayloads.forEach(asyncPayloadRecord => {
|
|
1203
|
+
var _a;
|
|
1204
|
+
if (isStreamPayload(asyncPayloadRecord) && ((_a = asyncPayloadRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) {
|
|
1205
|
+
promises.push(asyncPayloadRecord.iterator.return());
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
return Promise.all(promises);
|
|
1209
|
+
}
|
|
1210
|
+
return {
|
|
1211
|
+
[Symbol.asyncIterator]() {
|
|
1212
|
+
return this;
|
|
1213
|
+
},
|
|
1214
|
+
next,
|
|
1215
|
+
async return() {
|
|
1216
|
+
await returnStreamIterators();
|
|
1217
|
+
isDone = true;
|
|
1218
|
+
return { value: undefined, done: true };
|
|
1219
|
+
},
|
|
1220
|
+
async throw(error) {
|
|
1221
|
+
await returnStreamIterators();
|
|
1222
|
+
isDone = true;
|
|
1223
|
+
return Promise.reject(error);
|
|
1224
|
+
},
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
class DeferredFragmentRecord {
|
|
1228
|
+
constructor(opts) {
|
|
1229
|
+
this.type = 'defer';
|
|
1230
|
+
this.label = opts.label;
|
|
1231
|
+
this.path = (0, utils_1.pathToArray)(opts.path);
|
|
1232
|
+
this.parentContext = opts.parentContext;
|
|
1233
|
+
this.errors = [];
|
|
1234
|
+
this._exeContext = opts.exeContext;
|
|
1235
|
+
this._exeContext.subsequentPayloads.add(this);
|
|
1236
|
+
this.isCompleted = false;
|
|
1237
|
+
this.data = null;
|
|
1238
|
+
this.promise = new Promise(resolve => {
|
|
1239
|
+
this._resolve = MaybePromise => {
|
|
1240
|
+
resolve(MaybePromise);
|
|
1241
|
+
};
|
|
1242
|
+
}).then(data => {
|
|
1243
|
+
this.data = data;
|
|
1244
|
+
this.isCompleted = true;
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
addData(data) {
|
|
1248
|
+
var _a, _b, _c;
|
|
1249
|
+
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise;
|
|
1250
|
+
if (parentData) {
|
|
1251
|
+
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => data));
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, data);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
class StreamRecord {
|
|
1258
|
+
constructor(opts) {
|
|
1259
|
+
this.type = 'stream';
|
|
1260
|
+
this.items = null;
|
|
1261
|
+
this.label = opts.label;
|
|
1262
|
+
this.path = (0, utils_1.pathToArray)(opts.path);
|
|
1263
|
+
this.parentContext = opts.parentContext;
|
|
1264
|
+
this.iterator = opts.iterator;
|
|
1265
|
+
this.errors = [];
|
|
1266
|
+
this._exeContext = opts.exeContext;
|
|
1267
|
+
this._exeContext.subsequentPayloads.add(this);
|
|
1268
|
+
this.isCompleted = false;
|
|
1269
|
+
this.items = null;
|
|
1270
|
+
this.promise = new Promise(resolve => {
|
|
1271
|
+
this._resolve = MaybePromise => {
|
|
1272
|
+
resolve(MaybePromise);
|
|
1273
|
+
};
|
|
1274
|
+
}).then(items => {
|
|
1275
|
+
this.items = items;
|
|
1276
|
+
this.isCompleted = true;
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
addItems(items) {
|
|
1280
|
+
var _a, _b, _c;
|
|
1281
|
+
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise;
|
|
1282
|
+
if (parentData) {
|
|
1283
|
+
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => items));
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, items);
|
|
1287
|
+
}
|
|
1288
|
+
setIsCompletedIterator() {
|
|
1289
|
+
this.isCompletedIterator = true;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
function isStreamPayload(asyncPayload) {
|
|
1293
|
+
return asyncPayload.type === 'stream';
|
|
1294
|
+
}
|
|
750
1295
|
/**
|
|
751
1296
|
* This method looks up the field on the given type definition.
|
|
752
1297
|
* It has special casing for the three introspection fields,
|