@graphql-tools/executor
Advanced tools
Comparing version 0.0.3-alpha-20221031195219-a47ad94a to 0.0.3-alpha-20221031225249-642ec6e9
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
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; | ||
exports.getFieldDef = exports.createSourceEventStream = exports.subscribe = exports.defaultFieldResolver = exports.defaultTypeResolver = exports.buildResolveInfo = exports.buildExecutionContext = exports.assertValidExecutionArguments = exports.executeSync = exports.execute = void 0; | ||
const graphql_1 = require("graphql"); | ||
const utils_1 = require("@graphql-tools/utils"); | ||
const values_js_1 = require("./values.js"); | ||
const promiseForObject_js_1 = require("./promiseForObject.js"); | ||
const flattenAsyncIterable_js_1 = require("./flattenAsyncIterable.js"); | ||
const invariant_js_1 = require("./invariant.js"); | ||
/** | ||
* A memoized collection of relevant subfields with regard to the return | ||
* type. Memoizing ensures the subfields are not repeatedly calculated, which | ||
* saves overhead when resolving lists of values. | ||
*/ | ||
const collectSubfields = (0, utils_1.memoize3)((exeContext, returnType, fieldNodes) => (0, utils_1.collectSubFields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes)); | ||
const UNEXPECTED_MULTIPLE_PAYLOADS = 'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)'; | ||
/** | ||
* Implements the "Executing requests" section of the GraphQL specification. | ||
@@ -26,40 +16,4 @@ * | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, it will throw or resolve to an object containing an error instead. | ||
* Use `experimentalExecuteIncrementally` if you want to support incremental | ||
* delivery. | ||
*/ | ||
function execute(args) { | ||
const result = experimentalExecuteIncrementally(args); | ||
if (!(0, utils_1.isPromise)(result)) { | ||
if ('initialResult' in result) { | ||
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS); | ||
} | ||
return result; | ||
} | ||
return result.then(incrementalResult => { | ||
if ('initialResult' in incrementalResult) { | ||
return { | ||
errors: [(0, utils_1.createGraphQLError)(UNEXPECTED_MULTIPLE_PAYLOADS)], | ||
}; | ||
} | ||
return incrementalResult; | ||
}); | ||
} | ||
exports.execute = execute; | ||
/** | ||
* Implements the "Executing requests" section of the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* This function returns a Promise of an ExperimentalIncrementalExecutionResults | ||
* object. This object either consists of a single ExecutionResult, or an | ||
* object containing an `initialResult` and a stream of `subsequentResults`. | ||
* | ||
* If the arguments to this function do not result in a legal execution context, | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
*/ | ||
function experimentalExecuteIncrementally(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
@@ -74,3 +28,3 @@ // a "Response" with only errors is returned. | ||
} | ||
exports.experimentalExecuteIncrementally = experimentalExecuteIncrementally; | ||
exports.execute = execute; | ||
function executeImpl(exeContext) { | ||
@@ -91,15 +45,3 @@ // Return a Promise that will eventually resolve to the data described by | ||
if ((0, utils_1.isPromise)(result)) { | ||
return result.then(data => { | ||
const initialResult = buildResponse(data, exeContext.errors); | ||
if (exeContext.subsequentPayloads.size > 0) { | ||
return { | ||
initialResult: { | ||
...initialResult, | ||
hasNext: true, | ||
}, | ||
subsequentResults: yieldSubsequentPayloads(exeContext), | ||
}; | ||
} | ||
return initialResult; | ||
}, error => { | ||
return result.then(data => buildResponse(data, exeContext.errors), error => { | ||
exeContext.errors.push(error); | ||
@@ -109,13 +51,3 @@ return buildResponse(null, exeContext.errors); | ||
} | ||
const initialResult = buildResponse(result, exeContext.errors); | ||
if (exeContext.subsequentPayloads.size > 0) { | ||
return { | ||
initialResult: { | ||
...initialResult, | ||
hasNext: true, | ||
}, | ||
subsequentResults: yieldSubsequentPayloads(exeContext), | ||
}; | ||
} | ||
return initialResult; | ||
return buildResponse(result, exeContext.errors); | ||
} | ||
@@ -133,5 +65,5 @@ catch (error) { | ||
function executeSync(args) { | ||
const result = experimentalExecuteIncrementally(args); | ||
const result = execute(args); | ||
// Assert that the execution was synchronous. | ||
if ((0, utils_1.isPromise)(result) || 'initialResult' in result) { | ||
if ((0, utils_1.isPromise)(result)) { | ||
throw new Error('GraphQL execution failed to complete synchronously.'); | ||
@@ -224,3 +156,2 @@ } | ||
subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : exports.defaultFieldResolver, | ||
subsequentPayloads: new Set(), | ||
errors: [], | ||
@@ -234,3 +165,2 @@ }; | ||
rootValue: payload, | ||
subsequentPayloads: new Set(), | ||
errors: [], | ||
@@ -245,21 +175,15 @@ }; | ||
const rootType = (0, utils_1.getDefinedRootType)(schema, operation.operation, [operation]); | ||
if (rootType == null) { | ||
(0, utils_1.createGraphQLError)(`Schema is not configured to execute ${operation.operation} operation.`, { | ||
nodes: operation, | ||
}); | ||
} | ||
const { fields: rootFields, patches } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const rootFields = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const path = undefined; | ||
let result; | ||
if (operation.operation === 'mutation') { | ||
result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields); | ||
switch (operation.operation) { | ||
case 'query': | ||
return executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
case 'mutation': | ||
return executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields); | ||
case 'subscription': | ||
// TODO: deprecate `subscribe` and move all logic here | ||
// Temporary solution until we finish merging execute and subscribe together | ||
return executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
} | ||
else { | ||
result = executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
} | ||
for (const patch of patches) { | ||
const { label, fields: patchFields } = patch; | ||
executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path); | ||
} | ||
return result; | ||
throw new Error(`Can only execute queries, mutations and subscriptions, got "${operation.operation}".`); | ||
} | ||
@@ -271,3 +195,3 @@ /** | ||
function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) { | ||
return (0, utils_1.promiseReduce)(fields, (results, [responseName, fieldNodes]) => { | ||
return (0, utils_1.promiseReduce)(fields.entries(), (results, [responseName, fieldNodes]) => { | ||
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name); | ||
@@ -292,26 +216,15 @@ const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); | ||
*/ | ||
function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) { | ||
function executeFields(exeContext, parentType, sourceValue, path, fields) { | ||
const results = Object.create(null); | ||
let containsPromise = false; | ||
try { | ||
for (const [responseName, fieldNodes] of fields) { | ||
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name); | ||
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath, asyncPayloadRecord); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if ((0, utils_1.isPromise)(result)) { | ||
containsPromise = true; | ||
} | ||
for (const [responseName, fieldNodes] of fields.entries()) { | ||
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name); | ||
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if ((0, utils_1.isPromise)(result)) { | ||
containsPromise = true; | ||
} | ||
} | ||
} | ||
catch (error) { | ||
if (containsPromise) { | ||
// Ensure that any promises returned by other fields are handled, as they may also reject. | ||
return (0, promiseForObject_js_1.promiseForObject)(results).finally(() => { | ||
throw error; | ||
}); | ||
} | ||
throw error; | ||
} | ||
// If there are no promises, we can just return the object | ||
@@ -324,3 +237,9 @@ if (!containsPromise) { | ||
// same map, but with any promises replaced with the values they resolved to. | ||
return (0, promiseForObject_js_1.promiseForObject)(results); | ||
return Promise.all(Object.values(results)).then(resolvedValues => { | ||
const resolvedObject = Object.create(null); | ||
for (const [i, key] of Object.keys(results).entries()) { | ||
resolvedObject[key] = resolvedValues[i]; | ||
} | ||
return resolvedObject; | ||
}); | ||
} | ||
@@ -333,5 +252,4 @@ /** | ||
*/ | ||
function executeField(exeContext, parentType, source, fieldNodes, path, asyncPayloadRecord) { | ||
var _a, _b; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
function executeField(exeContext, parentType, source, fieldNodes, path) { | ||
var _a; | ||
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); | ||
@@ -342,3 +260,3 @@ if (!fieldDef) { | ||
const returnType = fieldDef.type; | ||
const resolveFn = (_b = fieldDef.resolve) !== null && _b !== void 0 ? _b : exeContext.fieldResolver; | ||
const resolveFn = (_a = fieldDef.resolve) !== null && _a !== void 0 ? _a : exeContext.fieldResolver; | ||
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path); | ||
@@ -358,6 +276,6 @@ // Get the resolve function, regardless of if its result is normal or abrupt (error). | ||
if ((0, utils_1.isPromise)(result)) { | ||
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved, asyncPayloadRecord)); | ||
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved)); | ||
} | ||
else { | ||
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -369,5 +287,3 @@ if ((0, utils_1.isPromise)(completed)) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path)); | ||
const handledError = handleFieldError(error, returnType, errors); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return handledError; | ||
return handleFieldError(error, returnType, exeContext); | ||
}); | ||
@@ -379,5 +295,3 @@ } | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path)); | ||
const handledError = handleFieldError(error, returnType, errors); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return handledError; | ||
return handleFieldError(error, returnType, exeContext); | ||
} | ||
@@ -406,3 +320,3 @@ } | ||
exports.buildResolveInfo = buildResolveInfo; | ||
function handleFieldError(error, returnType, errors) { | ||
function handleFieldError(error, returnType, exeContext) { | ||
// If the field type is non-nullable, then it is resolved without any | ||
@@ -415,3 +329,3 @@ // protection from errors, however it still properly locates the error. | ||
// a null value for this field if one is encountered. | ||
errors.push(error); | ||
exeContext.errors.push(error); | ||
return null; | ||
@@ -440,3 +354,3 @@ } | ||
*/ | ||
function completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
// If result is an Error, throw a located error. | ||
@@ -449,3 +363,3 @@ if (result instanceof Error) { | ||
if ((0, graphql_1.isNonNullType)(returnType)) { | ||
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result); | ||
if (completed === null) { | ||
@@ -462,3 +376,3 @@ throw new Error(`Cannot return null for non-nullable field ${info.parentType.name}.${info.fieldName}.`); | ||
if ((0, graphql_1.isListType)(returnType)) { | ||
return completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeListValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -473,7 +387,7 @@ // If field type is a leaf type, Scalar or Enum, serialize to a valid value, | ||
if ((0, graphql_1.isAbstractType)(returnType)) { | ||
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
// If field type is Object, execute and complete all sub-selections. | ||
if ((0, graphql_1.isObjectType)(returnType)) { | ||
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -485,35 +399,6 @@ /* c8 ignore next 6 */ | ||
/** | ||
* Returns an object containing the `@stream` arguments if a field should be | ||
* streamed based on the experimental flag, stream directive present and | ||
* not disabled by the "if" argument. | ||
*/ | ||
function getStreamValues(exeContext, fieldNodes, path) { | ||
// do not stream inner lists of multi-dimensional lists | ||
if (typeof path.key === 'number') { | ||
return; | ||
} | ||
// validation only allows equivalent streams on multiple fields, so it is | ||
// safe to only check the first fieldNode for the stream directive | ||
const stream = (0, graphql_1.getDirectiveValues)(utils_1.GraphQLStreamDirective, fieldNodes[0], exeContext.variableValues); | ||
if (!stream) { | ||
return; | ||
} | ||
if (stream['if'] === false) { | ||
return; | ||
} | ||
(0, invariant_js_1.invariant)(typeof stream['initialCount'] === 'number', 'initialCount must be a number'); | ||
(0, invariant_js_1.invariant)(stream['initialCount'] >= 0, 'initialCount must be a positive integer'); | ||
return { | ||
initialCount: stream['initialCount'], | ||
label: typeof stream['label'] === 'string' ? stream['label'] : undefined, | ||
}; | ||
} | ||
/** | ||
* Complete a async iterator value by completing the result and calling | ||
* recursively until all the results are completed. | ||
*/ | ||
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord) { | ||
var _a; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
const stream = getStreamValues(exeContext, fieldNodes, path); | ||
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator) { | ||
let containsPromise = false; | ||
@@ -523,22 +408,28 @@ const completedResults = []; | ||
while (true) { | ||
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) { | ||
executeStreamIterator(index, iterator, exeContext, fieldNodes, info, itemType, path, stream.label, asyncPayloadRecord); | ||
break; | ||
} | ||
const itemPath = (0, utils_1.addPath)(path, index, undefined); | ||
let iteration; | ||
const fieldPath = (0, utils_1.addPath)(path, index, undefined); | ||
try { | ||
iteration = await iterator.next(); | ||
if (iteration.done) { | ||
const { value, done } = await iterator.next(); | ||
if (done) { | ||
break; | ||
} | ||
try { | ||
// TODO can the error checking logic be consolidated with completeListValue? | ||
const completedItem = completeValue(exeContext, itemType, fieldNodes, info, fieldPath, value); | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
containsPromise = true; | ||
} | ||
completedResults.push(completedItem); | ||
} | ||
catch (rawError) { | ||
completedResults.push(null); | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(fieldPath)); | ||
handleFieldError(error, itemType, exeContext); | ||
} | ||
} | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
completedResults.push(handleFieldError(error, itemType, errors)); | ||
completedResults.push(null); | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(fieldPath)); | ||
handleFieldError(error, itemType, exeContext); | ||
break; | ||
} | ||
if (completeListItemValue(iteration.value, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) { | ||
containsPromise = true; | ||
} | ||
index += 1; | ||
@@ -552,9 +443,7 @@ } | ||
*/ | ||
function completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
var _a; | ||
function completeListValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
const itemType = returnType.ofType; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
if ((0, utils_1.isAsyncIterable)(result)) { | ||
const iterator = result[Symbol.asyncIterator](); | ||
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord); | ||
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator); | ||
} | ||
@@ -564,61 +453,36 @@ if (!(0, utils_1.isIterableObject)(result)) { | ||
} | ||
const stream = getStreamValues(exeContext, fieldNodes, path); | ||
// This is specified as a simple map, however we're optimizing the path | ||
// where the list contains no Promises by avoiding creating another Promise. | ||
let containsPromise = false; | ||
let previousAsyncPayloadRecord = asyncPayloadRecord; | ||
const completedResults = []; | ||
let index = 0; | ||
for (const item of result) { | ||
const completedResults = Array.from(result, (item, index) => { | ||
// No need to modify the info object containing the path, | ||
// since from here on it is not ever accessed by resolver functions. | ||
const itemPath = (0, utils_1.addPath)(path, index, undefined); | ||
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) { | ||
previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord); | ||
index++; | ||
continue; | ||
try { | ||
let completedItem; | ||
if ((0, utils_1.isPromise)(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item); | ||
} | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
containsPromise = true; | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
return completedItem.then(undefined, rawError => { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
return handleFieldError(error, itemType, exeContext); | ||
}); | ||
} | ||
return completedItem; | ||
} | ||
if (completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) { | ||
containsPromise = true; | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
return handleFieldError(error, itemType, exeContext); | ||
} | ||
index++; | ||
} | ||
}); | ||
return containsPromise ? Promise.all(completedResults) : completedResults; | ||
} | ||
/** | ||
* Complete a list item value by adding it to the completed results. | ||
* | ||
* Returns true if the value is a Promise. | ||
*/ | ||
function completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord) { | ||
try { | ||
let completedItem; | ||
if ((0, utils_1.isPromise)(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
} | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
completedResults.push(completedItem.then(undefined, rawError => { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const handledError = handleFieldError(error, itemType, errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
})); | ||
return true; | ||
} | ||
completedResults.push(completedItem); | ||
} | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const handledError = handleFieldError(error, itemType, errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
completedResults.push(handledError); | ||
} | ||
return false; | ||
} | ||
/** | ||
* Complete a Scalar or Enum by serializing to a valid value, returning | ||
@@ -639,3 +503,3 @@ * null if serialization is not possible. | ||
*/ | ||
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
var _a; | ||
@@ -646,5 +510,5 @@ const resolveTypeFn = (_a = returnType.resolveType) !== null && _a !== void 0 ? _a : exeContext.typeResolver; | ||
if ((0, utils_1.isPromise)(runtimeType)) { | ||
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord)); | ||
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result)); | ||
} | ||
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result); | ||
} | ||
@@ -679,3 +543,5 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) { | ||
*/ | ||
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
// Collect sub-fields to execute to complete this value. | ||
const subFieldNodes = (0, utils_1.collectSubFields)(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes); | ||
// If there is an isTypeOf predicate function, call it with the | ||
@@ -691,3 +557,3 @@ // current result. If isTypeOf returns false, then raise an error rather | ||
} | ||
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord); | ||
return executeFields(exeContext, returnType, result, path, subFieldNodes); | ||
}); | ||
@@ -699,3 +565,3 @@ } | ||
} | ||
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord); | ||
return executeFields(exeContext, returnType, result, path, subFieldNodes); | ||
} | ||
@@ -707,12 +573,2 @@ function invalidReturnTypeError(returnType, result, fieldNodes) { | ||
} | ||
function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord) { | ||
// Collect sub-fields to execute to complete this value. | ||
const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes); | ||
const subFields = executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord); | ||
for (const subPatch of subPatches) { | ||
const { label, fields: subPatchFieldNodes } = subPatch; | ||
executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord); | ||
} | ||
return subFields; | ||
} | ||
/** | ||
@@ -785,7 +641,7 @@ * If a resolveType function is not given, then a default resolve behavior is | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with | ||
* descriptive errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* If the source stream could not be created due to faulty subscription | ||
* resolver logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
@@ -796,64 +652,5 @@ * | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, each `InitialIncrementalExecutionResult` and | ||
* `SubsequentIncrementalExecutionResult` in the result stream will be replaced | ||
* with an `ExecutionResult` with a single error stating that defer/stream is | ||
* not supported. Use `experimentalSubscribeIncrementally` if you want to | ||
* support incremental delivery. | ||
* | ||
* Accepts an object with named arguments. | ||
* Accepts either an object with named arguments, or individual arguments. | ||
*/ | ||
function subscribe(args) { | ||
const maybePromise = experimentalSubscribeIncrementally(args); | ||
if ((0, utils_1.isPromise)(maybePromise)) { | ||
return maybePromise.then(resultOrIterable => (0, utils_1.isAsyncIterable)(resultOrIterable) | ||
? (0, utils_1.mapAsyncIterator)(resultOrIterable, ensureSingleExecutionResult) | ||
: resultOrIterable); | ||
} | ||
return (0, utils_1.isAsyncIterable)(maybePromise) ? (0, utils_1.mapAsyncIterator)(maybePromise, ensureSingleExecutionResult) : maybePromise; | ||
} | ||
exports.subscribe = subscribe; | ||
function ensureSingleExecutionResult(result) { | ||
if ('hasNext' in result) { | ||
return { | ||
errors: [(0, utils_1.createGraphQLError)(UNEXPECTED_MULTIPLE_PAYLOADS)], | ||
}; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Implements the "Subscribe" algorithm described in the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* Returns a Promise which resolves to either an AsyncIterator (if successful) | ||
* or an ExecutionResult (error). The promise will be rejected if the schema or | ||
* other arguments to this function are invalid, or if the resolved event stream | ||
* is not an async iterable. | ||
* | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
* | ||
* If the operation succeeded, the promise resolves to an AsyncIterator, which | ||
* yields a stream of result representing the response stream. | ||
* | ||
* Each result may be an ExecutionResult with no `hasNext` (if executing the | ||
* event did not use `@defer` or `@stream`), or an | ||
* `InitialIncrementalExecutionResult` or `SubsequentIncrementalExecutionResult` | ||
* (if executing the event used `@defer` or `@stream`). In the case of | ||
* incremental execution results, each event produces a single | ||
* `InitialIncrementalExecutionResult` followed by one or more | ||
* `SubsequentIncrementalExecutionResult`s; all but the last have `hasNext: true`, | ||
* and the last has `hasNext: false`. There is no interleaving between results | ||
* generated from the same original event. | ||
* | ||
* Accepts an object with named arguments. | ||
*/ | ||
function experimentalSubscribeIncrementally(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
@@ -872,12 +669,3 @@ // a "Response" with only errors is returned. | ||
} | ||
exports.experimentalSubscribeIncrementally = experimentalSubscribeIncrementally; | ||
async function* ensureAsyncIterable(someExecutionResult) { | ||
if ('initialResult' in someExecutionResult) { | ||
yield someExecutionResult.initialResult; | ||
yield* someExecutionResult.subsequentResults; | ||
} | ||
else { | ||
yield someExecutionResult; | ||
} | ||
} | ||
exports.subscribe = subscribe; | ||
function mapSourceToResponse(exeContext, resultOrStream) { | ||
@@ -893,3 +681,3 @@ if (!(0, utils_1.isAsyncIterable)(resultOrStream)) { | ||
// "ExecuteQuery" algorithm, for which `execute` is also used. | ||
return (0, flattenAsyncIterable_js_1.flattenAsyncIterable)((0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload))))); | ||
return (0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), (payload) => executeImpl(buildPerEventExecutionContext(exeContext, payload))); | ||
} | ||
@@ -954,3 +742,3 @@ /** | ||
} | ||
const { fields: rootFields } = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const rootFields = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const [responseName, fieldNodes] = [...rootFields.entries()][0]; | ||
@@ -999,335 +787,2 @@ const fieldName = fieldNodes[0].name.value; | ||
} | ||
function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) { | ||
const asyncPayloadRecord = new DeferredFragmentRecord({ | ||
label, | ||
path, | ||
parentContext, | ||
exeContext, | ||
}); | ||
let promiseOrData; | ||
try { | ||
promiseOrData = executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord); | ||
if ((0, utils_1.isPromise)(promiseOrData)) { | ||
promiseOrData = promiseOrData.then(null, e => { | ||
asyncPayloadRecord.errors.push(e); | ||
return null; | ||
}); | ||
} | ||
} | ||
catch (e) { | ||
asyncPayloadRecord.errors.push(e); | ||
promiseOrData = null; | ||
} | ||
asyncPayloadRecord.addData(promiseOrData); | ||
} | ||
function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext) { | ||
const asyncPayloadRecord = new StreamRecord({ | ||
label, | ||
path: itemPath, | ||
parentContext, | ||
exeContext, | ||
}); | ||
let completedItem; | ||
try { | ||
try { | ||
if ((0, utils_1.isPromise)(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
} | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
completedItem = completedItem.then(undefined, rawError => { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
}); | ||
} | ||
} | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
completedItem = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
} | ||
} | ||
catch (error) { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
asyncPayloadRecord.addItems(null); | ||
return asyncPayloadRecord; | ||
} | ||
let completedItems; | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
completedItems = completedItem.then(value => [value], error => { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return null; | ||
}); | ||
} | ||
else { | ||
completedItems = [completedItem]; | ||
} | ||
asyncPayloadRecord.addItems(completedItems); | ||
return asyncPayloadRecord; | ||
} | ||
async function executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath) { | ||
let item; | ||
try { | ||
const { value, done } = await iterator.next(); | ||
if (done) { | ||
asyncPayloadRecord.setIsCompletedIterator(); | ||
return { done, value: undefined }; | ||
} | ||
item = value; | ||
} | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
// don't continue if iterator throws | ||
return { done: true, value }; | ||
} | ||
let completedItem; | ||
try { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
completedItem = completedItem.then(undefined, rawError => { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
}); | ||
} | ||
return { done: false, value: completedItem }; | ||
} | ||
catch (rawError) { | ||
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath)); | ||
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return { done: false, value }; | ||
} | ||
} | ||
async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNodes, info, itemType, path, label, parentContext) { | ||
let index = initialIndex; | ||
let previousAsyncPayloadRecord = parentContext !== null && parentContext !== void 0 ? parentContext : undefined; | ||
while (true) { | ||
const itemPath = (0, utils_1.addPath)(path, index, undefined); | ||
const asyncPayloadRecord = new StreamRecord({ | ||
label, | ||
path: itemPath, | ||
parentContext: previousAsyncPayloadRecord, | ||
iterator, | ||
exeContext, | ||
}); | ||
let iteration; | ||
try { | ||
iteration = await executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath); | ||
} | ||
catch (error) { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
asyncPayloadRecord.addItems(null); | ||
// entire stream has errored and bubbled upwards | ||
if (iterator === null || iterator === void 0 ? void 0 : iterator.return) { | ||
iterator.return().catch(() => { | ||
// ignore errors | ||
}); | ||
} | ||
return; | ||
} | ||
const { done, value: completedItem } = iteration; | ||
let completedItems; | ||
if ((0, utils_1.isPromise)(completedItem)) { | ||
completedItems = completedItem.then(value => [value], error => { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return null; | ||
}); | ||
} | ||
else { | ||
completedItems = [completedItem]; | ||
} | ||
asyncPayloadRecord.addItems(completedItems); | ||
if (done) { | ||
break; | ||
} | ||
previousAsyncPayloadRecord = asyncPayloadRecord; | ||
index++; | ||
} | ||
} | ||
function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) { | ||
const nullPathArray = (0, utils_1.pathToArray)(nullPath); | ||
exeContext.subsequentPayloads.forEach(asyncRecord => { | ||
var _a; | ||
if (asyncRecord === currentAsyncRecord) { | ||
// don't remove payload from where error originates | ||
return; | ||
} | ||
for (let i = 0; i < nullPathArray.length; i++) { | ||
if (asyncRecord.path[i] !== nullPathArray[i]) { | ||
// asyncRecord points to a path unaffected by this payload | ||
return; | ||
} | ||
} | ||
// asyncRecord path points to nulled error field | ||
if (isStreamPayload(asyncRecord) && ((_a = asyncRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) { | ||
asyncRecord.iterator.return().catch(() => { | ||
// ignore error | ||
}); | ||
} | ||
exeContext.subsequentPayloads.delete(asyncRecord); | ||
}); | ||
} | ||
function getCompletedIncrementalResults(exeContext) { | ||
const incrementalResults = []; | ||
for (const asyncPayloadRecord of exeContext.subsequentPayloads) { | ||
const incrementalResult = {}; | ||
if (!asyncPayloadRecord.isCompleted) { | ||
continue; | ||
} | ||
exeContext.subsequentPayloads.delete(asyncPayloadRecord); | ||
if (isStreamPayload(asyncPayloadRecord)) { | ||
const items = asyncPayloadRecord.items; | ||
if (asyncPayloadRecord.isCompletedIterator) { | ||
// async iterable resolver just finished but there may be pending payloads | ||
continue; | ||
} | ||
incrementalResult.items = items; | ||
} | ||
else { | ||
const data = asyncPayloadRecord.data; | ||
incrementalResult.data = data !== null && data !== void 0 ? data : null; | ||
} | ||
incrementalResult.path = asyncPayloadRecord.path; | ||
if (asyncPayloadRecord.label) { | ||
incrementalResult.label = asyncPayloadRecord.label; | ||
} | ||
if (asyncPayloadRecord.errors.length > 0) { | ||
incrementalResult.errors = asyncPayloadRecord.errors; | ||
} | ||
incrementalResults.push(incrementalResult); | ||
} | ||
return incrementalResults; | ||
} | ||
function yieldSubsequentPayloads(exeContext) { | ||
let isDone = false; | ||
async function next() { | ||
if (isDone) { | ||
return { value: undefined, done: true }; | ||
} | ||
await Promise.race(Array.from(exeContext.subsequentPayloads).map(p => p.promise)); | ||
if (isDone) { | ||
// a different call to next has exhausted all payloads | ||
return { value: undefined, done: true }; | ||
} | ||
const incremental = getCompletedIncrementalResults(exeContext); | ||
const hasNext = exeContext.subsequentPayloads.size > 0; | ||
if (!incremental.length && hasNext) { | ||
return next(); | ||
} | ||
if (!hasNext) { | ||
isDone = true; | ||
} | ||
return { | ||
value: incremental.length ? { incremental, hasNext } : { hasNext }, | ||
done: false, | ||
}; | ||
} | ||
function returnStreamIterators() { | ||
const promises = []; | ||
exeContext.subsequentPayloads.forEach(asyncPayloadRecord => { | ||
var _a; | ||
if (isStreamPayload(asyncPayloadRecord) && ((_a = asyncPayloadRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) { | ||
promises.push(asyncPayloadRecord.iterator.return()); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
return { | ||
[Symbol.asyncIterator]() { | ||
return this; | ||
}, | ||
next, | ||
async return() { | ||
await returnStreamIterators(); | ||
isDone = true; | ||
return { value: undefined, done: true }; | ||
}, | ||
async throw(error) { | ||
await returnStreamIterators(); | ||
isDone = true; | ||
return Promise.reject(error); | ||
}, | ||
}; | ||
} | ||
class DeferredFragmentRecord { | ||
constructor(opts) { | ||
this.type = 'defer'; | ||
this.label = opts.label; | ||
this.path = (0, utils_1.pathToArray)(opts.path); | ||
this.parentContext = opts.parentContext; | ||
this.errors = []; | ||
this._exeContext = opts.exeContext; | ||
this._exeContext.subsequentPayloads.add(this); | ||
this.isCompleted = false; | ||
this.data = null; | ||
this.promise = new Promise(resolve => { | ||
this._resolve = MaybePromise => { | ||
resolve(MaybePromise); | ||
}; | ||
}).then(data => { | ||
this.data = data; | ||
this.isCompleted = true; | ||
}); | ||
} | ||
addData(data) { | ||
var _a, _b, _c; | ||
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise; | ||
if (parentData) { | ||
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => data)); | ||
return; | ||
} | ||
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, data); | ||
} | ||
} | ||
class StreamRecord { | ||
constructor(opts) { | ||
this.type = 'stream'; | ||
this.items = null; | ||
this.label = opts.label; | ||
this.path = (0, utils_1.pathToArray)(opts.path); | ||
this.parentContext = opts.parentContext; | ||
this.iterator = opts.iterator; | ||
this.errors = []; | ||
this._exeContext = opts.exeContext; | ||
this._exeContext.subsequentPayloads.add(this); | ||
this.isCompleted = false; | ||
this.items = null; | ||
this.promise = new Promise(resolve => { | ||
this._resolve = MaybePromise => { | ||
resolve(MaybePromise); | ||
}; | ||
}).then(items => { | ||
this.items = items; | ||
this.isCompleted = true; | ||
}); | ||
} | ||
addItems(items) { | ||
var _a, _b, _c; | ||
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise; | ||
if (parentData) { | ||
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => items)); | ||
return; | ||
} | ||
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, items); | ||
} | ||
setIsCompletedIterator() { | ||
this.isCompletedIterator = true; | ||
} | ||
} | ||
function isStreamPayload(asyncPayload) { | ||
return asyncPayload.type === 'stream'; | ||
} | ||
/** | ||
@@ -1334,0 +789,0 @@ * This method looks up the field on the given type definition. |
@@ -1,15 +0,5 @@ | ||
import { locatedError, Kind, isAbstractType, isLeafType, isListType, isNonNullType, isObjectType, assertValidSchema, getDirectiveValues, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql'; | ||
import { createGraphQLError, inspect, isAsyncIterable, isIterableObject, isObjectLike, isPromise, pathToArray, addPath, getArgumentValues, promiseReduce, memoize3, getDefinedRootType, mapAsyncIterator, GraphQLStreamDirective, collectFields, collectSubFields as _collectSubfields, } from '@graphql-tools/utils'; | ||
import { locatedError, Kind, isAbstractType, isLeafType, isListType, isNonNullType, isObjectType, assertValidSchema, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, } from 'graphql'; | ||
import { collectFields, collectSubFields, createGraphQLError, inspect, isAsyncIterable, isIterableObject, isObjectLike, isPromise, mapAsyncIterator, pathToArray, addPath, getArgumentValues, promiseReduce, getDefinedRootType, } from '@graphql-tools/utils'; | ||
import { getVariableValues } from './values.js'; | ||
import { promiseForObject } from './promiseForObject.js'; | ||
import { flattenAsyncIterable } from './flattenAsyncIterable.js'; | ||
import { invariant } from './invariant.js'; | ||
/** | ||
* A memoized collection of relevant subfields with regard to the return | ||
* type. Memoizing ensures the subfields are not repeatedly calculated, which | ||
* saves overhead when resolving lists of values. | ||
*/ | ||
const collectSubfields = memoize3((exeContext, returnType, fieldNodes) => _collectSubfields(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes)); | ||
const UNEXPECTED_MULTIPLE_PAYLOADS = 'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)'; | ||
/** | ||
* Implements the "Executing requests" section of the GraphQL specification. | ||
@@ -23,39 +13,4 @@ * | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, it will throw or resolve to an object containing an error instead. | ||
* Use `experimentalExecuteIncrementally` if you want to support incremental | ||
* delivery. | ||
*/ | ||
export function execute(args) { | ||
const result = experimentalExecuteIncrementally(args); | ||
if (!isPromise(result)) { | ||
if ('initialResult' in result) { | ||
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS); | ||
} | ||
return result; | ||
} | ||
return result.then(incrementalResult => { | ||
if ('initialResult' in incrementalResult) { | ||
return { | ||
errors: [createGraphQLError(UNEXPECTED_MULTIPLE_PAYLOADS)], | ||
}; | ||
} | ||
return incrementalResult; | ||
}); | ||
} | ||
/** | ||
* Implements the "Executing requests" section of the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* This function returns a Promise of an ExperimentalIncrementalExecutionResults | ||
* object. This object either consists of a single ExecutionResult, or an | ||
* object containing an `initialResult` and a stream of `subsequentResults`. | ||
* | ||
* If the arguments to this function do not result in a legal execution context, | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
*/ | ||
export function experimentalExecuteIncrementally(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
@@ -85,15 +40,3 @@ // a "Response" with only errors is returned. | ||
if (isPromise(result)) { | ||
return result.then(data => { | ||
const initialResult = buildResponse(data, exeContext.errors); | ||
if (exeContext.subsequentPayloads.size > 0) { | ||
return { | ||
initialResult: { | ||
...initialResult, | ||
hasNext: true, | ||
}, | ||
subsequentResults: yieldSubsequentPayloads(exeContext), | ||
}; | ||
} | ||
return initialResult; | ||
}, error => { | ||
return result.then(data => buildResponse(data, exeContext.errors), error => { | ||
exeContext.errors.push(error); | ||
@@ -103,13 +46,3 @@ return buildResponse(null, exeContext.errors); | ||
} | ||
const initialResult = buildResponse(result, exeContext.errors); | ||
if (exeContext.subsequentPayloads.size > 0) { | ||
return { | ||
initialResult: { | ||
...initialResult, | ||
hasNext: true, | ||
}, | ||
subsequentResults: yieldSubsequentPayloads(exeContext), | ||
}; | ||
} | ||
return initialResult; | ||
return buildResponse(result, exeContext.errors); | ||
} | ||
@@ -127,5 +60,5 @@ catch (error) { | ||
export function executeSync(args) { | ||
const result = experimentalExecuteIncrementally(args); | ||
const result = execute(args); | ||
// Assert that the execution was synchronous. | ||
if (isPromise(result) || 'initialResult' in result) { | ||
if (isPromise(result)) { | ||
throw new Error('GraphQL execution failed to complete synchronously.'); | ||
@@ -216,3 +149,2 @@ } | ||
subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : defaultFieldResolver, | ||
subsequentPayloads: new Set(), | ||
errors: [], | ||
@@ -225,3 +157,2 @@ }; | ||
rootValue: payload, | ||
subsequentPayloads: new Set(), | ||
errors: [], | ||
@@ -236,21 +167,15 @@ }; | ||
const rootType = getDefinedRootType(schema, operation.operation, [operation]); | ||
if (rootType == null) { | ||
createGraphQLError(`Schema is not configured to execute ${operation.operation} operation.`, { | ||
nodes: operation, | ||
}); | ||
} | ||
const { fields: rootFields, patches } = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const rootFields = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const path = undefined; | ||
let result; | ||
if (operation.operation === 'mutation') { | ||
result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields); | ||
switch (operation.operation) { | ||
case 'query': | ||
return executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
case 'mutation': | ||
return executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields); | ||
case 'subscription': | ||
// TODO: deprecate `subscribe` and move all logic here | ||
// Temporary solution until we finish merging execute and subscribe together | ||
return executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
} | ||
else { | ||
result = executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
} | ||
for (const patch of patches) { | ||
const { label, fields: patchFields } = patch; | ||
executeDeferredFragment(exeContext, rootType, rootValue, patchFields, label, path); | ||
} | ||
return result; | ||
throw new Error(`Can only execute queries, mutations and subscriptions, got "${operation.operation}".`); | ||
} | ||
@@ -262,3 +187,3 @@ /** | ||
function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) { | ||
return promiseReduce(fields, (results, [responseName, fieldNodes]) => { | ||
return promiseReduce(fields.entries(), (results, [responseName, fieldNodes]) => { | ||
const fieldPath = addPath(path, responseName, parentType.name); | ||
@@ -283,26 +208,15 @@ const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); | ||
*/ | ||
function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) { | ||
function executeFields(exeContext, parentType, sourceValue, path, fields) { | ||
const results = Object.create(null); | ||
let containsPromise = false; | ||
try { | ||
for (const [responseName, fieldNodes] of fields) { | ||
const fieldPath = addPath(path, responseName, parentType.name); | ||
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath, asyncPayloadRecord); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if (isPromise(result)) { | ||
containsPromise = true; | ||
} | ||
for (const [responseName, fieldNodes] of fields.entries()) { | ||
const fieldPath = addPath(path, responseName, parentType.name); | ||
const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if (isPromise(result)) { | ||
containsPromise = true; | ||
} | ||
} | ||
} | ||
catch (error) { | ||
if (containsPromise) { | ||
// Ensure that any promises returned by other fields are handled, as they may also reject. | ||
return promiseForObject(results).finally(() => { | ||
throw error; | ||
}); | ||
} | ||
throw error; | ||
} | ||
// If there are no promises, we can just return the object | ||
@@ -315,3 +229,9 @@ if (!containsPromise) { | ||
// same map, but with any promises replaced with the values they resolved to. | ||
return promiseForObject(results); | ||
return Promise.all(Object.values(results)).then(resolvedValues => { | ||
const resolvedObject = Object.create(null); | ||
for (const [i, key] of Object.keys(results).entries()) { | ||
resolvedObject[key] = resolvedValues[i]; | ||
} | ||
return resolvedObject; | ||
}); | ||
} | ||
@@ -324,5 +244,4 @@ /** | ||
*/ | ||
function executeField(exeContext, parentType, source, fieldNodes, path, asyncPayloadRecord) { | ||
var _a, _b; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
function executeField(exeContext, parentType, source, fieldNodes, path) { | ||
var _a; | ||
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); | ||
@@ -333,3 +252,3 @@ if (!fieldDef) { | ||
const returnType = fieldDef.type; | ||
const resolveFn = (_b = fieldDef.resolve) !== null && _b !== void 0 ? _b : exeContext.fieldResolver; | ||
const resolveFn = (_a = fieldDef.resolve) !== null && _a !== void 0 ? _a : exeContext.fieldResolver; | ||
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path); | ||
@@ -349,6 +268,6 @@ // Get the resolve function, regardless of if its result is normal or abrupt (error). | ||
if (isPromise(result)) { | ||
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved, asyncPayloadRecord)); | ||
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved)); | ||
} | ||
else { | ||
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -360,5 +279,3 @@ if (isPromise(completed)) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(path)); | ||
const handledError = handleFieldError(error, returnType, errors); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return handledError; | ||
return handleFieldError(error, returnType, exeContext); | ||
}); | ||
@@ -370,5 +287,3 @@ } | ||
const error = locatedError(rawError, fieldNodes, pathToArray(path)); | ||
const handledError = handleFieldError(error, returnType, errors); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return handledError; | ||
return handleFieldError(error, returnType, exeContext); | ||
} | ||
@@ -396,3 +311,3 @@ } | ||
} | ||
function handleFieldError(error, returnType, errors) { | ||
function handleFieldError(error, returnType, exeContext) { | ||
// If the field type is non-nullable, then it is resolved without any | ||
@@ -405,3 +320,3 @@ // protection from errors, however it still properly locates the error. | ||
// a null value for this field if one is encountered. | ||
errors.push(error); | ||
exeContext.errors.push(error); | ||
return null; | ||
@@ -430,3 +345,3 @@ } | ||
*/ | ||
function completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
// If result is an Error, throw a located error. | ||
@@ -439,3 +354,3 @@ if (result instanceof Error) { | ||
if (isNonNullType(returnType)) { | ||
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result); | ||
if (completed === null) { | ||
@@ -452,3 +367,3 @@ throw new Error(`Cannot return null for non-nullable field ${info.parentType.name}.${info.fieldName}.`); | ||
if (isListType(returnType)) { | ||
return completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeListValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -463,7 +378,7 @@ // If field type is a leaf type, Scalar or Enum, serialize to a valid value, | ||
if (isAbstractType(returnType)) { | ||
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
// If field type is Object, execute and complete all sub-selections. | ||
if (isObjectType(returnType)) { | ||
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result); | ||
} | ||
@@ -475,35 +390,6 @@ /* c8 ignore next 6 */ | ||
/** | ||
* Returns an object containing the `@stream` arguments if a field should be | ||
* streamed based on the experimental flag, stream directive present and | ||
* not disabled by the "if" argument. | ||
*/ | ||
function getStreamValues(exeContext, fieldNodes, path) { | ||
// do not stream inner lists of multi-dimensional lists | ||
if (typeof path.key === 'number') { | ||
return; | ||
} | ||
// validation only allows equivalent streams on multiple fields, so it is | ||
// safe to only check the first fieldNode for the stream directive | ||
const stream = getDirectiveValues(GraphQLStreamDirective, fieldNodes[0], exeContext.variableValues); | ||
if (!stream) { | ||
return; | ||
} | ||
if (stream['if'] === false) { | ||
return; | ||
} | ||
invariant(typeof stream['initialCount'] === 'number', 'initialCount must be a number'); | ||
invariant(stream['initialCount'] >= 0, 'initialCount must be a positive integer'); | ||
return { | ||
initialCount: stream['initialCount'], | ||
label: typeof stream['label'] === 'string' ? stream['label'] : undefined, | ||
}; | ||
} | ||
/** | ||
* Complete a async iterator value by completing the result and calling | ||
* recursively until all the results are completed. | ||
*/ | ||
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord) { | ||
var _a; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
const stream = getStreamValues(exeContext, fieldNodes, path); | ||
async function completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator) { | ||
let containsPromise = false; | ||
@@ -513,22 +399,28 @@ const completedResults = []; | ||
while (true) { | ||
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) { | ||
executeStreamIterator(index, iterator, exeContext, fieldNodes, info, itemType, path, stream.label, asyncPayloadRecord); | ||
break; | ||
} | ||
const itemPath = addPath(path, index, undefined); | ||
let iteration; | ||
const fieldPath = addPath(path, index, undefined); | ||
try { | ||
iteration = await iterator.next(); | ||
if (iteration.done) { | ||
const { value, done } = await iterator.next(); | ||
if (done) { | ||
break; | ||
} | ||
try { | ||
// TODO can the error checking logic be consolidated with completeListValue? | ||
const completedItem = completeValue(exeContext, itemType, fieldNodes, info, fieldPath, value); | ||
if (isPromise(completedItem)) { | ||
containsPromise = true; | ||
} | ||
completedResults.push(completedItem); | ||
} | ||
catch (rawError) { | ||
completedResults.push(null); | ||
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath)); | ||
handleFieldError(error, itemType, exeContext); | ||
} | ||
} | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
completedResults.push(handleFieldError(error, itemType, errors)); | ||
completedResults.push(null); | ||
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath)); | ||
handleFieldError(error, itemType, exeContext); | ||
break; | ||
} | ||
if (completeListItemValue(iteration.value, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) { | ||
containsPromise = true; | ||
} | ||
index += 1; | ||
@@ -542,9 +434,7 @@ } | ||
*/ | ||
function completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
var _a; | ||
function completeListValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
const itemType = returnType.ofType; | ||
const errors = (_a = asyncPayloadRecord === null || asyncPayloadRecord === void 0 ? void 0 : asyncPayloadRecord.errors) !== null && _a !== void 0 ? _a : exeContext.errors; | ||
if (isAsyncIterable(result)) { | ||
const iterator = result[Symbol.asyncIterator](); | ||
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord); | ||
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator); | ||
} | ||
@@ -554,61 +444,36 @@ if (!isIterableObject(result)) { | ||
} | ||
const stream = getStreamValues(exeContext, fieldNodes, path); | ||
// This is specified as a simple map, however we're optimizing the path | ||
// where the list contains no Promises by avoiding creating another Promise. | ||
let containsPromise = false; | ||
let previousAsyncPayloadRecord = asyncPayloadRecord; | ||
const completedResults = []; | ||
let index = 0; | ||
for (const item of result) { | ||
const completedResults = Array.from(result, (item, index) => { | ||
// No need to modify the info object containing the path, | ||
// since from here on it is not ever accessed by resolver functions. | ||
const itemPath = addPath(path, index, undefined); | ||
if (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) { | ||
previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord); | ||
index++; | ||
continue; | ||
try { | ||
let completedItem; | ||
if (isPromise(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item); | ||
} | ||
if (isPromise(completedItem)) { | ||
containsPromise = true; | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
return completedItem.then(undefined, rawError => { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
return handleFieldError(error, itemType, exeContext); | ||
}); | ||
} | ||
return completedItem; | ||
} | ||
if (completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) { | ||
containsPromise = true; | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
return handleFieldError(error, itemType, exeContext); | ||
} | ||
index++; | ||
} | ||
}); | ||
return containsPromise ? Promise.all(completedResults) : completedResults; | ||
} | ||
/** | ||
* Complete a list item value by adding it to the completed results. | ||
* | ||
* Returns true if the value is a Promise. | ||
*/ | ||
function completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord) { | ||
try { | ||
let completedItem; | ||
if (isPromise(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
} | ||
if (isPromise(completedItem)) { | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
completedResults.push(completedItem.then(undefined, rawError => { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const handledError = handleFieldError(error, itemType, errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
})); | ||
return true; | ||
} | ||
completedResults.push(completedItem); | ||
} | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const handledError = handleFieldError(error, itemType, errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
completedResults.push(handledError); | ||
} | ||
return false; | ||
} | ||
/** | ||
* Complete a Scalar or Enum by serializing to a valid value, returning | ||
@@ -629,3 +494,3 @@ * null if serialization is not possible. | ||
*/ | ||
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
var _a; | ||
@@ -636,5 +501,5 @@ const resolveTypeFn = (_a = returnType.resolveType) !== null && _a !== void 0 ? _a : exeContext.typeResolver; | ||
if (isPromise(runtimeType)) { | ||
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord)); | ||
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result)); | ||
} | ||
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord); | ||
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result); | ||
} | ||
@@ -669,3 +534,5 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) { | ||
*/ | ||
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) { | ||
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result) { | ||
// Collect sub-fields to execute to complete this value. | ||
const subFieldNodes = collectSubFields(exeContext.schema, exeContext.fragments, exeContext.variableValues, returnType, fieldNodes); | ||
// If there is an isTypeOf predicate function, call it with the | ||
@@ -681,3 +548,3 @@ // current result. If isTypeOf returns false, then raise an error rather | ||
} | ||
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord); | ||
return executeFields(exeContext, returnType, result, path, subFieldNodes); | ||
}); | ||
@@ -689,3 +556,3 @@ } | ||
} | ||
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord); | ||
return executeFields(exeContext, returnType, result, path, subFieldNodes); | ||
} | ||
@@ -697,12 +564,2 @@ function invalidReturnTypeError(returnType, result, fieldNodes) { | ||
} | ||
function collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord) { | ||
// Collect sub-fields to execute to complete this value. | ||
const { fields: subFieldNodes, patches: subPatches } = collectSubfields(exeContext, returnType, fieldNodes); | ||
const subFields = executeFields(exeContext, returnType, result, path, subFieldNodes, asyncPayloadRecord); | ||
for (const subPatch of subPatches) { | ||
const { label, fields: subPatchFieldNodes } = subPatch; | ||
executeDeferredFragment(exeContext, returnType, result, subPatchFieldNodes, label, path, asyncPayloadRecord); | ||
} | ||
return subFields; | ||
} | ||
/** | ||
@@ -773,7 +630,7 @@ * If a resolveType function is not given, then a default resolve behavior is | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with | ||
* descriptive errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* If the source stream could not be created due to faulty subscription | ||
* resolver logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
@@ -784,63 +641,5 @@ * | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, each `InitialIncrementalExecutionResult` and | ||
* `SubsequentIncrementalExecutionResult` in the result stream will be replaced | ||
* with an `ExecutionResult` with a single error stating that defer/stream is | ||
* not supported. Use `experimentalSubscribeIncrementally` if you want to | ||
* support incremental delivery. | ||
* | ||
* Accepts an object with named arguments. | ||
* Accepts either an object with named arguments, or individual arguments. | ||
*/ | ||
export function subscribe(args) { | ||
const maybePromise = experimentalSubscribeIncrementally(args); | ||
if (isPromise(maybePromise)) { | ||
return maybePromise.then(resultOrIterable => isAsyncIterable(resultOrIterable) | ||
? mapAsyncIterator(resultOrIterable, ensureSingleExecutionResult) | ||
: resultOrIterable); | ||
} | ||
return isAsyncIterable(maybePromise) ? mapAsyncIterator(maybePromise, ensureSingleExecutionResult) : maybePromise; | ||
} | ||
function ensureSingleExecutionResult(result) { | ||
if ('hasNext' in result) { | ||
return { | ||
errors: [createGraphQLError(UNEXPECTED_MULTIPLE_PAYLOADS)], | ||
}; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Implements the "Subscribe" algorithm described in the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* Returns a Promise which resolves to either an AsyncIterator (if successful) | ||
* or an ExecutionResult (error). The promise will be rejected if the schema or | ||
* other arguments to this function are invalid, or if the resolved event stream | ||
* is not an async iterable. | ||
* | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
* | ||
* If the operation succeeded, the promise resolves to an AsyncIterator, which | ||
* yields a stream of result representing the response stream. | ||
* | ||
* Each result may be an ExecutionResult with no `hasNext` (if executing the | ||
* event did not use `@defer` or `@stream`), or an | ||
* `InitialIncrementalExecutionResult` or `SubsequentIncrementalExecutionResult` | ||
* (if executing the event used `@defer` or `@stream`). In the case of | ||
* incremental execution results, each event produces a single | ||
* `InitialIncrementalExecutionResult` followed by one or more | ||
* `SubsequentIncrementalExecutionResult`s; all but the last have `hasNext: true`, | ||
* and the last has `hasNext: false`. There is no interleaving between results | ||
* generated from the same original event. | ||
* | ||
* Accepts an object with named arguments. | ||
*/ | ||
export function experimentalSubscribeIncrementally(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
@@ -859,11 +658,2 @@ // a "Response" with only errors is returned. | ||
} | ||
async function* ensureAsyncIterable(someExecutionResult) { | ||
if ('initialResult' in someExecutionResult) { | ||
yield someExecutionResult.initialResult; | ||
yield* someExecutionResult.subsequentResults; | ||
} | ||
else { | ||
yield someExecutionResult; | ||
} | ||
} | ||
function mapSourceToResponse(exeContext, resultOrStream) { | ||
@@ -879,3 +669,3 @@ if (!isAsyncIterable(resultOrStream)) { | ||
// "ExecuteQuery" algorithm, for which `execute` is also used. | ||
return flattenAsyncIterable(mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload))))); | ||
return mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), (payload) => executeImpl(buildPerEventExecutionContext(exeContext, payload))); | ||
} | ||
@@ -939,3 +729,3 @@ /** | ||
} | ||
const { fields: rootFields } = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const rootFields = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet); | ||
const [responseName, fieldNodes] = [...rootFields.entries()][0]; | ||
@@ -984,335 +774,2 @@ const fieldName = fieldNodes[0].name.value; | ||
} | ||
function executeDeferredFragment(exeContext, parentType, sourceValue, fields, label, path, parentContext) { | ||
const asyncPayloadRecord = new DeferredFragmentRecord({ | ||
label, | ||
path, | ||
parentContext, | ||
exeContext, | ||
}); | ||
let promiseOrData; | ||
try { | ||
promiseOrData = executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord); | ||
if (isPromise(promiseOrData)) { | ||
promiseOrData = promiseOrData.then(null, e => { | ||
asyncPayloadRecord.errors.push(e); | ||
return null; | ||
}); | ||
} | ||
} | ||
catch (e) { | ||
asyncPayloadRecord.errors.push(e); | ||
promiseOrData = null; | ||
} | ||
asyncPayloadRecord.addData(promiseOrData); | ||
} | ||
function executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, label, parentContext) { | ||
const asyncPayloadRecord = new StreamRecord({ | ||
label, | ||
path: itemPath, | ||
parentContext, | ||
exeContext, | ||
}); | ||
let completedItem; | ||
try { | ||
try { | ||
if (isPromise(item)) { | ||
completedItem = item.then(resolved => completeValue(exeContext, itemType, fieldNodes, info, itemPath, resolved, asyncPayloadRecord)); | ||
} | ||
else { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
} | ||
if (isPromise(completedItem)) { | ||
// Note: we don't rely on a `catch` method, but we do expect "thenable" | ||
// to take a second callback for the error case. | ||
completedItem = completedItem.then(undefined, rawError => { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
}); | ||
} | ||
} | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
completedItem = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
} | ||
} | ||
catch (error) { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
asyncPayloadRecord.addItems(null); | ||
return asyncPayloadRecord; | ||
} | ||
let completedItems; | ||
if (isPromise(completedItem)) { | ||
completedItems = completedItem.then(value => [value], error => { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return null; | ||
}); | ||
} | ||
else { | ||
completedItems = [completedItem]; | ||
} | ||
asyncPayloadRecord.addItems(completedItems); | ||
return asyncPayloadRecord; | ||
} | ||
async function executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath) { | ||
let item; | ||
try { | ||
const { value, done } = await iterator.next(); | ||
if (done) { | ||
asyncPayloadRecord.setIsCompletedIterator(); | ||
return { done, value: undefined }; | ||
} | ||
item = value; | ||
} | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
// don't continue if iterator throws | ||
return { done: true, value }; | ||
} | ||
let completedItem; | ||
try { | ||
completedItem = completeValue(exeContext, itemType, fieldNodes, info, itemPath, item, asyncPayloadRecord); | ||
if (isPromise(completedItem)) { | ||
completedItem = completedItem.then(undefined, rawError => { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const handledError = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return handledError; | ||
}); | ||
} | ||
return { done: false, value: completedItem }; | ||
} | ||
catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
filterSubsequentPayloads(exeContext, itemPath, asyncPayloadRecord); | ||
return { done: false, value }; | ||
} | ||
} | ||
async function executeStreamIterator(initialIndex, iterator, exeContext, fieldNodes, info, itemType, path, label, parentContext) { | ||
let index = initialIndex; | ||
let previousAsyncPayloadRecord = parentContext !== null && parentContext !== void 0 ? parentContext : undefined; | ||
while (true) { | ||
const itemPath = addPath(path, index, undefined); | ||
const asyncPayloadRecord = new StreamRecord({ | ||
label, | ||
path: itemPath, | ||
parentContext: previousAsyncPayloadRecord, | ||
iterator, | ||
exeContext, | ||
}); | ||
let iteration; | ||
try { | ||
iteration = await executeStreamIteratorItem(iterator, exeContext, fieldNodes, info, itemType, asyncPayloadRecord, itemPath); | ||
} | ||
catch (error) { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
asyncPayloadRecord.addItems(null); | ||
// entire stream has errored and bubbled upwards | ||
if (iterator === null || iterator === void 0 ? void 0 : iterator.return) { | ||
iterator.return().catch(() => { | ||
// ignore errors | ||
}); | ||
} | ||
return; | ||
} | ||
const { done, value: completedItem } = iteration; | ||
let completedItems; | ||
if (isPromise(completedItem)) { | ||
completedItems = completedItem.then(value => [value], error => { | ||
asyncPayloadRecord.errors.push(error); | ||
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord); | ||
return null; | ||
}); | ||
} | ||
else { | ||
completedItems = [completedItem]; | ||
} | ||
asyncPayloadRecord.addItems(completedItems); | ||
if (done) { | ||
break; | ||
} | ||
previousAsyncPayloadRecord = asyncPayloadRecord; | ||
index++; | ||
} | ||
} | ||
function filterSubsequentPayloads(exeContext, nullPath, currentAsyncRecord) { | ||
const nullPathArray = pathToArray(nullPath); | ||
exeContext.subsequentPayloads.forEach(asyncRecord => { | ||
var _a; | ||
if (asyncRecord === currentAsyncRecord) { | ||
// don't remove payload from where error originates | ||
return; | ||
} | ||
for (let i = 0; i < nullPathArray.length; i++) { | ||
if (asyncRecord.path[i] !== nullPathArray[i]) { | ||
// asyncRecord points to a path unaffected by this payload | ||
return; | ||
} | ||
} | ||
// asyncRecord path points to nulled error field | ||
if (isStreamPayload(asyncRecord) && ((_a = asyncRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) { | ||
asyncRecord.iterator.return().catch(() => { | ||
// ignore error | ||
}); | ||
} | ||
exeContext.subsequentPayloads.delete(asyncRecord); | ||
}); | ||
} | ||
function getCompletedIncrementalResults(exeContext) { | ||
const incrementalResults = []; | ||
for (const asyncPayloadRecord of exeContext.subsequentPayloads) { | ||
const incrementalResult = {}; | ||
if (!asyncPayloadRecord.isCompleted) { | ||
continue; | ||
} | ||
exeContext.subsequentPayloads.delete(asyncPayloadRecord); | ||
if (isStreamPayload(asyncPayloadRecord)) { | ||
const items = asyncPayloadRecord.items; | ||
if (asyncPayloadRecord.isCompletedIterator) { | ||
// async iterable resolver just finished but there may be pending payloads | ||
continue; | ||
} | ||
incrementalResult.items = items; | ||
} | ||
else { | ||
const data = asyncPayloadRecord.data; | ||
incrementalResult.data = data !== null && data !== void 0 ? data : null; | ||
} | ||
incrementalResult.path = asyncPayloadRecord.path; | ||
if (asyncPayloadRecord.label) { | ||
incrementalResult.label = asyncPayloadRecord.label; | ||
} | ||
if (asyncPayloadRecord.errors.length > 0) { | ||
incrementalResult.errors = asyncPayloadRecord.errors; | ||
} | ||
incrementalResults.push(incrementalResult); | ||
} | ||
return incrementalResults; | ||
} | ||
function yieldSubsequentPayloads(exeContext) { | ||
let isDone = false; | ||
async function next() { | ||
if (isDone) { | ||
return { value: undefined, done: true }; | ||
} | ||
await Promise.race(Array.from(exeContext.subsequentPayloads).map(p => p.promise)); | ||
if (isDone) { | ||
// a different call to next has exhausted all payloads | ||
return { value: undefined, done: true }; | ||
} | ||
const incremental = getCompletedIncrementalResults(exeContext); | ||
const hasNext = exeContext.subsequentPayloads.size > 0; | ||
if (!incremental.length && hasNext) { | ||
return next(); | ||
} | ||
if (!hasNext) { | ||
isDone = true; | ||
} | ||
return { | ||
value: incremental.length ? { incremental, hasNext } : { hasNext }, | ||
done: false, | ||
}; | ||
} | ||
function returnStreamIterators() { | ||
const promises = []; | ||
exeContext.subsequentPayloads.forEach(asyncPayloadRecord => { | ||
var _a; | ||
if (isStreamPayload(asyncPayloadRecord) && ((_a = asyncPayloadRecord.iterator) === null || _a === void 0 ? void 0 : _a.return)) { | ||
promises.push(asyncPayloadRecord.iterator.return()); | ||
} | ||
}); | ||
return Promise.all(promises); | ||
} | ||
return { | ||
[Symbol.asyncIterator]() { | ||
return this; | ||
}, | ||
next, | ||
async return() { | ||
await returnStreamIterators(); | ||
isDone = true; | ||
return { value: undefined, done: true }; | ||
}, | ||
async throw(error) { | ||
await returnStreamIterators(); | ||
isDone = true; | ||
return Promise.reject(error); | ||
}, | ||
}; | ||
} | ||
class DeferredFragmentRecord { | ||
constructor(opts) { | ||
this.type = 'defer'; | ||
this.label = opts.label; | ||
this.path = pathToArray(opts.path); | ||
this.parentContext = opts.parentContext; | ||
this.errors = []; | ||
this._exeContext = opts.exeContext; | ||
this._exeContext.subsequentPayloads.add(this); | ||
this.isCompleted = false; | ||
this.data = null; | ||
this.promise = new Promise(resolve => { | ||
this._resolve = MaybePromise => { | ||
resolve(MaybePromise); | ||
}; | ||
}).then(data => { | ||
this.data = data; | ||
this.isCompleted = true; | ||
}); | ||
} | ||
addData(data) { | ||
var _a, _b, _c; | ||
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise; | ||
if (parentData) { | ||
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => data)); | ||
return; | ||
} | ||
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, data); | ||
} | ||
} | ||
class StreamRecord { | ||
constructor(opts) { | ||
this.type = 'stream'; | ||
this.items = null; | ||
this.label = opts.label; | ||
this.path = pathToArray(opts.path); | ||
this.parentContext = opts.parentContext; | ||
this.iterator = opts.iterator; | ||
this.errors = []; | ||
this._exeContext = opts.exeContext; | ||
this._exeContext.subsequentPayloads.add(this); | ||
this.isCompleted = false; | ||
this.items = null; | ||
this.promise = new Promise(resolve => { | ||
this._resolve = MaybePromise => { | ||
resolve(MaybePromise); | ||
}; | ||
}).then(items => { | ||
this.items = items; | ||
this.isCompleted = true; | ||
}); | ||
} | ||
addItems(items) { | ||
var _a, _b, _c; | ||
const parentData = (_a = this.parentContext) === null || _a === void 0 ? void 0 : _a.promise; | ||
if (parentData) { | ||
(_b = this._resolve) === null || _b === void 0 ? void 0 : _b.call(this, parentData.then(() => items)); | ||
return; | ||
} | ||
(_c = this._resolve) === null || _c === void 0 ? void 0 : _c.call(this, items); | ||
} | ||
setIsCompletedIterator() { | ||
this.isCompletedIterator = true; | ||
} | ||
} | ||
function isStreamPayload(asyncPayload) { | ||
return asyncPayload.type === 'stream'; | ||
} | ||
/** | ||
@@ -1319,0 +776,0 @@ * This method looks up the field on the given type definition. |
{ | ||
"name": "@graphql-tools/executor", | ||
"version": "0.0.3-alpha-20221031195219-a47ad94a", | ||
"version": "0.0.3-alpha-20221031225249-642ec6e9", | ||
"sideEffects": false, | ||
@@ -9,3 +9,3 @@ "peerDependencies": { | ||
"dependencies": { | ||
"@graphql-tools/utils": "9.0.0-alpha-20221031195219-a47ad94a", | ||
"@graphql-tools/utils": "9.0.0-alpha-20221031225249-642ec6e9", | ||
"@graphql-typed-document-node/core": "3.1.1" | ||
@@ -12,0 +12,0 @@ }, |
@@ -1,4 +0,3 @@ | ||
import { GraphQLFormattedError, FieldNode, FragmentDefinitionNode, OperationDefinitionNode, GraphQLField, GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver, GraphQLSchema } from 'graphql'; | ||
import type { GraphQLError } from 'graphql'; | ||
import { Path, Maybe, MaybePromise, ExecutionResult } from '@graphql-tools/utils'; | ||
import { GraphQLFormattedError, GraphQLError, FieldNode, FragmentDefinitionNode, OperationDefinitionNode, GraphQLField, GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver, GraphQLSchema } from 'graphql'; | ||
import { Maybe, Path, MaybePromise, ExecutionResult } from '@graphql-tools/utils'; | ||
import { TypedDocumentNode } from '@graphql-typed-document-node/core'; | ||
@@ -30,3 +29,3 @@ /** | ||
*/ | ||
export interface ExecutionContext<TVariables = any, TContext = any> { | ||
export interface ExecutionContext<TContext = any> { | ||
schema: GraphQLSchema; | ||
@@ -37,3 +36,5 @@ fragments: Record<string, FragmentDefinitionNode>; | ||
operation: OperationDefinitionNode; | ||
variableValues: TVariables; | ||
variableValues: { | ||
[variable: string]: unknown; | ||
}; | ||
fieldResolver: GraphQLFieldResolver<any, TContext>; | ||
@@ -43,5 +44,4 @@ typeResolver: GraphQLTypeResolver<any, TContext>; | ||
errors: Array<GraphQLError>; | ||
subsequentPayloads: Set<AsyncPayloadRecord>; | ||
} | ||
export interface FormattedExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { | ||
export interface FormattedExecutionResult<TData = any, TExtensions = any> { | ||
errors?: ReadonlyArray<GraphQLFormattedError>; | ||
@@ -51,50 +51,2 @@ data?: TData | null; | ||
} | ||
export interface ExperimentalIncrementalExecutionResults<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { | ||
initialResult: InitialIncrementalExecutionResult<TData, TExtensions>; | ||
subsequentResults: AsyncGenerator<SubsequentIncrementalExecutionResult<TData, TExtensions>, void, void>; | ||
} | ||
export interface InitialIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends ExecutionResult<TData, TExtensions> { | ||
hasNext: boolean; | ||
incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>; | ||
extensions?: TExtensions; | ||
} | ||
export interface FormattedInitialIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends FormattedExecutionResult<TData, TExtensions> { | ||
hasNext: boolean; | ||
incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>; | ||
extensions?: TExtensions; | ||
} | ||
export interface SubsequentIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { | ||
hasNext: boolean; | ||
incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>; | ||
extensions?: TExtensions; | ||
} | ||
export interface FormattedSubsequentIncrementalExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> { | ||
hasNext: boolean; | ||
incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>; | ||
extensions?: TExtensions; | ||
} | ||
export interface IncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends ExecutionResult<TData, TExtensions> { | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
} | ||
export interface FormattedIncrementalDeferResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> extends FormattedExecutionResult<TData, TExtensions> { | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
} | ||
export interface IncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> { | ||
errors?: ReadonlyArray<GraphQLError>; | ||
items?: TData | null; | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
extensions?: TExtensions; | ||
} | ||
export interface FormattedIncrementalStreamResult<TData = Array<unknown>, TExtensions = Record<string, unknown>> { | ||
errors?: ReadonlyArray<GraphQLFormattedError>; | ||
items?: TData | null; | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
extensions?: TExtensions; | ||
} | ||
export declare type IncrementalResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> = IncrementalDeferResult<TData, TExtensions> | IncrementalStreamResult<TData, TExtensions>; | ||
export declare type FormattedIncrementalResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> = FormattedIncrementalDeferResult<TData, TExtensions> | FormattedIncrementalStreamResult<TData, TExtensions>; | ||
export interface ExecutionArgs<TData = any, TVariables = any, TContext = any> { | ||
@@ -120,24 +72,9 @@ schema: GraphQLSchema; | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, it will throw or resolve to an object containing an error instead. | ||
* Use `experimentalExecuteIncrementally` if you want to support incremental | ||
* delivery. | ||
*/ | ||
export declare function execute<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<ExecutionResult<TData>>; | ||
export declare function execute<TData = { | ||
[key: string]: any; | ||
}, TVariables = { | ||
[key: string]: any; | ||
}, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<ExecutionResult<TData>>; | ||
/** | ||
* Implements the "Executing requests" section of the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* This function returns a Promise of an ExperimentalIncrementalExecutionResults | ||
* object. This object either consists of a single ExecutionResult, or an | ||
* object containing an `initialResult` and a stream of `subsequentResults`. | ||
* | ||
* If the arguments to this function do not result in a legal execution context, | ||
* a GraphQLError will be thrown immediately explaining the invalid input. | ||
*/ | ||
export declare function experimentalExecuteIncrementally<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<ExecutionResult<TData> | ExperimentalIncrementalExecutionResults<TData>>; | ||
/** | ||
* Also implements the "Executing requests" section of the GraphQL specification. | ||
@@ -164,3 +101,7 @@ * However, it guarantees to complete synchronously (or throw an error) assuming | ||
*/ | ||
export declare function buildExecutionContext<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): ReadonlyArray<GraphQLError> | ExecutionContext; | ||
export declare function buildExecutionContext<TData = { | ||
[key: string]: any; | ||
}, TVariables = { | ||
[key: string]: any; | ||
}, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): ReadonlyArray<GraphQLError> | ExecutionContext; | ||
/** | ||
@@ -170,3 +111,3 @@ * TODO: consider no longer exporting this function | ||
*/ | ||
export declare function buildResolveInfo(exeContext: ExecutionContext, fieldDef: GraphQLField<unknown, unknown>, fieldNodes: Array<FieldNode>, parentType: GraphQLObjectType, path: Path): GraphQLResolveInfo; | ||
export declare function buildResolveInfo(exeContext: ExecutionContext, fieldDef: GraphQLField<unknown, unknown>, fieldNodes: ReadonlyArray<FieldNode>, parentType: GraphQLObjectType, path: Path): GraphQLResolveInfo; | ||
/** | ||
@@ -199,7 +140,7 @@ * If a resolveType function is not given, then a default resolve behavior is | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with | ||
* descriptive errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* If the source stream could not be created due to faulty subscription | ||
* resolver logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
@@ -210,48 +151,6 @@ * | ||
* | ||
* This function does not support incremental delivery (`@defer` and `@stream`). | ||
* If an operation which would defer or stream data is executed with this | ||
* function, each `InitialIncrementalExecutionResult` and | ||
* `SubsequentIncrementalExecutionResult` in the result stream will be replaced | ||
* with an `ExecutionResult` with a single error stating that defer/stream is | ||
* not supported. Use `experimentalSubscribeIncrementally` if you want to | ||
* support incremental delivery. | ||
* | ||
* Accepts an object with named arguments. | ||
* Accepts either an object with named arguments, or individual arguments. | ||
*/ | ||
export declare function subscribe(args: ExecutionArgs): MaybePromise<AsyncIterable<ExecutionResult> | ExecutionResult>; | ||
/** | ||
* Implements the "Subscribe" algorithm described in the GraphQL specification, | ||
* including `@defer` and `@stream` as proposed in | ||
* https://github.com/graphql/graphql-spec/pull/742 | ||
* | ||
* Returns a Promise which resolves to either an AsyncIterator (if successful) | ||
* or an ExecutionResult (error). The promise will be rejected if the schema or | ||
* other arguments to this function are invalid, or if the resolved event stream | ||
* is not an async iterable. | ||
* | ||
* If the client-provided arguments to this function do not result in a | ||
* compliant subscription, a GraphQL Response (ExecutionResult) with descriptive | ||
* errors and no data will be returned. | ||
* | ||
* If the source stream could not be created due to faulty subscription resolver | ||
* logic or underlying systems, the promise will resolve to a single | ||
* ExecutionResult containing `errors` and no `data`. | ||
* | ||
* If the operation succeeded, the promise resolves to an AsyncIterator, which | ||
* yields a stream of result representing the response stream. | ||
* | ||
* Each result may be an ExecutionResult with no `hasNext` (if executing the | ||
* event did not use `@defer` or `@stream`), or an | ||
* `InitialIncrementalExecutionResult` or `SubsequentIncrementalExecutionResult` | ||
* (if executing the event used `@defer` or `@stream`). In the case of | ||
* incremental execution results, each event produces a single | ||
* `InitialIncrementalExecutionResult` followed by one or more | ||
* `SubsequentIncrementalExecutionResult`s; all but the last have `hasNext: true`, | ||
* and the last has `hasNext: false`. There is no interleaving between results | ||
* generated from the same original event. | ||
* | ||
* Accepts an object with named arguments. | ||
*/ | ||
export declare function experimentalSubscribeIncrementally(args: ExecutionArgs): MaybePromise<AsyncGenerator<ExecutionResult | InitialIncrementalExecutionResult | SubsequentIncrementalExecutionResult, void, void> | ExecutionResult>; | ||
/** | ||
* Implements the "CreateSourceEventStream" algorithm described in the | ||
@@ -285,45 +184,2 @@ * GraphQL specification, resolving the subscription source event stream. | ||
export declare function createSourceEventStream(args: ExecutionArgs): MaybePromise<AsyncIterable<unknown> | ExecutionResult>; | ||
declare class DeferredFragmentRecord { | ||
type: 'defer'; | ||
errors: Array<GraphQLError>; | ||
label: string | undefined; | ||
path: Array<string | number>; | ||
promise: Promise<void>; | ||
data: Record<string, unknown> | null; | ||
parentContext: AsyncPayloadRecord | undefined; | ||
isCompleted: boolean; | ||
_exeContext: ExecutionContext; | ||
_resolve?: (arg: MaybePromise<Record<string, unknown> | null>) => void; | ||
constructor(opts: { | ||
label: string | undefined; | ||
path: Path | undefined; | ||
parentContext: AsyncPayloadRecord | undefined; | ||
exeContext: ExecutionContext; | ||
}); | ||
addData(data: MaybePromise<Record<string, unknown> | null>): void; | ||
} | ||
declare class StreamRecord { | ||
type: 'stream'; | ||
errors: Array<GraphQLError>; | ||
label: string | undefined; | ||
path: Array<string | number>; | ||
items: Array<unknown> | null; | ||
promise: Promise<void>; | ||
parentContext: AsyncPayloadRecord | undefined; | ||
iterator: AsyncIterator<unknown> | undefined; | ||
isCompletedIterator?: boolean; | ||
isCompleted: boolean; | ||
_exeContext: ExecutionContext; | ||
_resolve?: (arg: MaybePromise<Array<unknown> | null>) => void; | ||
constructor(opts: { | ||
label: string | undefined; | ||
path: Path | undefined; | ||
iterator?: AsyncIterator<unknown>; | ||
parentContext: AsyncPayloadRecord | undefined; | ||
exeContext: ExecutionContext; | ||
}); | ||
addItems(items: MaybePromise<Array<unknown> | null>): void; | ||
setIsCompletedIterator(): void; | ||
} | ||
declare type AsyncPayloadRecord = DeferredFragmentRecord | StreamRecord; | ||
/** | ||
@@ -341,2 +197,1 @@ * This method looks up the field on the given type definition. | ||
export declare function getFieldDef(schema: GraphQLSchema, parentType: GraphQLObjectType, fieldNode: FieldNode): Maybe<GraphQLField<unknown, unknown>>; | ||
export {}; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
122981
23
2291
+ Added@graphql-tools/utils@9.0.0-alpha-20221031225249-642ec6e9(transitive)
- Removed@graphql-tools/utils@9.0.0-alpha-20221031195219-a47ad94a(transitive)
Updated@graphql-tools/utils@9.0.0-alpha-20221031225249-642ec6e9