Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@graphql-tools/executor

Package Overview
Dependencies
Maintainers
3
Versions
403
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@graphql-tools/executor - npm Package Compare versions

Comparing version 0.0.2-alpha-20221027131523-6d0f4d51 to 0.0.2-alpha-20221029152711-14f4f7a7

cjs/directives/defer.js

763

cjs/execution/execute.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFieldDef = exports.createSourceEventStream = exports.subscribe = exports.defaultFieldResolver = exports.defaultTypeResolver = exports.buildResolveInfo = exports.buildExecutionContext = exports.assertValidExecutionArguments = exports.executeSync = exports.execute = void 0;
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;
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 collectFields_js_1 = require("./collectFields.js");
const index_js_1 = require("../directives/index.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, collectFields_js_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.

@@ -16,4 +28,40 @@ *

* 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,

@@ -28,3 +76,3 @@ // a "Response" with only errors is returned.

}
exports.execute = execute;
exports.experimentalExecuteIncrementally = experimentalExecuteIncrementally;
function executeImpl(exeContext) {

@@ -45,3 +93,15 @@ // Return a Promise that will eventually resolve to the data described by

if ((0, utils_1.isPromise)(result)) {
return result.then(data => buildResponse(data, exeContext.errors), error => {
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 => {
exeContext.errors.push(error);

@@ -51,3 +111,13 @@ return buildResponse(null, exeContext.errors);

}
return buildResponse(result, exeContext.errors);
const initialResult = buildResponse(result, exeContext.errors);
if (exeContext.subsequentPayloads.size > 0) {
return {
initialResult: {
...initialResult,
hasNext: true,
},
subsequentResults: yieldSubsequentPayloads(exeContext),
};
}
return initialResult;
}

@@ -65,5 +135,5 @@ catch (error) {

function executeSync(args) {
const result = execute(args);
const result = experimentalExecuteIncrementally(args);
// Assert that the execution was synchronous.
if ((0, utils_1.isPromise)(result)) {
if ((0, utils_1.isPromise)(result) || 'initialResult' in result) {
throw new Error('GraphQL execution failed to complete synchronously.');

@@ -156,2 +226,3 @@ }

subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : exports.defaultFieldResolver,
subsequentPayloads: new Set(),
errors: [],

@@ -165,2 +236,3 @@ };

rootValue: payload,
subsequentPayloads: new Set(),
errors: [],

@@ -175,15 +247,21 @@ };

const rootType = (0, utils_1.getDefinedRootType)(schema, operation.operation, [operation]);
const rootFields = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
if (rootType == null) {
(0, utils_1.createGraphQLError)(`Schema is not configured to execute ${operation.operation} operation.`, {
nodes: operation,
});
}
const { fields: rootFields, patches } = (0, collectFields_js_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
const path = undefined;
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);
let result;
if (operation.operation === 'mutation') {
result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields);
}
throw new Error(`Can only execute queries, mutations and subscriptions, got "${operation.operation}".`);
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;
}

@@ -195,3 +273,3 @@ /**

function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) {
return (0, utils_1.promiseReduce)(fields.entries(), (results, [responseName, fieldNodes]) => {
return (0, utils_1.promiseReduce)(fields, (results, [responseName, fieldNodes]) => {
const fieldPath = (0, utils_1.addPath)(path, responseName, parentType.name);

@@ -216,15 +294,26 @@ const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath);

*/
function executeFields(exeContext, parentType, sourceValue, path, fields) {
function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) {
const results = Object.create(null);
let containsPromise = false;
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;
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;
}
}
}
}
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

@@ -237,9 +326,3 @@ if (!containsPromise) {

// same map, but with any promises replaced with the values they resolved to.
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;
});
return (0, promiseForObject_js_1.promiseForObject)(results);
}

@@ -252,4 +335,5 @@ /**

*/
function executeField(exeContext, parentType, source, fieldNodes, path) {
var _a;
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;
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]);

@@ -260,3 +344,3 @@ if (!fieldDef) {

const returnType = fieldDef.type;
const resolveFn = (_a = fieldDef.resolve) !== null && _a !== void 0 ? _a : exeContext.fieldResolver;
const resolveFn = (_b = fieldDef.resolve) !== null && _b !== void 0 ? _b : exeContext.fieldResolver;
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path);

