Comparing version 17.0.0-alpha.1.canary.pr.3659.cef660554446d49cec9a0958afb9690dd0b19193 to 17.0.0-alpha.1.canary.pr.3673.53c289997f206acd10388d6a574341c68cc1a30e
@@ -9,10 +9,2 @@ import type { ObjMap } from '../jsutils/ObjMap'; | ||
import type { GraphQLSchema } from '../type/schema'; | ||
export interface PatchFields { | ||
label?: string; | ||
fields: Map<string, ReadonlyArray<FieldNode>>; | ||
} | ||
export interface FieldsAndPatches { | ||
fields: Map<string, ReadonlyArray<FieldNode>>; | ||
patches: Array<PatchFields>; | ||
} | ||
/** | ||
@@ -35,3 +27,3 @@ * Given a selectionSet, collects all of the fields and returns them. | ||
selectionSet: SelectionSetNode, | ||
): FieldsAndPatches; | ||
): Map<string, ReadonlyArray<FieldNode>>; | ||
/** | ||
@@ -55,2 +47,2 @@ * Given an array of field nodes, collects all of the subfields of the passed | ||
fieldNodes: ReadonlyArray<FieldNode>, | ||
): FieldsAndPatches; | ||
): Map<string, ReadonlyArray<FieldNode>>; |
@@ -5,3 +5,2 @@ import { AccumulatorMap } from '../jsutils/AccumulatorMap.js'; | ||
import { | ||
GraphQLDeferDirective, | ||
GraphQLIncludeDirective, | ||
@@ -29,3 +28,2 @@ GraphQLSkipDirective, | ||
const fields = new AccumulatorMap(); | ||
const patches = []; | ||
collectFieldsImpl( | ||
@@ -38,6 +36,5 @@ schema, | ||
fields, | ||
patches, | ||
new Set(), | ||
); | ||
return { fields, patches }; | ||
return fields; | ||
} | ||
@@ -63,7 +60,2 @@ /** | ||
const visitedFragmentNames = new Set(); | ||
const subPatches = []; | ||
const subFieldsAndPatches = { | ||
fields: subFieldNodes, | ||
patches: subPatches, | ||
}; | ||
for (const node of fieldNodes) { | ||
@@ -78,3 +70,2 @@ if (node.selectionSet) { | ||
subFieldNodes, | ||
subPatches, | ||
visitedFragmentNames, | ||
@@ -84,3 +75,3 @@ ); | ||
} | ||
return subFieldsAndPatches; | ||
return subFieldNodes; | ||
} | ||
@@ -95,3 +86,2 @@ // eslint-disable-next-line max-params | ||
fields, | ||
patches, | ||
visitedFragmentNames, | ||
@@ -115,31 +105,11 @@ ) { | ||
} | ||
const defer = getDeferValues(variableValues, selection); | ||
if (defer) { | ||
const patchFields = new AccumulatorMap(); | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
selection.selectionSet, | ||
patchFields, | ||
patches, | ||
visitedFragmentNames, | ||
); | ||
patches.push({ | ||
label: defer.label, | ||
fields: patchFields, | ||
}); | ||
} else { | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
selection.selectionSet, | ||
fields, | ||
patches, | ||
visitedFragmentNames, | ||
); | ||
} | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
selection.selectionSet, | ||
fields, | ||
visitedFragmentNames, | ||
); | ||
break; | ||
@@ -149,9 +119,9 @@ } | ||
const fragName = selection.name.value; | ||
if (!shouldIncludeNode(variableValues, selection)) { | ||
if ( | ||
visitedFragmentNames.has(fragName) || | ||
!shouldIncludeNode(variableValues, selection) | ||
) { | ||
continue; | ||
} | ||
const defer = getDeferValues(variableValues, selection); | ||
if (visitedFragmentNames.has(fragName) && !defer) { | ||
continue; | ||
} | ||
visitedFragmentNames.add(fragName); | ||
const fragment = fragments[fragName]; | ||
@@ -164,33 +134,11 @@ if ( | ||
} | ||
if (!defer) { | ||
visitedFragmentNames.add(fragName); | ||
} | ||
if (defer) { | ||
const patchFields = new AccumulatorMap(); | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
fragment.selectionSet, | ||
patchFields, | ||
patches, | ||
visitedFragmentNames, | ||
); | ||
patches.push({ | ||
label: defer.label, | ||
fields: patchFields, | ||
}); | ||
} else { | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
fragment.selectionSet, | ||
fields, | ||
patches, | ||
visitedFragmentNames, | ||
); | ||
} | ||
collectFieldsImpl( | ||
schema, | ||
fragments, | ||
variableValues, | ||
runtimeType, | ||
fragment.selectionSet, | ||
fields, | ||
visitedFragmentNames, | ||
); | ||
break; | ||
@@ -202,19 +150,2 @@ } | ||
/** | ||
* Returns an object containing the `@defer` arguments if a field should be | ||
* deferred based on the experimental flag, defer directive present and | ||
* not disabled by the "if" argument. | ||
*/ | ||
function getDeferValues(variableValues, node) { | ||
const defer = getDirectiveValues(GraphQLDeferDirective, node, variableValues); | ||
if (!defer) { | ||
return; | ||
} | ||
if (defer.if === false) { | ||
return; | ||
} | ||
return { | ||
label: typeof defer.label === 'string' ? defer.label : undefined, | ||
}; | ||
} | ||
/** | ||
* Determines if a field should be included based on the `@include` and `@skip` | ||
@@ -221,0 +152,0 @@ * directives, where `@skip` has higher precedence than `@include`. |
@@ -59,3 +59,2 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
errors: Array<GraphQLError>; | ||
subsequentPayloads: Array<AsyncPayloadRecord>; | ||
} | ||
@@ -67,7 +66,6 @@ /** | ||
* - `data` is the result of a successful execution of the query. | ||
* - `hasNext` is true if a future payload is expected. | ||
* - `extensions` is reserved for adding non-standard properties. | ||
*/ | ||
export interface ExecutionResult< | ||
TData = unknown, | ||
TData = ObjMap<unknown>, | ||
TExtensions = ObjMap<unknown>, | ||
@@ -77,3 +75,2 @@ > { | ||
data?: TData | null; | ||
hasNext?: boolean; | ||
extensions?: TExtensions; | ||
@@ -87,40 +84,4 @@ } | ||
data?: TData | null; | ||
hasNext?: boolean; | ||
extensions?: TExtensions; | ||
} | ||
/** | ||
* The result of an asynchronous GraphQL patch. | ||
* | ||
* - `errors` is included when any errors occurred as a non-empty array. | ||
* - `data` is the result of the additional asynchronous data. | ||
* - `path` is the location of data. | ||
* - `label` is the label provided to `@defer` or `@stream`. | ||
* - `hasNext` is true if a future payload is expected. | ||
* - `extensions` is reserved for adding non-standard properties. | ||
*/ | ||
export interface ExecutionPatchResult< | ||
TData = unknown, | ||
TExtensions = ObjMap<unknown>, | ||
> { | ||
errors?: ReadonlyArray<GraphQLError>; | ||
data?: TData | null; | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
hasNext: boolean; | ||
extensions?: TExtensions; | ||
} | ||
export interface FormattedExecutionPatchResult< | ||
TData = unknown, | ||
TExtensions = ObjMap<unknown>, | ||
> { | ||
errors?: ReadonlyArray<GraphQLFormattedError>; | ||
data?: TData | null; | ||
path?: ReadonlyArray<string | number>; | ||
label?: string; | ||
hasNext: boolean; | ||
extensions?: TExtensions; | ||
} | ||
export declare type AsyncExecutionResult = | ||
| ExecutionResult | ||
| ExecutionPatchResult; | ||
export interface ExecutionArgs { | ||
@@ -151,5 +112,3 @@ schema: GraphQLSchema; | ||
args: ExecutionArgs, | ||
): PromiseOrValue< | ||
ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, void> | ||
>; | ||
): PromiseOrValue<ExecutionResult>; | ||
/** | ||
@@ -262,20 +221,1 @@ * Also implements the "Executing requests" section of the GraphQL specification. | ||
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult>; | ||
declare class AsyncPayloadRecord { | ||
errors: Array<GraphQLError>; | ||
label?: string; | ||
path?: Path; | ||
data?: PromiseOrValue<unknown>; | ||
parentContext?: AsyncPayloadRecord; | ||
iterator?: AsyncIterator<unknown>; | ||
isCompletedIterator?: boolean; | ||
constructor(opts: { | ||
label?: string; | ||
path?: Path; | ||
iterator?: AsyncIterator<unknown>; | ||
parentContext?: AsyncPayloadRecord; | ||
}); | ||
addData(data: PromiseOrValue<unknown>): void; | ||
getData(): PromiseOrValue<unknown>; | ||
setIsCompletedIterator(): void; | ||
} | ||
export {}; |
@@ -22,3 +22,2 @@ import { inspect } from '../jsutils/inspect.js'; | ||
} from '../type/definition.js'; | ||
import { GraphQLStreamDirective } from '../type/directives.js'; | ||
import { assertValidSchema } from '../type/validate.js'; | ||
@@ -29,9 +28,4 @@ import { | ||
} from './collectFields.js'; | ||
import { flattenAsyncIterator } from './flattenAsyncIterator.js'; | ||
import { mapAsyncIterator } from './mapAsyncIterator.js'; | ||
import { | ||
getArgumentValues, | ||
getDirectiveValues, | ||
getVariableValues, | ||
} from './values.js'; | ||
import { getArgumentValues, getVariableValues } from './values.js'; | ||
/* eslint-disable max-params */ | ||
@@ -90,9 +84,3 @@ // This file contains a lot of such errors but we plan to refactor it anyway | ||
return result.then( | ||
(data) => { | ||
const initialResult = buildResponse(data, exeContext.errors); | ||
if (exeContext.subsequentPayloads.length > 0) { | ||
return yieldSubsequentPayloads(exeContext, initialResult); | ||
} | ||
return initialResult; | ||
}, | ||
(data) => buildResponse(data, exeContext.errors), | ||
(error) => { | ||
@@ -104,7 +92,3 @@ exeContext.errors.push(error); | ||
} | ||
const initialResult = buildResponse(result, exeContext.errors); | ||
if (exeContext.subsequentPayloads.length > 0) { | ||
return yieldSubsequentPayloads(exeContext, initialResult); | ||
} | ||
return initialResult; | ||
return buildResponse(result, exeContext.errors); | ||
} catch (error) { | ||
@@ -123,3 +107,3 @@ exeContext.errors.push(error); | ||
// Assert that the execution was synchronous. | ||
if (isPromise(result) || isAsyncIterable(result)) { | ||
if (isPromise(result)) { | ||
throw new Error('GraphQL execution failed to complete synchronously.'); | ||
@@ -212,3 +196,2 @@ } | ||
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver, | ||
subsequentPayloads: [], | ||
errors: [], | ||
@@ -237,3 +220,3 @@ }; | ||
} | ||
const { fields: rootFields, patches } = collectFields( | ||
const rootFields = collectFields( | ||
schema, | ||
@@ -246,9 +229,7 @@ fragments, | ||
const path = undefined; | ||
let result; | ||
switch (operation.operation) { | ||
case OperationTypeNode.QUERY: | ||
result = executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
break; | ||
return executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
case OperationTypeNode.MUTATION: | ||
result = executeFieldsSerially( | ||
return executeFieldsSerially( | ||
exeContext, | ||
@@ -260,20 +241,7 @@ rootType, | ||
); | ||
break; | ||
case OperationTypeNode.SUBSCRIPTION: | ||
// TODO: deprecate `subscribe` and move all logic here | ||
// Temporary solution until we finish merging execute and subscribe together | ||
result = executeFields(exeContext, rootType, rootValue, path, rootFields); | ||
return 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; | ||
} | ||
@@ -321,10 +289,3 @@ /** | ||
*/ | ||
function executeFields( | ||
exeContext, | ||
parentType, | ||
sourceValue, | ||
path, | ||
fields, | ||
asyncPayloadRecord, | ||
) { | ||
function executeFields(exeContext, parentType, sourceValue, path, fields) { | ||
const results = Object.create(null); | ||
@@ -340,3 +301,2 @@ let containsPromise = false; | ||
fieldPath, | ||
asyncPayloadRecord, | ||
); | ||
@@ -365,11 +325,3 @@ if (result !== undefined) { | ||
*/ | ||
function executeField( | ||
exeContext, | ||
parentType, | ||
source, | ||
fieldNodes, | ||
path, | ||
asyncPayloadRecord, | ||
) { | ||
const errors = asyncPayloadRecord?.errors ?? exeContext.errors; | ||
function executeField(exeContext, parentType, source, fieldNodes, path) { | ||
const fieldName = fieldNodes[0].name.value; | ||
@@ -407,11 +359,3 @@ const fieldDef = exeContext.schema.getField(parentType, fieldName); | ||
completed = result.then((resolved) => | ||
completeValue( | ||
exeContext, | ||
returnType, | ||
fieldNodes, | ||
info, | ||
path, | ||
resolved, | ||
asyncPayloadRecord, | ||
), | ||
completeValue(exeContext, returnType, fieldNodes, info, path, resolved), | ||
); | ||
@@ -426,3 +370,2 @@ } else { | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -435,3 +378,3 @@ } | ||
const error = locatedError(rawError, fieldNodes, pathToArray(path)); | ||
return handleFieldError(error, returnType, errors); | ||
return handleFieldError(error, returnType, exeContext); | ||
}); | ||
@@ -442,3 +385,3 @@ } | ||
const error = locatedError(rawError, fieldNodes, pathToArray(path)); | ||
return handleFieldError(error, returnType, errors); | ||
return handleFieldError(error, returnType, exeContext); | ||
} | ||
@@ -472,3 +415,3 @@ } | ||
} | ||
function handleFieldError(error, returnType, errors) { | ||
function handleFieldError(error, returnType, exeContext) { | ||
// If the field type is non-nullable, then it is resolved without any | ||
@@ -481,3 +424,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; | ||
@@ -506,11 +449,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. | ||
@@ -530,3 +465,2 @@ if (result instanceof Error) { | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -553,3 +487,2 @@ if (completed === null) { | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -572,3 +505,2 @@ } | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -585,3 +517,2 @@ } | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -598,34 +529,2 @@ } | ||
/** | ||
* 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; | ||
} | ||
typeof stream.initialCount === 'number' || | ||
invariant(false, 'initialCount must be a number'); | ||
stream.initialCount >= 0 || | ||
invariant(false, '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 | ||
@@ -641,6 +540,3 @@ * recursively until all the results are completed. | ||
iterator, | ||
asyncPayloadRecord, | ||
) { | ||
const errors = asyncPayloadRecord?.errors ?? exeContext.errors; | ||
const stream = getStreamValues(exeContext, fieldNodes, path); | ||
let containsPromise = false; | ||
@@ -651,21 +547,2 @@ const completedResults = []; | ||
while (true) { | ||
if ( | ||
stream && | ||
typeof stream.initialCount === 'number' && | ||
index >= stream.initialCount | ||
) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
executeStreamIterator( | ||
index, | ||
iterator, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
path, | ||
stream.label, | ||
asyncPayloadRecord, | ||
); | ||
break; | ||
} | ||
const fieldPath = addPath(path, index, undefined); | ||
@@ -687,3 +564,2 @@ try { | ||
value, | ||
asyncPayloadRecord, | ||
); | ||
@@ -701,3 +577,3 @@ if (isPromise(completedItem)) { | ||
); | ||
handleFieldError(error, itemType, errors); | ||
handleFieldError(error, itemType, exeContext); | ||
} | ||
@@ -707,3 +583,3 @@ } catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath)); | ||
handleFieldError(error, itemType, errors); | ||
handleFieldError(error, itemType, exeContext); | ||
break; | ||
@@ -726,6 +602,4 @@ } | ||
result, | ||
asyncPayloadRecord, | ||
) { | ||
const itemType = returnType.ofType; | ||
const errors = asyncPayloadRecord?.errors ?? exeContext.errors; | ||
if (isAsyncIterable(result)) { | ||
@@ -740,3 +614,2 @@ const iterator = result[Symbol.asyncIterator](); | ||
iterator, | ||
asyncPayloadRecord, | ||
); | ||
@@ -749,10 +622,6 @@ } | ||
} | ||
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, | ||
@@ -763,20 +632,2 @@ // since from here on it is not ever accessed by resolver functions. | ||
let completedItem; | ||
if ( | ||
stream && | ||
typeof stream.initialCount === 'number' && | ||
index >= stream.initialCount | ||
) { | ||
previousAsyncPayloadRecord = executeStreamField( | ||
itemPath, | ||
item, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
stream.label, | ||
previousAsyncPayloadRecord, | ||
); | ||
index++; | ||
continue; | ||
} | ||
if (isPromise(item)) { | ||
@@ -791,3 +642,2 @@ completedItem = item.then((resolved) => | ||
resolved, | ||
asyncPayloadRecord, | ||
), | ||
@@ -803,3 +653,2 @@ ); | ||
item, | ||
asyncPayloadRecord, | ||
); | ||
@@ -811,21 +660,17 @@ } | ||
// to take a second callback for the error case. | ||
completedResults.push( | ||
completedItem.then(undefined, (rawError) => { | ||
const error = locatedError( | ||
rawError, | ||
fieldNodes, | ||
pathToArray(itemPath), | ||
); | ||
return handleFieldError(error, itemType, errors); | ||
}), | ||
); | ||
} else { | ||
completedResults.push(completedItem); | ||
return completedItem.then(undefined, (rawError) => { | ||
const error = locatedError( | ||
rawError, | ||
fieldNodes, | ||
pathToArray(itemPath), | ||
); | ||
return handleFieldError(error, itemType, exeContext); | ||
}); | ||
} | ||
return completedItem; | ||
} catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath)); | ||
completedResults.push(handleFieldError(error, itemType, errors)); | ||
return handleFieldError(error, itemType, exeContext); | ||
} | ||
index++; | ||
} | ||
}); | ||
return containsPromise ? Promise.all(completedResults) : completedResults; | ||
@@ -858,3 +703,2 @@ } | ||
result, | ||
asyncPayloadRecord, | ||
) { | ||
@@ -880,3 +724,2 @@ const resolveTypeFn = returnType.resolveType ?? exeContext.typeResolver; | ||
result, | ||
asyncPayloadRecord, | ||
), | ||
@@ -899,3 +742,2 @@ ); | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
@@ -961,4 +803,5 @@ } | ||
result, | ||
asyncPayloadRecord, | ||
) { | ||
// Collect sub-fields to execute to complete this value. | ||
const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); | ||
// If there is an isTypeOf predicate function, call it with the | ||
@@ -974,9 +817,8 @@ // current result. If isTypeOf returns false, then raise an error rather | ||
} | ||
return collectAndExecuteSubfields( | ||
return executeFields( | ||
exeContext, | ||
returnType, | ||
fieldNodes, | ||
result, | ||
path, | ||
result, | ||
asyncPayloadRecord, | ||
subFieldNodes, | ||
); | ||
@@ -989,10 +831,3 @@ }); | ||
} | ||
return collectAndExecuteSubfields( | ||
exeContext, | ||
returnType, | ||
fieldNodes, | ||
path, | ||
result, | ||
asyncPayloadRecord, | ||
); | ||
return executeFields(exeContext, returnType, result, path, subFieldNodes); | ||
} | ||
@@ -1005,38 +840,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; | ||
} | ||
/** | ||
@@ -1154,6 +953,4 @@ * If a resolveType function is not given, then a default resolve behavior is | ||
// "ExecuteQuery" algorithm, for which `execute` is also used. | ||
return flattenAsyncIterator( | ||
mapAsyncIterator(resultOrStream, (payload) => | ||
executeImpl(buildPerEventExecutionContext(exeContext, payload)), | ||
), | ||
return mapAsyncIterator(resultOrStream, (payload) => | ||
executeImpl(buildPerEventExecutionContext(exeContext, payload)), | ||
); | ||
@@ -1220,3 +1017,3 @@ } | ||
} | ||
const { fields: rootFields } = collectFields( | ||
const rootFields = collectFields( | ||
schema, | ||
@@ -1282,341 +1079,1 @@ fragments, | ||
} | ||
function executeDeferredFragment( | ||
exeContext, | ||
parentType, | ||
sourceValue, | ||
fields, | ||
label, | ||
path, | ||
parentContext, | ||
) { | ||
const asyncPayloadRecord = new AsyncPayloadRecord({ | ||
label, | ||
path, | ||
parentContext, | ||
}); | ||
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); | ||
exeContext.subsequentPayloads.push(asyncPayloadRecord); | ||
} | ||
function executeStreamField( | ||
path, | ||
item, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
label, | ||
parentContext, | ||
) { | ||
const asyncPayloadRecord = new AsyncPayloadRecord({ | ||
label, | ||
path, | ||
parentContext, | ||
}); | ||
let completedItem; | ||
try { | ||
try { | ||
if (isPromise(item)) { | ||
completedItem = item.then((resolved) => | ||
completeValue( | ||
exeContext, | ||
itemType, | ||
fieldNodes, | ||
info, | ||
path, | ||
resolved, | ||
asyncPayloadRecord, | ||
), | ||
); | ||
} else { | ||
completedItem = completeValue( | ||
exeContext, | ||
itemType, | ||
fieldNodes, | ||
info, | ||
path, | ||
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(path)); | ||
return handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
}); | ||
} | ||
} catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(path)); | ||
completedItem = handleFieldError( | ||
error, | ||
itemType, | ||
asyncPayloadRecord.errors, | ||
); | ||
} | ||
} catch (error) { | ||
asyncPayloadRecord.errors.push(error); | ||
asyncPayloadRecord.addData(null); | ||
exeContext.subsequentPayloads.push(asyncPayloadRecord); | ||
return asyncPayloadRecord; | ||
} | ||
if (isPromise(completedItem)) { | ||
completedItem = completedItem.then( | ||
(value) => [value], | ||
(error) => { | ||
asyncPayloadRecord.errors.push(error); | ||
return null; | ||
}, | ||
); | ||
} else { | ||
completedItem = [completedItem]; | ||
} | ||
asyncPayloadRecord.addData(completedItem); | ||
exeContext.subsequentPayloads.push(asyncPayloadRecord); | ||
return asyncPayloadRecord; | ||
} | ||
async function executeStreamIteratorItem( | ||
iterator, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
asyncPayloadRecord, | ||
fieldPath, | ||
) { | ||
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(fieldPath)); | ||
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, | ||
fieldPath, | ||
item, | ||
asyncPayloadRecord, | ||
); | ||
if (isPromise(completedItem)) { | ||
completedItem = completedItem.then(undefined, (rawError) => { | ||
const error = locatedError( | ||
rawError, | ||
fieldNodes, | ||
pathToArray(fieldPath), | ||
); | ||
return handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
}); | ||
} | ||
return { done: false, value: completedItem }; | ||
} catch (rawError) { | ||
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath)); | ||
const value = handleFieldError(error, itemType, asyncPayloadRecord.errors); | ||
return { done: false, value }; | ||
} | ||
} | ||
async function executeStreamIterator( | ||
initialIndex, | ||
iterator, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
path, | ||
label, | ||
parentContext, | ||
) { | ||
const subsequentPayloads = exeContext.subsequentPayloads; | ||
let index = initialIndex; | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
const fieldPath = addPath(path, index, undefined); | ||
const asyncPayloadRecord = new AsyncPayloadRecord({ | ||
label, | ||
path: fieldPath, | ||
parentContext, | ||
iterator, | ||
}); | ||
const dataPromise = executeStreamIteratorItem( | ||
iterator, | ||
exeContext, | ||
fieldNodes, | ||
info, | ||
itemType, | ||
asyncPayloadRecord, | ||
fieldPath, | ||
); | ||
asyncPayloadRecord.addData( | ||
dataPromise | ||
.then(({ value }) => value) | ||
.then( | ||
(value) => [value], | ||
(err) => { | ||
asyncPayloadRecord.errors.push(err); | ||
return null; | ||
}, | ||
), | ||
); | ||
subsequentPayloads.push(asyncPayloadRecord); | ||
try { | ||
// eslint-disable-next-line no-await-in-loop | ||
const { done } = await dataPromise; | ||
if (done) { | ||
break; | ||
} | ||
} catch (err) { | ||
// do nothing, error is already handled above | ||
} | ||
index++; | ||
} | ||
} | ||
function yieldSubsequentPayloads(exeContext, initialResult) { | ||
let _hasReturnedInitialResult = false; | ||
let isDone = false; | ||
async function race() { | ||
if (exeContext.subsequentPayloads.length === 0) { | ||
// async iterable resolver just finished and no more pending payloads | ||
return { | ||
value: { | ||
hasNext: false, | ||
}, | ||
done: false, | ||
}; | ||
} | ||
const asyncPayloadRecord = await new Promise((resolve) => { | ||
exeContext.subsequentPayloads.forEach((payload) => { | ||
const data = payload.getData(); | ||
if (isPromise(data)) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
data.then(() => { | ||
resolve(payload); | ||
}); | ||
} else { | ||
resolve(payload); | ||
} | ||
}); | ||
}); | ||
const data = await asyncPayloadRecord.data; | ||
if (exeContext.subsequentPayloads.length === 0) { | ||
// a different call to next has exhausted all payloads | ||
return { value: undefined, done: true }; | ||
} | ||
const index = exeContext.subsequentPayloads.indexOf(asyncPayloadRecord); | ||
if (index === -1) { | ||
// a different call to next has consumed this payload | ||
return race(); | ||
} | ||
exeContext.subsequentPayloads.splice(index, 1); | ||
if (asyncPayloadRecord.isCompletedIterator) { | ||
// async iterable resolver just finished but there may be pending payloads | ||
// return the next one | ||
return race(); | ||
} | ||
const returnValue = { | ||
data, | ||
path: asyncPayloadRecord.path ? pathToArray(asyncPayloadRecord.path) : [], | ||
hasNext: exeContext.subsequentPayloads.length > 0, | ||
}; | ||
if (asyncPayloadRecord.label) { | ||
returnValue.label = asyncPayloadRecord.label; | ||
} | ||
if (asyncPayloadRecord.errors.length > 0) { | ||
returnValue.errors = asyncPayloadRecord.errors; | ||
} | ||
return { | ||
value: returnValue, | ||
done: false, | ||
}; | ||
} | ||
return { | ||
[Symbol.asyncIterator]() { | ||
return this; | ||
}, | ||
next: () => { | ||
if (!_hasReturnedInitialResult) { | ||
_hasReturnedInitialResult = true; | ||
return Promise.resolve({ | ||
value: { | ||
...initialResult, | ||
hasNext: true, | ||
}, | ||
done: false, | ||
}); | ||
} else if (exeContext.subsequentPayloads.length === 0 || isDone) { | ||
return Promise.resolve({ value: undefined, done: true }); | ||
} | ||
return race(); | ||
}, | ||
async return() { | ||
await Promise.all( | ||
exeContext.subsequentPayloads.map((asyncPayloadRecord) => | ||
asyncPayloadRecord.iterator?.return?.(), | ||
), | ||
); | ||
isDone = true; | ||
return { value: undefined, done: true }; | ||
}, | ||
async throw(error) { | ||
await Promise.all( | ||
exeContext.subsequentPayloads.map((asyncPayloadRecord) => | ||
asyncPayloadRecord.iterator?.return?.(), | ||
), | ||
); | ||
isDone = true; | ||
return Promise.reject(error); | ||
}, | ||
}; | ||
} | ||
class AsyncPayloadRecord { | ||
constructor(opts) { | ||
this.label = opts.label; | ||
this.path = opts.path; | ||
this.parentContext = opts.parentContext; | ||
this.iterator = opts.iterator; | ||
this.errors = []; | ||
} | ||
addData(data) { | ||
this.data = data; | ||
} | ||
getData() { | ||
const parentData = this.parentContext?.getData(); | ||
if (parentData) { | ||
return Promise.resolve(parentData).then(() => this.data); | ||
} | ||
return this.data; | ||
} | ||
setIsCompletedIterator() { | ||
this.isCompletedIterator = true; | ||
} | ||
} |
@@ -14,5 +14,2 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path'; | ||
FormattedExecutionResult, | ||
ExecutionPatchResult, | ||
FormattedExecutionPatchResult, | ||
AsyncExecutionResult, | ||
} from './execute'; | ||
@@ -19,0 +16,0 @@ export { |
@@ -8,6 +8,3 @@ import type { Maybe } from './jsutils/Maybe'; | ||
import type { GraphQLSchema } from './type/schema'; | ||
import type { | ||
AsyncExecutionResult, | ||
ExecutionResult, | ||
} from './execution/execute'; | ||
import type { ExecutionResult } from './execution/execute'; | ||
/** | ||
@@ -64,5 +61,3 @@ * This is the primary entry point function for fulfilling GraphQL operations | ||
} | ||
export declare function graphql( | ||
args: GraphQLArgs, | ||
): Promise<ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, void>>; | ||
export declare function graphql(args: GraphQLArgs): Promise<ExecutionResult>; | ||
/** | ||
@@ -69,0 +64,0 @@ * The graphqlSync function also fulfills GraphQL operations by parsing, |
@@ -1,2 +0,1 @@ | ||
import { isAsyncIterable } from './jsutils/isAsyncIterable.js'; | ||
import { isPromise } from './jsutils/isPromise.js'; | ||
@@ -20,3 +19,3 @@ import { parse } from './language/parser.js'; | ||
// Assert that the execution was synchronous. | ||
if (isPromise(result) || isAsyncIterable(result)) { | ||
if (isPromise(result)) { | ||
throw new Error('GraphQL execution failed to complete synchronously.'); | ||
@@ -23,0 +22,0 @@ } |
@@ -55,4 +55,2 @@ /** | ||
GraphQLSkipDirective, | ||
GraphQLDeferDirective, | ||
GraphQLStreamDirective, | ||
GraphQLDeprecatedDirective, | ||
@@ -207,2 +205,3 @@ GraphQLSpecifiedByDirective, | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -235,2 +234,6 @@ isConstValueNode, | ||
ArgumentNode, | ||
NullabilityAssertionNode, | ||
NonNullAssertionNode, | ||
ErrorBoundaryNode, | ||
ListNullabilityOperatorNode, | ||
ConstArgumentNode, | ||
@@ -300,5 +303,2 @@ FragmentSpreadNode, | ||
FormattedExecutionResult, | ||
ExecutionPatchResult, | ||
FormattedExecutionPatchResult, | ||
AsyncExecutionResult, | ||
} from './execution/index'; | ||
@@ -354,2 +354,3 @@ export { | ||
export { | ||
Anonymizer, | ||
getIntrospectionQuery, | ||
@@ -356,0 +357,0 @@ getOperationAST, |
@@ -60,4 +60,2 @@ /** | ||
GraphQLSkipDirective, | ||
GraphQLDeferDirective, | ||
GraphQLStreamDirective, | ||
GraphQLDeprecatedDirective, | ||
@@ -169,2 +167,3 @@ GraphQLSpecifiedByDirective, | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -241,2 +240,3 @@ isConstValueNode, | ||
export { | ||
Anonymizer, | ||
// Produce the GraphQL query recommended for a full schema introspection. | ||
@@ -243,0 +243,0 @@ // Accepts optional IntrospectionOptions. |
@@ -137,3 +137,6 @@ import type { Kind } from './kinds'; | ||
| EnumTypeExtensionNode | ||
| InputObjectTypeExtensionNode; | ||
| InputObjectTypeExtensionNode | ||
| NonNullAssertionNode | ||
| ErrorBoundaryNode | ||
| ListNullabilityOperatorNode; | ||
/** | ||
@@ -216,5 +219,25 @@ * Utility type listing all nodes indexed by their kind. | ||
readonly arguments?: ReadonlyArray<ArgumentNode>; | ||
readonly nullabilityAssertion?: NullabilityAssertionNode; | ||
readonly directives?: ReadonlyArray<DirectiveNode>; | ||
readonly selectionSet?: SelectionSetNode; | ||
} | ||
export declare type NullabilityAssertionNode = | ||
| NonNullAssertionNode | ||
| ErrorBoundaryNode | ||
| ListNullabilityOperatorNode; | ||
export interface ListNullabilityOperatorNode { | ||
readonly kind: Kind.LIST_NULLABILITY_OPERATOR; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: NullabilityAssertionNode; | ||
} | ||
export interface NonNullAssertionNode { | ||
readonly kind: Kind.NON_NULL_ASSERTION; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: ListNullabilityOperatorNode; | ||
} | ||
export interface ErrorBoundaryNode { | ||
readonly kind: Kind.ERROR_BOUNDARY; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: ListNullabilityOperatorNode; | ||
} | ||
export interface ArgumentNode { | ||
@@ -221,0 +244,0 @@ readonly kind: Kind.ARGUMENT; |
@@ -64,4 +64,18 @@ /** | ||
SelectionSet: ['selections'], | ||
Field: ['alias', 'name', 'arguments', 'directives', 'selectionSet'], | ||
Field: [ | ||
'alias', | ||
'name', | ||
'arguments', | ||
'directives', | ||
'selectionSet', | ||
// Note: Client Controlled Nullability is experimental and may be changed | ||
// or removed in the future. | ||
'nullabilityAssertion', | ||
], | ||
Argument: ['name', 'value'], | ||
// Note: Client Controlled Nullability is experimental and may be changed | ||
// or removed in the future. | ||
ListNullabilityOperator: ['nullabilityAssertion'], | ||
NonNullAssertion: ['nullabilityAssertion'], | ||
ErrorBoundary: ['nullabilityAssertion'], | ||
FragmentSpread: ['name', 'directives'], | ||
@@ -68,0 +82,0 @@ InlineFragment: ['typeCondition', 'directives', 'selectionSet'], |
@@ -27,2 +27,6 @@ export { Source } from './source'; | ||
FieldNode, | ||
NullabilityAssertionNode, | ||
NonNullAssertionNode, | ||
ErrorBoundaryNode, | ||
ListNullabilityOperatorNode, | ||
ArgumentNode, | ||
@@ -81,2 +85,3 @@ ConstArgumentNode, | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -83,0 +88,0 @@ isConstValueNode, |
@@ -20,2 +20,3 @@ export { Source } from './source.js'; | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -22,0 +23,0 @@ isConstValueNode, |
@@ -14,2 +14,6 @@ /** | ||
ARGUMENT = 'Argument', | ||
/** Nullability Modifiers */ | ||
LIST_NULLABILITY_OPERATOR = 'ListNullabilityOperator', | ||
NON_NULL_ASSERTION = 'NonNullAssertion', | ||
ERROR_BOUNDARY = 'ErrorBoundary', | ||
/** Fragments */ | ||
@@ -16,0 +20,0 @@ FRAGMENT_SPREAD = 'FragmentSpread', |
@@ -15,2 +15,6 @@ /** | ||
Kind['ARGUMENT'] = 'Argument'; | ||
/** Nullability Modifiers */ | ||
Kind['LIST_NULLABILITY_OPERATOR'] = 'ListNullabilityOperator'; | ||
Kind['NON_NULL_ASSERTION'] = 'NonNullAssertion'; | ||
Kind['ERROR_BOUNDARY'] = 'ErrorBoundary'; | ||
/** Fragments */ | ||
@@ -17,0 +21,0 @@ Kind['FRAGMENT_SPREAD'] = 'FragmentSpread'; |
@@ -64,2 +64,3 @@ import { syntaxError } from '../error/syntaxError.js'; | ||
kind === TokenKind.BANG || | ||
kind === TokenKind.QUESTION_MARK || | ||
kind === TokenKind.DOLLAR || | ||
@@ -238,2 +239,9 @@ kind === TokenKind.AMP || | ||
return createToken(lexer, TokenKind.BRACE_R, position, position + 1); | ||
case 0x003f: // ? | ||
return createToken( | ||
lexer, | ||
TokenKind.QUESTION_MARK, | ||
position, | ||
position + 1, | ||
); | ||
// StringValue | ||
@@ -240,0 +248,0 @@ case 0x0022: // " |
@@ -31,2 +31,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
NameNode, | ||
NullabilityAssertionNode, | ||
ObjectFieldNode, | ||
@@ -84,2 +85,24 @@ ObjectTypeDefinitionNode, | ||
allowLegacyFragmentVariables?: boolean; | ||
/** | ||
* EXPERIMENTAL: | ||
* | ||
* If enabled, the parser will understand and parse Client Controlled Nullability | ||
* Designators contained in Fields. They'll be represented in the | ||
* `nullabilityAssertion` field of the FieldNode. | ||
* | ||
* The syntax looks like the following: | ||
* | ||
* ```graphql | ||
* { | ||
* nullableField! | ||
* nonNullableField? | ||
* nonNullableSelectionSet? { | ||
* childField! | ||
* } | ||
* } | ||
* ``` | ||
* Note: this feature is experimental and may change or be removed in the | ||
* future. | ||
*/ | ||
experimentalClientControlledNullability?: boolean; | ||
} | ||
@@ -218,2 +241,3 @@ /** | ||
parseField(): FieldNode; | ||
parseNullabilityAssertion(): NullabilityAssertionNode | undefined; | ||
/** | ||
@@ -220,0 +244,0 @@ * Arguments[Const] : ( Argument[?Const]+ ) |
@@ -302,2 +302,5 @@ import { syntaxError } from '../error/syntaxError.js'; | ||
arguments: this.parseArguments(false), | ||
// Experimental support for Client Controlled Nullability changes | ||
// the grammar of Field: | ||
nullabilityAssertion: this.parseNullabilityAssertion(), | ||
directives: this.parseDirectives(false), | ||
@@ -309,2 +312,32 @@ selectionSet: this.peek(TokenKind.BRACE_L) | ||
} | ||
// TODO: add grammar comment after it finalizes | ||
parseNullabilityAssertion() { | ||
// Note: Client Controlled Nullability is experimental and may be changed or | ||
// removed in the future. | ||
if (this._options?.experimentalClientControlledNullability !== true) { | ||
return undefined; | ||
} | ||
const start = this._lexer.token; | ||
let nullabilityAssertion; | ||
if (this.expectOptionalToken(TokenKind.BRACKET_L)) { | ||
const innerModifier = this.parseNullabilityAssertion(); | ||
this.expectToken(TokenKind.BRACKET_R); | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.LIST_NULLABILITY_OPERATOR, | ||
nullabilityAssertion: innerModifier, | ||
}); | ||
} | ||
if (this.expectOptionalToken(TokenKind.BANG)) { | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.NON_NULL_ASSERTION, | ||
nullabilityAssertion, | ||
}); | ||
} else if (this.expectOptionalToken(TokenKind.QUESTION_MARK)) { | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.ERROR_BOUNDARY, | ||
nullabilityAssertion, | ||
}); | ||
} | ||
return nullabilityAssertion; | ||
} | ||
parseArguments(isConst) { | ||
@@ -311,0 +344,0 @@ const item = isConst ? this.parseConstArgument : this.parseArgument; |
@@ -6,2 +6,3 @@ import type { | ||
ExecutableDefinitionNode, | ||
NullabilityAssertionNode, | ||
SelectionNode, | ||
@@ -20,2 +21,5 @@ TypeDefinitionNode, | ||
export declare function isSelectionNode(node: ASTNode): node is SelectionNode; | ||
export declare function isNullabilityAssertionNode( | ||
node: ASTNode, | ||
): node is NullabilityAssertionNode; | ||
export declare function isValueNode(node: ASTNode): node is ValueNode; | ||
@@ -22,0 +26,0 @@ export declare function isConstValueNode(node: ASTNode): node is ConstValueNode; |
@@ -22,2 +22,9 @@ import { Kind } from './kinds.js'; | ||
} | ||
export function isNullabilityAssertionNode(node) { | ||
return ( | ||
node.kind === Kind.LIST_NULLABILITY_OPERATOR || | ||
node.kind === Kind.NON_NULL_ASSERTION || | ||
node.kind === Kind.ERROR_BOUNDARY | ||
); | ||
} | ||
export function isValueNode(node) { | ||
@@ -24,0 +31,0 @@ return ( |
@@ -45,4 +45,11 @@ import { printBlockString } from './blockString.js'; | ||
Field: { | ||
leave({ alias, name, arguments: args, directives, selectionSet }) { | ||
const prefix = wrap('', alias, ': ') + name; | ||
leave({ | ||
alias, | ||
name, | ||
arguments: args, | ||
nullabilityAssertion, | ||
directives, | ||
selectionSet, | ||
}) { | ||
const prefix = join([wrap('', alias, ': '), name], ''); | ||
let argsLine = prefix + wrap('(', join(args, ', '), ')'); | ||
@@ -52,6 +59,29 @@ if (argsLine.length > MAX_LINE_LENGTH) { | ||
} | ||
return join([argsLine, join(directives, ' '), selectionSet], ' '); | ||
return join([ | ||
argsLine, | ||
// Note: Client Controlled Nullability is experimental and may be | ||
// changed or removed in the future. | ||
nullabilityAssertion, | ||
wrap(' ', join(directives, ' ')), | ||
wrap(' ', selectionSet), | ||
]); | ||
}, | ||
}, | ||
Argument: { leave: ({ name, value }) => name + ': ' + value }, | ||
// Nullability Modifiers | ||
ListNullabilityOperator: { | ||
leave({ nullabilityAssertion }) { | ||
return join(['[', nullabilityAssertion, ']']); | ||
}, | ||
}, | ||
NonNullAssertion: { | ||
leave({ nullabilityAssertion }) { | ||
return join([nullabilityAssertion, '!']); | ||
}, | ||
}, | ||
ErrorBoundary: { | ||
leave({ nullabilityAssertion }) { | ||
return join([nullabilityAssertion, '?']); | ||
}, | ||
}, | ||
// Fragments | ||
@@ -58,0 +88,0 @@ FragmentSpread: { |
@@ -9,2 +9,3 @@ /** | ||
BANG = '!', | ||
QUESTION_MARK = '?', | ||
DOLLAR = '$', | ||
@@ -11,0 +12,0 @@ AMP = '&', |
@@ -10,2 +10,3 @@ /** | ||
TokenKind['BANG'] = '!'; | ||
TokenKind['QUESTION_MARK'] = '?'; | ||
TokenKind['DOLLAR'] = '$'; | ||
@@ -12,0 +13,0 @@ TokenKind['AMP'] = '&'; |
{ | ||
"name": "graphql", | ||
"version": "17.0.0-alpha.1.canary.pr.3659.cef660554446d49cec9a0958afb9690dd0b19193", | ||
"version": "17.0.0-alpha.1.canary.pr.3673.53c289997f206acd10388d6a574341c68cc1a30e", | ||
"description": "A Query Language and Runtime which can target any service.", | ||
@@ -35,3 +35,3 @@ "license": "MIT", | ||
"publishConfig": { | ||
"tag": "canary-pr-3659" | ||
"tag": "canary-pr-3673" | ||
}, | ||
@@ -50,3 +50,3 @@ "type": "module", | ||
}, | ||
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/3659, no gurantees provided so please use your own discretion." | ||
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/3673, no gurantees provided so please use your own discretion." | ||
} |
@@ -68,10 +68,2 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
/** | ||
* Used to conditionally defer fragments. | ||
*/ | ||
export declare const GraphQLDeferDirective: GraphQLDirective; | ||
/** | ||
* Used to conditionally stream list fields. | ||
*/ | ||
export declare const GraphQLStreamDirective: GraphQLDirective; | ||
/** | ||
* Constant string used for default reason for a deprecation. | ||
@@ -78,0 +70,0 @@ */ |
@@ -11,3 +11,3 @@ import { inspect } from '../jsutils/inspect.js'; | ||
} from './definition.js'; | ||
import { GraphQLBoolean, GraphQLInt, GraphQLString } from './scalars.js'; | ||
import { GraphQLBoolean, GraphQLString } from './scalars.js'; | ||
/** | ||
@@ -102,48 +102,2 @@ * Test if the given value is a GraphQL directive. | ||
/** | ||
* Used to conditionally defer fragments. | ||
*/ | ||
export const GraphQLDeferDirective = new GraphQLDirective({ | ||
name: 'defer', | ||
description: | ||
'Directs the executor to defer this fragment when the `if` argument is true or undefined.', | ||
locations: [ | ||
DirectiveLocation.FRAGMENT_SPREAD, | ||
DirectiveLocation.INLINE_FRAGMENT, | ||
], | ||
args: { | ||
if: { | ||
type: GraphQLBoolean, | ||
description: 'Deferred when true or undefined.', | ||
}, | ||
label: { | ||
type: GraphQLString, | ||
description: 'Unique name', | ||
}, | ||
}, | ||
}); | ||
/** | ||
* Used to conditionally stream list fields. | ||
*/ | ||
export const GraphQLStreamDirective = new GraphQLDirective({ | ||
name: 'stream', | ||
description: | ||
'Directs the executor to stream plural fields when the `if` argument is true or undefined.', | ||
locations: [DirectiveLocation.FIELD], | ||
args: { | ||
if: { | ||
type: GraphQLBoolean, | ||
description: 'Stream when true or undefined.', | ||
}, | ||
label: { | ||
type: GraphQLString, | ||
description: 'Unique name', | ||
}, | ||
initialCount: { | ||
defaultValue: 0, | ||
type: GraphQLInt, | ||
description: 'Number of items to return immediately', | ||
}, | ||
}, | ||
}); | ||
/** | ||
* Constant string used for default reason for a deprecation. | ||
@@ -150,0 +104,0 @@ */ |
@@ -116,4 +116,2 @@ export type { Path as ResponsePath } from '../jsutils/Path'; | ||
GraphQLSkipDirective, | ||
GraphQLDeferDirective, | ||
GraphQLStreamDirective, | ||
GraphQLDeprecatedDirective, | ||
@@ -120,0 +118,0 @@ GraphQLSpecifiedByDirective, |
@@ -76,4 +76,2 @@ export { | ||
GraphQLSkipDirective, | ||
GraphQLDeferDirective, | ||
GraphQLStreamDirective, | ||
GraphQLDeprecatedDirective, | ||
@@ -80,0 +78,0 @@ GraphQLSpecifiedByDirective, |
@@ -0,1 +1,2 @@ | ||
export { Anonymizer } from './Anonymizer'; | ||
export { getIntrospectionQuery } from './getIntrospectionQuery'; | ||
@@ -2,0 +3,0 @@ export type { |
@@ -0,1 +1,2 @@ | ||
export { Anonymizer } from './Anonymizer.js'; | ||
// Produce the GraphQL query recommended for a full schema introspection. | ||
@@ -2,0 +3,0 @@ export { getIntrospectionQuery } from './getIntrospectionQuery.js'; |
@@ -5,4 +5,2 @@ export { validate } from './validate'; | ||
export { specifiedRules } from './specifiedRules'; | ||
export { DeferStreamDirectiveLabelRule } from './rules/DeferStreamDirectiveLabelRule'; | ||
export { DeferStreamDirectiveOnRootFieldRule } from './rules/DeferStreamDirectiveOnRootFieldRule'; | ||
export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; | ||
@@ -25,3 +23,2 @@ export { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule'; | ||
export { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule'; | ||
export { StreamDirectiveOnListFieldRule } from './rules/StreamDirectiveOnListFieldRule'; | ||
export { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule'; | ||
@@ -28,0 +25,0 @@ export { UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocationRule'; |
@@ -5,6 +5,2 @@ export { validate } from './validate.js'; | ||
export { specifiedRules } from './specifiedRules.js'; | ||
// Spec Section: "Defer And Stream Directive Labels Are Unique" | ||
export { DeferStreamDirectiveLabelRule } from './rules/DeferStreamDirectiveLabelRule.js'; | ||
// Spec Section: "Defer And Stream Directives Are Used On Valid Root Field" | ||
export { DeferStreamDirectiveOnRootFieldRule } from './rules/DeferStreamDirectiveOnRootFieldRule.js'; | ||
// Spec Section: "Executable Definitions" | ||
@@ -44,4 +40,2 @@ export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule.js'; | ||
export { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule.js'; | ||
// Spec Section: "Stream Directives Are Used On List Fields" | ||
export { StreamDirectiveOnListFieldRule } from './rules/StreamDirectiveOnListFieldRule.js'; | ||
// Spec Section: "Argument Uniqueness" | ||
@@ -48,0 +42,0 @@ export { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule.js'; |
@@ -530,12 +530,2 @@ import { inspect } from '../../jsutils/inspect.js'; | ||
} | ||
// FIXME https://github.com/graphql/graphql-js/issues/2203 | ||
const directives1 = /* c8 ignore next */ node1.directives ?? []; | ||
const directives2 = /* c8 ignore next */ node2.directives ?? []; | ||
if (!sameStreams(directives1, directives2)) { | ||
return [ | ||
[responseName, 'they have differing stream directives'], | ||
[node1], | ||
[node2], | ||
]; | ||
} | ||
// The return type for each field. | ||
@@ -588,18 +578,2 @@ const type1 = def1?.type; | ||
} | ||
function getStreamDirective(directives) { | ||
return directives.find((directive) => directive.name.value === 'stream'); | ||
} | ||
function sameStreams(directives1, directives2) { | ||
const stream1 = getStreamDirective(directives1); | ||
const stream2 = getStreamDirective(directives2); | ||
if (!stream1 && !stream2) { | ||
// both fields do not have streams | ||
return true; | ||
} else if (stream1 && stream2) { | ||
// check if both fields have equivalent streams | ||
return stringifyArguments(stream1) === stringifyArguments(stream2); | ||
} | ||
// fields have a mix of stream and no stream | ||
return false; | ||
} | ||
// Two types conflict if both types could not apply to a value simultaneously. | ||
@@ -606,0 +580,0 @@ // Composite types are ignored as their individual field types will be compared |
@@ -28,3 +28,3 @@ import { GraphQLError } from '../../error/GraphQLError.js'; | ||
} | ||
const { fields } = collectFields( | ||
const fields = collectFields( | ||
schema, | ||
@@ -31,0 +31,0 @@ fragments, |
@@ -1,5 +0,1 @@ | ||
// Spec Section: "Defer And Stream Directive Labels Are Unique" | ||
import { DeferStreamDirectiveLabelRule } from './rules/DeferStreamDirectiveLabelRule.js'; | ||
// Spec Section: "Defer And Stream Directives Are Used On Valid Root Field" | ||
import { DeferStreamDirectiveOnRootFieldRule } from './rules/DeferStreamDirectiveOnRootFieldRule.js'; | ||
// Spec Section: "Executable Definitions" | ||
@@ -48,4 +44,2 @@ import { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule.js'; | ||
import { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule.js'; | ||
// Spec Section: "Stream Directives Are Used On List Fields" | ||
import { StreamDirectiveOnListFieldRule } from './rules/StreamDirectiveOnListFieldRule.js'; | ||
import { UniqueArgumentDefinitionNamesRule } from './rules/UniqueArgumentDefinitionNamesRule.js'; | ||
@@ -101,5 +95,2 @@ // Spec Section: "Argument Uniqueness" | ||
UniqueDirectivesPerLocationRule, | ||
DeferStreamDirectiveOnRootFieldRule, | ||
DeferStreamDirectiveLabelRule, | ||
StreamDirectiveOnListFieldRule, | ||
KnownArgumentNamesRule, | ||
@@ -106,0 +97,0 @@ UniqueArgumentNamesRule, |
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
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
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
686651
260
21024