@@ -276,6 +360,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));
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved, asyncPayloadRecord));
}
else {
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result);
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -287,3 +371,5 @@ if ((0, utils_1.isPromise)(completed)) {

const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path));
return handleFieldError(error, returnType, exeContext);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
});

@@ -295,3 +381,5 @@ }

const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(path));
return handleFieldError(error, returnType, exeContext);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
}

@@ -320,3 +408,3 @@ }

exports.buildResolveInfo = buildResolveInfo;
function handleFieldError(error, returnType, exeContext) {
function handleFieldError(error, returnType, errors) {
// If the field type is non-nullable, then it is resolved without any

@@ -329,3 +417,3 @@ // protection from errors, however it still properly locates the error.

// a null value for this field if one is encountered.
exeContext.errors.push(error);
errors.push(error);
return null;

@@ -354,3 +442,3 @@ }

*/
function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
// If result is an Error, throw a located error.

@@ -363,3 +451,3 @@ if (result instanceof Error) {

if ((0, graphql_1.isNonNullType)(returnType)) {
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result);
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result, asyncPayloadRecord);
if (completed === null) {

@@ -376,3 +464,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);
return completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -387,7 +475,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);
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}
// 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);
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -399,6 +487,35 @@ /* 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)(index_js_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) {
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);
let containsPromise = false;

@@ -408,28 +525,22 @@ const completedResults = [];

while (true) {
const fieldPath = (0, utils_1.addPath)(path, index, undefined);
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;
try {
const { value, done } = await iterator.next();
if (done) {
iteration = await iterator.next();
if (iteration.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) {
completedResults.push(null);
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(fieldPath));
handleFieldError(error, itemType, exeContext);
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
completedResults.push(handleFieldError(error, itemType, errors));
break;
}
if (completeListItemValue(iteration.value, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
containsPromise = true;
}
index += 1;

@@ -443,7 +554,9 @@ }

*/
function completeListValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
var _a;
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);
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord);
}

@@ -453,36 +566,61 @@ 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;
const completedResults = Array.from(result, (item, index) => {
let previousAsyncPayloadRecord = asyncPayloadRecord;
const completedResults = [];
let index = 0;
for (const item of result) {
// 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);
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 (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord);
index++;
continue;
}
catch (rawError) {
const error = (0, graphql_1.locatedError)(rawError, fieldNodes, (0, utils_1.pathToArray)(itemPath));
return handleFieldError(error, itemType, exeContext);
if (completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
containsPromise = true;
}
});
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

@@ -503,3 +641,3 @@ * null if serialization is not possible.

*/
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
var _a;

@@ -510,5 +648,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));
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord));
}
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result);
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -543,5 +681,3 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) {

*/
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);
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
// If there is an isTypeOf predicate function, call it with the

@@ -557,3 +693,3 @@ // current result. If isTypeOf returns false, then raise an error rather

}
return executeFields(exeContext, returnType, result, path, subFieldNodes);
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
});

@@ -565,3 +701,3 @@ }

}
return executeFields(exeContext, returnType, result, path, subFieldNodes);
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
}

@@ -573,2 +709,12 @@ 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;
}
/**

@@ -641,7 +787,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`.

@@ -652,5 +798,64 @@ *

*
* Accepts either an object with named arguments, or individual arguments.
* 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.
*/
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,

@@ -669,3 +874,12 @@ // a "Response" with only errors is returned.

}
exports.subscribe = subscribe;
exports.experimentalSubscribeIncrementally = experimentalSubscribeIncrementally;
async function* ensureAsyncIterable(someExecutionResult) {
if ('initialResult' in someExecutionResult) {
yield someExecutionResult.initialResult;
yield* someExecutionResult.subsequentResults;
}
else {
yield someExecutionResult;
}
}
function mapSourceToResponse(exeContext, resultOrStream) {

@@ -681,3 +895,3 @@ if (!(0, utils_1.isAsyncIterable)(resultOrStream)) {

// "ExecuteQuery" algorithm, for which `execute` is also used.
return (0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), (payload) => executeImpl(buildPerEventExecutionContext(exeContext, payload)));
return (0, flattenAsyncIterable_js_1.flattenAsyncIterable)((0, utils_1.mapAsyncIterator)(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload)))));
}

@@ -742,3 +956,3 @@ /**

}
const rootFields = (0, utils_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
const { fields: rootFields } = (0, collectFields_js_1.collectFields)(schema, fragments, variableValues, rootType, operation.selectionSet);
const [responseName, fieldNodes] = [...rootFields.entries()][0];

@@ -787,2 +1001,335 @@ 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';
}
/**

@@ -789,0 +1336,0 @@ * This method looks up the field on the given type definition.

@@ -5,1 +5,2 @@ "use strict";

tslib_1.__exportStar(require("./execution/index.js"), exports);
tslib_1.__exportStar(require("./directives/index.js"), exports);

@@ -1,5 +0,17 @@

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 { 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, } from '@graphql-tools/utils';
import { getVariableValues } from './values.js';
import { promiseForObject } from './promiseForObject.js';
import { flattenAsyncIterable } from './flattenAsyncIterable.js';
import { collectFields, collectSubfields as _collectSubfields } from './collectFields.js';
import { GraphQLStreamDirective } from '../directives/index.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.

@@ -13,4 +25,39 @@ *

* 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,

@@ -40,3 +87,15 @@ // a "Response" with only errors is returned.

if (isPromise(result)) {
return result.then(data => buildResponse(data, exeContext.errors), error => {
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 => {
exeContext.errors.push(error);

@@ -46,3 +105,13 @@ return buildResponse(null, exeContext.errors);

}
return buildResponse(result, exeContext.errors);
const initialResult = buildResponse(result, exeContext.errors);
if (exeContext.subsequentPayloads.size > 0) {
return {
initialResult: {
...initialResult,
hasNext: true,
},
subsequentResults: yieldSubsequentPayloads(exeContext),
};
}
return initialResult;
}

@@ -60,5 +129,5 @@ catch (error) {

export function executeSync(args) {
const result = execute(args);
const result = experimentalExecuteIncrementally(args);
// Assert that the execution was synchronous.
if (isPromise(result)) {
if (isPromise(result) || 'initialResult' in result) {
throw new Error('GraphQL execution failed to complete synchronously.');

@@ -149,2 +218,3 @@ }

subscribeFieldResolver: subscribeFieldResolver !== null && subscribeFieldResolver !== void 0 ? subscribeFieldResolver : defaultFieldResolver,
subsequentPayloads: new Set(),
errors: [],

@@ -157,2 +227,3 @@ };

rootValue: payload,
subsequentPayloads: new Set(),
errors: [],

@@ -167,15 +238,21 @@ };

const rootType = getDefinedRootType(schema, operation.operation, [operation]);
const rootFields = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet);
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 path = undefined;
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);
let result;
if (operation.operation === 'mutation') {
result = executeFieldsSerially(exeContext, rootType, rootValue, path, rootFields);
}
throw new Error(`Can only execute queries, mutations and subscriptions, got "${operation.operation}".`);
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;
}

@@ -187,3 +264,3 @@ /**

function executeFieldsSerially(exeContext, parentType, sourceValue, path, fields) {
return promiseReduce(fields.entries(), (results, [responseName, fieldNodes]) => {
return promiseReduce(fields, (results, [responseName, fieldNodes]) => {
const fieldPath = addPath(path, responseName, parentType.name);

@@ -208,15 +285,26 @@ const result = executeField(exeContext, parentType, sourceValue, fieldNodes, fieldPath);

*/
function executeFields(exeContext, parentType, sourceValue, path, fields) {
function executeFields(exeContext, parentType, sourceValue, path, fields, asyncPayloadRecord) {
const results = Object.create(null);
let containsPromise = false;
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;
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;
}
}
}
}
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

@@ -229,9 +317,3 @@ if (!containsPromise) {

// same map, but with any promises replaced with the values they resolved to.
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;
});
return promiseForObject(results);
}

@@ -244,4 +326,5 @@ /**

*/
function executeField(exeContext, parentType, source, fieldNodes, path) {
var _a;
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;
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]);

@@ -252,3 +335,3 @@ if (!fieldDef) {

const returnType = fieldDef.type;
const resolveFn = (_a = fieldDef.resolve) !== null && _a !== void 0 ? _a : exeContext.fieldResolver;
const resolveFn = (_b = fieldDef.resolve) !== null && _b !== void 0 ? _b : exeContext.fieldResolver;
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path);

@@ -268,6 +351,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));
completed = result.then(resolved => completeValue(exeContext, returnType, fieldNodes, info, path, resolved, asyncPayloadRecord));
}
else {
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result);
completed = completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -279,3 +362,5 @@ if (isPromise(completed)) {

const error = locatedError(rawError, fieldNodes, pathToArray(path));
return handleFieldError(error, returnType, exeContext);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
});

@@ -287,3 +372,5 @@ }

const error = locatedError(rawError, fieldNodes, pathToArray(path));
return handleFieldError(error, returnType, exeContext);
const handledError = handleFieldError(error, returnType, errors);
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
return handledError;
}

@@ -311,3 +398,3 @@ }

}
function handleFieldError(error, returnType, exeContext) {
function handleFieldError(error, returnType, errors) {
// If the field type is non-nullable, then it is resolved without any

@@ -320,3 +407,3 @@ // protection from errors, however it still properly locates the error.

// a null value for this field if one is encountered.
exeContext.errors.push(error);
errors.push(error);
return null;

@@ -345,3 +432,3 @@ }

*/
function completeValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
// If result is an Error, throw a located error.

@@ -354,3 +441,3 @@ if (result instanceof Error) {

if (isNonNullType(returnType)) {
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result);
const completed = completeValue(exeContext, returnType.ofType, fieldNodes, info, path, result, asyncPayloadRecord);
if (completed === null) {

@@ -367,3 +454,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);
return completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -378,7 +465,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);
return completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}
// If field type is Object, execute and complete all sub-selections.
if (isObjectType(returnType)) {
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result);
return completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -390,6 +477,35 @@ /* 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) {
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);
let containsPromise = false;

@@ -399,28 +515,22 @@ const completedResults = [];

while (true) {
const fieldPath = addPath(path, index, undefined);
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;
try {
const { value, done } = await iterator.next();
if (done) {
iteration = await iterator.next();
if (iteration.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) {
completedResults.push(null);
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath));
handleFieldError(error, itemType, exeContext);
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
completedResults.push(handleFieldError(error, itemType, errors));
break;
}
if (completeListItemValue(iteration.value, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
containsPromise = true;
}
index += 1;

@@ -434,7 +544,9 @@ }

*/
function completeListValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeListValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
var _a;
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);
return completeAsyncIteratorValue(exeContext, itemType, fieldNodes, info, path, iterator, asyncPayloadRecord);
}

@@ -444,36 +556,61 @@ 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;
const completedResults = Array.from(result, (item, index) => {
let previousAsyncPayloadRecord = asyncPayloadRecord;
const completedResults = [];
let index = 0;
for (const item of result) {
// 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);
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 (stream && typeof stream.initialCount === 'number' && index >= stream.initialCount) {
previousAsyncPayloadRecord = executeStreamField(path, itemPath, item, exeContext, fieldNodes, info, itemType, stream.label, previousAsyncPayloadRecord);
index++;
continue;
}
catch (rawError) {
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
return handleFieldError(error, itemType, exeContext);
if (completeListItemValue(item, completedResults, errors, exeContext, itemType, fieldNodes, info, itemPath, asyncPayloadRecord)) {
containsPromise = true;
}
});
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

@@ -494,3 +631,3 @@ * null if serialization is not possible.

*/
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result) {
function completeAbstractValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
var _a;

@@ -501,5 +638,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));
return runtimeType.then(resolvedRuntimeType => completeObjectValue(exeContext, ensureValidRuntimeType(resolvedRuntimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord));
}
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result);
return completeObjectValue(exeContext, ensureValidRuntimeType(runtimeType, exeContext, returnType, fieldNodes, info, result), fieldNodes, info, path, result, asyncPayloadRecord);
}

@@ -534,5 +671,3 @@ function ensureValidRuntimeType(runtimeTypeName, exeContext, returnType, fieldNodes, info, result) {

*/
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);
function completeObjectValue(exeContext, returnType, fieldNodes, info, path, result, asyncPayloadRecord) {
// If there is an isTypeOf predicate function, call it with the

@@ -548,3 +683,3 @@ // current result. If isTypeOf returns false, then raise an error rather

}
return executeFields(exeContext, returnType, result, path, subFieldNodes);
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
});

@@ -556,3 +691,3 @@ }

}
return executeFields(exeContext, returnType, result, path, subFieldNodes);
return collectAndExecuteSubfields(exeContext, returnType, fieldNodes, path, result, asyncPayloadRecord);
}

@@ -564,2 +699,12 @@ 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;
}
/**

@@ -630,7 +775,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`.

@@ -641,5 +786,63 @@ *

*
* Accepts either an object with named arguments, or individual arguments.
* 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.
*/
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,

@@ -658,2 +861,11 @@ // 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) {

@@ -669,3 +881,3 @@ if (!isAsyncIterable(resultOrStream)) {

// "ExecuteQuery" algorithm, for which `execute` is also used.
return mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), (payload) => executeImpl(buildPerEventExecutionContext(exeContext, payload)));
return flattenAsyncIterable(mapAsyncIterator(resultOrStream[Symbol.asyncIterator](), async (payload) => ensureAsyncIterable(await executeImpl(buildPerEventExecutionContext(exeContext, payload)))));
}

@@ -729,3 +941,3 @@ /**

}
const rootFields = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet);
const { fields: rootFields } = collectFields(schema, fragments, variableValues, rootType, operation.selectionSet);
const [responseName, fieldNodes] = [...rootFields.entries()][0];

@@ -774,2 +986,335 @@ 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';
}
/**

@@ -776,0 +1321,0 @@ * This method looks up the field on the given type definition.

export * from './execution/index.js';
export * from './directives/index.js';

4

package.json
{
"name": "@graphql-tools/executor",
"version": "0.0.2-alpha-20221027131523-6d0f4d51",
"version": "0.0.2-alpha-20221029152711-14f4f7a7",
"sideEffects": false,

@@ -9,3 +9,3 @@ "peerDependencies": {

"dependencies": {
"@graphql-tools/utils": "8.13.0",
"@graphql-tools/utils": "8.14.0-alpha-20221029152711-14f4f7a7",
"@graphql-typed-document-node/core": "3.1.1"

@@ -12,0 +12,0 @@ },

@@ -1,3 +0,4 @@

import { GraphQLFormattedError, GraphQLError, FieldNode, FragmentDefinitionNode, OperationDefinitionNode, GraphQLField, GraphQLFieldResolver, GraphQLObjectType, GraphQLResolveInfo, GraphQLTypeResolver, GraphQLSchema } from 'graphql';
import { Maybe, Path, MaybePromise, ExecutionResult } from '@graphql-tools/utils';
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 { TypedDocumentNode } from '@graphql-typed-document-node/core';

@@ -29,3 +30,3 @@ /**

*/
export interface ExecutionContext<TContext = any> {
export interface ExecutionContext<TVariables = any, TContext = any> {
schema: GraphQLSchema;

@@ -36,5 +37,3 @@ fragments: Record<string, FragmentDefinitionNode>;

operation: OperationDefinitionNode;
variableValues: {
[variable: string]: unknown;
};
variableValues: TVariables;
fieldResolver: GraphQLFieldResolver<any, TContext>;

@@ -44,4 +43,5 @@ typeResolver: GraphQLTypeResolver<any, TContext>;

errors: Array<GraphQLError>;
subsequentPayloads: Set<AsyncPayloadRecord>;
}
export interface FormattedExecutionResult<TData = any, TExtensions = any> {
export interface FormattedExecutionResult<TData = Record<string, unknown>, TExtensions = Record<string, unknown>> {
errors?: ReadonlyArray<GraphQLFormattedError>;

@@ -51,2 +51,50 @@ 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> {

@@ -72,9 +120,24 @@ 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 = {
[key: string]: any;
}, TVariables = {
[key: string]: any;
}, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): MaybePromise<ExecutionResult<TData>>;
export declare function execute<TData = any, TVariables = 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.

@@ -101,7 +164,3 @@ * However, it guarantees to complete synchronously (or throw an error) assuming

*/
export declare function buildExecutionContext<TData = {
[key: string]: any;
}, TVariables = {
[key: string]: any;
}, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): ReadonlyArray<GraphQLError> | ExecutionContext;
export declare function buildExecutionContext<TData = any, TVariables = any, TContext = any>(args: ExecutionArgs<TData, TVariables, TContext>): ReadonlyArray<GraphQLError> | ExecutionContext;
/**

@@ -139,7 +198,7 @@ * TODO: consider no longer exporting this function

* 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`.

@@ -150,6 +209,48 @@ *

*
* Accepts either an object with named arguments, or individual arguments.
* 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.
*/
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

@@ -183,2 +284,45 @@ * 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;
/**

@@ -196,2 +340,2 @@ * 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 { ExecutionResult } from '@graphql-tools/utils';
export {};
export * from './execution/index.js';
export * from './directives/index.js';

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc