Socket
Socket
Sign inDemoInstall

graphql

Package Overview
Dependencies
Maintainers
7
Versions
259
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql - npm Package Compare versions

Comparing version 17.0.0-alpha.1.canary.pr.3658.null to 17.0.0-alpha.1.canary.pr.3659.5dba20aef36112d13569d5f296ef967383e60d0f

execution/flattenAsyncIterable.d.ts

2

error/GraphQLError.d.ts

@@ -18,3 +18,3 @@ import type { Maybe } from '../jsutils/Maybe';

export interface GraphQLErrorOptions {
nodes?: ReadonlyArray<ASTNode> | ASTNode | null;
nodes?: ReadonlyArray<ASTNode> | ASTNode | null | undefined;
source?: Maybe<Source>;

@@ -21,0 +21,0 @@ positions?: Maybe<ReadonlyArray<number>>;

@@ -9,2 +9,10 @@ import type { ObjMap } from '../jsutils/ObjMap';

import type { GraphQLSchema } from '../type/schema';
export interface PatchFields {
label: string | undefined;
fields: Map<string, ReadonlyArray<FieldNode>>;
}
export interface FieldsAndPatches {
fields: Map<string, ReadonlyArray<FieldNode>>;
patches: Array<PatchFields>;
}
/**

@@ -27,3 +35,3 @@ * Given a selectionSet, collects all of the fields and returns them.

selectionSet: SelectionSetNode,
): Map<string, ReadonlyArray<FieldNode>>;
): FieldsAndPatches;
/**

@@ -47,2 +55,2 @@ * Given an array of field nodes, collects all of the subfields of the passed

fieldNodes: ReadonlyArray<FieldNode>,
): Map<string, ReadonlyArray<FieldNode>>;
): FieldsAndPatches;

@@ -5,2 +5,3 @@ import { AccumulatorMap } from '../jsutils/AccumulatorMap.js';

import {
GraphQLDeferDirective,
GraphQLIncludeDirective,

@@ -28,2 +29,3 @@ GraphQLSkipDirective,

const fields = new AccumulatorMap();
const patches = [];
collectFieldsImpl(

@@ -36,5 +38,6 @@ schema,

fields,
patches,
new Set(),
);
return fields;
return { fields, patches };
}

@@ -60,2 +63,7 @@ /**

const visitedFragmentNames = new Set();
const subPatches = [];
const subFieldsAndPatches = {
fields: subFieldNodes,
patches: subPatches,
};
for (const node of fieldNodes) {

@@ -70,2 +78,3 @@ if (node.selectionSet) {

subFieldNodes,
subPatches,
visitedFragmentNames,

@@ -75,3 +84,3 @@ );

}
return subFieldNodes;
return subFieldsAndPatches;
}

@@ -86,2 +95,3 @@ // eslint-disable-next-line max-params

fields,
patches,
visitedFragmentNames,

@@ -105,11 +115,31 @@ ) {

}
collectFieldsImpl(
schema,
fragments,
variableValues,
runtimeType,
selection.selectionSet,
fields,
visitedFragmentNames,
);
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,
);
}
break;

@@ -119,9 +149,9 @@ }

const fragName = selection.name.value;
if (
visitedFragmentNames.has(fragName) ||
!shouldIncludeNode(variableValues, selection)
) {
if (!shouldIncludeNode(variableValues, selection)) {
continue;
}
visitedFragmentNames.add(fragName);
const defer = getDeferValues(variableValues, selection);
if (visitedFragmentNames.has(fragName) && !defer) {
continue;
}
const fragment = fragments[fragName];

@@ -134,11 +164,33 @@ if (

}
collectFieldsImpl(
schema,
fragments,
variableValues,
runtimeType,
fragment.selectionSet,
fields,
visitedFragmentNames,
);
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,
);
}
break;

@@ -150,2 +202,19 @@ }

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

@@ -152,0 +221,0 @@ * directives, where `@skip` has higher precedence than `@include`.

@@ -59,2 +59,3 @@ import type { Maybe } from '../jsutils/Maybe';

errors: Array<GraphQLError>;
subsequentPayloads: Set<AsyncPayloadRecord>;
}

@@ -66,3 +67,5 @@ /**

* - `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.
* - `incremental` is a list of the results from defer/stream directives.
*/

@@ -85,2 +88,95 @@ export interface ExecutionResult<

}
export declare type ExperimentalExecuteIncrementallyResults<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> =
| {
singleResult: ExecutionResult<TData, TExtensions>;
}
| {
initialResult: InitialIncrementalExecutionResult<TData, TExtensions>;
subsequentResults: AsyncGenerator<
SubsequentIncrementalExecutionResult<TData, TExtensions>,
void,
void
>;
};
export interface InitialIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends ExecutionResult<TData, TExtensions> {
hasNext: boolean;
incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>;
extensions?: TExtensions;
}
export interface FormattedInitialIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends FormattedExecutionResult<TData, TExtensions> {
hasNext: boolean;
incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>;
extensions?: TExtensions;
}
export interface SubsequentIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> {
hasNext: boolean;
incremental?: ReadonlyArray<IncrementalResult<TData, TExtensions>>;
extensions?: TExtensions;
}
export interface FormattedSubsequentIncrementalExecutionResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> {
hasNext: boolean;
incremental?: ReadonlyArray<FormattedIncrementalResult<TData, TExtensions>>;
extensions?: TExtensions;
}
export interface IncrementalDeferResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends ExecutionResult<TData, TExtensions> {
path?: ReadonlyArray<string | number>;
label?: string;
}
export interface FormattedIncrementalDeferResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> extends FormattedExecutionResult<TData, TExtensions> {
path?: ReadonlyArray<string | number>;
label?: string;
}
export interface IncrementalStreamResult<
TData = Array<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLError>;
items?: TData | null;
path?: ReadonlyArray<string | number>;
label?: string;
extensions?: TExtensions;
}
export interface FormattedIncrementalStreamResult<
TData = Array<unknown>,
TExtensions = ObjMap<unknown>,
> {
errors?: ReadonlyArray<GraphQLFormattedError>;
items?: TData | null;
path?: ReadonlyArray<string | number>;
label?: string;
extensions?: TExtensions;
}
export declare type IncrementalResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> =
| IncrementalDeferResult<TData, TExtensions>
| IncrementalStreamResult<TData, TExtensions>;
export declare type FormattedIncrementalResult<
TData = ObjMap<unknown>,
TExtensions = ObjMap<unknown>,
> =
| FormattedIncrementalDeferResult<TData, TExtensions>
| FormattedIncrementalStreamResult<TData, TExtensions>;
export interface ExecutionArgs {

@@ -108,2 +204,8 @@ 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.
*/

@@ -114,2 +216,17 @@ export declare function 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 ExperimentalExecuteIncrementallyResults
* object. This object either contains a single ExecutionResult as
* `singleResult`, or 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(
args: ExecutionArgs,
): PromiseOrValue<ExperimentalExecuteIncrementallyResults>;
/**
* Also implements the "Executing requests" section of the GraphQL specification.

@@ -173,7 +290,7 @@ * However, it guarantees to complete synchronously (or throw an error) assuming

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

@@ -184,3 +301,11 @@ *

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

@@ -193,2 +318,47 @@ export declare function subscribe(

/**
* 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,
): PromiseOrValue<
| AsyncGenerator<
| ExecutionResult
| InitialIncrementalExecutionResult
| SubsequentIncrementalExecutionResult,
void,
void
>
| ExecutionResult
>;
/**
* Implements the "CreateSourceEventStream" algorithm described in the

@@ -224,1 +394,45 @@ * GraphQL specification, resolving the subscription source event stream.

): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult>;
declare class DeferredFragmentRecord {
type: 'defer';
errors: Array<GraphQLError>;
label: string | undefined;
path: Path | undefined;
promise: Promise<void>;
data: ObjMap<unknown> | null;
parentContext: AsyncPayloadRecord | undefined;
isCompleted: boolean;
_exeContext: ExecutionContext;
_resolve?: (arg: PromiseOrValue<ObjMap<unknown> | null>) => void;
constructor(opts: {
label: string | undefined;
path: Path | undefined;
parentContext: AsyncPayloadRecord | undefined;
exeContext: ExecutionContext;
});
addData(data: PromiseOrValue<ObjMap<unknown> | null>): void;
}
declare class StreamRecord {
type: 'stream';
errors: Array<GraphQLError>;
label: string | undefined;
path: Path | undefined;
items: Array<unknown> | null;
promise: Promise<void>;
parentContext: AsyncPayloadRecord | undefined;
iterator: AsyncIterator<unknown> | undefined;
isCompletedIterator?: boolean;
isCompleted: boolean;
_exeContext: ExecutionContext;
_resolve?: (arg: PromiseOrValue<Array<unknown> | null>) => void;
constructor(opts: {
label: string | undefined;
path: Path | undefined;
iterator?: AsyncIterator<unknown>;
parentContext: AsyncPayloadRecord | undefined;
exeContext: ExecutionContext;
});
addItems(items: PromiseOrValue<Array<unknown> | null>): void;
setIsCompletedIterator(): void;
}
declare type AsyncPayloadRecord = DeferredFragmentRecord | StreamRecord;
export {};

@@ -22,2 +22,3 @@ import { inspect } from '../jsutils/inspect.js';

} from '../type/definition.js';
import { GraphQLStreamDirective } from '../type/directives.js';
import { assertValidSchema } from '../type/validate.js';

@@ -28,4 +29,9 @@ import {

} from './collectFields.js';
import { mapAsyncIterator } from './mapAsyncIterator.js';
import { getArgumentValues, getVariableValues } from './values.js';
import { flattenAsyncIterable } from './flattenAsyncIterable.js';
import { mapAsyncIterable } from './mapAsyncIterable.js';
import {
getArgumentValues,
getDirectiveValues,
getVariableValues,
} from './values.js';
/* eslint-disable max-params */

@@ -48,2 +54,4 @@ // This file contains a lot of such errors but we plan to refactor it anyway

);
const UNEXPECTED_MULTIPLE_PAYLOADS =
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)';
/**

@@ -58,4 +66,39 @@ * Implements the "Executing requests" section of the GraphQL specification.

* 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 ('singleResult' in result) {
return result.singleResult;
}
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS);
}
return result.then((incrementalResult) => {
if ('singleResult' in incrementalResult) {
return incrementalResult.singleResult;
}
return {
errors: [new GraphQLError(UNEXPECTED_MULTIPLE_PAYLOADS)],
};
});
}
/**
* 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 ExperimentalExecuteIncrementallyResults
* object. This object either contains a single ExecutionResult as
* `singleResult`, or 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,

@@ -66,3 +109,3 @@ // a "Response" with only errors is returned.

if (!('schema' in exeContext)) {
return { errors: exeContext };
return { singleResult: { errors: exeContext } };
}

@@ -87,13 +130,35 @@ return executeImpl(exeContext);

return result.then(
(data) => buildResponse(data, exeContext.errors),
(data) => {
const initialResult = buildResponse(data, exeContext.errors);
if (exeContext.subsequentPayloads.size > 0) {
return {
initialResult: {
...initialResult,
hasNext: true,
},
subsequentResults: yieldSubsequentPayloads(exeContext),
};
}
return { singleResult: initialResult };
},
(error) => {
exeContext.errors.push(error);
return buildResponse(null, exeContext.errors);
return { singleResult: 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 { singleResult: initialResult };
} catch (error) {
exeContext.errors.push(error);
return buildResponse(null, exeContext.errors);
return { singleResult: buildResponse(null, exeContext.errors) };
}

@@ -107,8 +172,8 @@ }

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.');
}
return result;
return result.singleResult;
}

@@ -198,2 +263,3 @@ /**

subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
subsequentPayloads: new Set(),
errors: [],

@@ -222,3 +288,3 @@ };

}
const rootFields = collectFields(
const { fields: rootFields, patches } = collectFields(
schema,

@@ -231,7 +297,9 @@ fragments,

const path = undefined;
let result;
switch (operation.operation) {
case OperationTypeNode.QUERY:
return executeFields(exeContext, rootType, rootValue, path, rootFields);
result = executeFields(exeContext, rootType, rootValue, path, rootFields);
break;
case OperationTypeNode.MUTATION:
return executeFieldsSerially(
result = executeFieldsSerially(
exeContext,

@@ -243,7 +311,20 @@ rootType,

);
break;
case OperationTypeNode.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);
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;
}

@@ -262,3 +343,3 @@ /**

return promiseReduce(
fields.entries(),
fields,
(results, [responseName, fieldNodes]) => {

@@ -292,6 +373,13 @@ const fieldPath = addPath(path, responseName, parentType.name);

*/
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()) {
for (const [responseName, fieldNodes] of fields) {
const fieldPath = addPath(path, responseName, parentType.name);

@@ -304,2 +392,3 @@ const result = executeField(

fieldPath,
asyncPayloadRecord,
);

@@ -328,3 +417,11 @@ if (result !== undefined) {

*/
function executeField(exeContext, parentType, source, fieldNodes, path) {
function executeField(
exeContext,
parentType,
source,
fieldNodes,
path,
asyncPayloadRecord,
) {
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
const fieldName = fieldNodes[0].name.value;

@@ -362,3 +459,11 @@ const fieldDef = exeContext.schema.getField(parentType, fieldName);

completed = result.then((resolved) =>
completeValue(exeContext, returnType, fieldNodes, info, path, resolved),
completeValue(
exeContext,
returnType,
fieldNodes,
info,
path,
resolved,
asyncPayloadRecord,
),
);

@@ -373,2 +478,3 @@ } else {

result,
asyncPayloadRecord,
);

@@ -381,3 +487,3 @@ }

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

@@ -388,3 +494,3 @@ }

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

@@ -418,3 +524,3 @@ }

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

@@ -427,3 +533,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;

@@ -452,3 +558,11 @@ }

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

@@ -468,2 +582,3 @@ if (result instanceof Error) {

result,
asyncPayloadRecord,
);

@@ -490,2 +605,3 @@ if (completed === null) {

result,
asyncPayloadRecord,
);

@@ -508,2 +624,3 @@ }

result,
asyncPayloadRecord,
);

@@ -520,2 +637,3 @@ }

result,
asyncPayloadRecord,
);

@@ -532,2 +650,114 @@ }

/**
* 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
* recursively until all the results are completed.
*/
async function completeAsyncIteratorValue(
exeContext,
itemType,
fieldNodes,
info,
path,
iterator,
asyncPayloadRecord,
) {
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
const stream = getStreamValues(exeContext, fieldNodes, path);
let containsPromise = false;
const completedResults = [];
let index = 0;
// eslint-disable-next-line no-constant-condition
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);
try {
// eslint-disable-next-line no-await-in-loop
const { value, done } = await iterator.next();
if (done) {
break;
}
try {
// TODO can the error checking logic be consolidated with completeListValue?
const completedItem = completeValue(
exeContext,
itemType,
fieldNodes,
info,
fieldPath,
value,
asyncPayloadRecord,
);
if (isPromise(completedItem)) {
containsPromise = true;
}
completedResults.push(completedItem);
} catch (rawError) {
completedResults.push(null);
const error = locatedError(
rawError,
fieldNodes,
pathToArray(fieldPath),
);
handleFieldError(error, itemType, errors);
}
} catch (rawError) {
completedResults.push(null);
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath));
handleFieldError(error, itemType, errors);
break;
}
index += 1;
}
return containsPromise ? Promise.all(completedResults) : completedResults;
}
/**
* Complete a list value by completing each item in the list with the

@@ -543,3 +773,18 @@ * inner type

result,
asyncPayloadRecord,
) {
const itemType = returnType.ofType;
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
if (isAsyncIterable(result)) {
const iterator = result[Symbol.asyncIterator]();
return completeAsyncIteratorValue(
exeContext,
itemType,
fieldNodes,
info,
path,
iterator,
asyncPayloadRecord,
);
}
if (!isIterableObject(result)) {

@@ -550,7 +795,10 @@ throw new GraphQLError(

}
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.
const itemType = returnType.ofType;
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,

@@ -561,2 +809,20 @@ // 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)) {

@@ -571,2 +837,3 @@ completedItem = item.then((resolved) =>

resolved,
asyncPayloadRecord,
),

@@ -582,2 +849,3 @@ );

item,
asyncPayloadRecord,
);

@@ -589,17 +857,21 @@ }

// 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);
});
completedResults.push(
completedItem.then(undefined, (rawError) => {
const error = locatedError(
rawError,
fieldNodes,
pathToArray(itemPath),
);
return handleFieldError(error, itemType, errors);
}),
);
} else {
completedResults.push(completedItem);
}
return completedItem;
} catch (rawError) {
const error = locatedError(rawError, fieldNodes, pathToArray(itemPath));
return handleFieldError(error, itemType, exeContext);
completedResults.push(handleFieldError(error, itemType, errors));
}
});
index++;
}
return containsPromise ? Promise.all(completedResults) : completedResults;

@@ -632,2 +904,3 @@ }

result,
asyncPayloadRecord,
) {

@@ -653,2 +926,3 @@ const resolveTypeFn = returnType.resolveType ?? exeContext.typeResolver;

result,
asyncPayloadRecord,
),

@@ -671,2 +945,3 @@ );

result,
asyncPayloadRecord,
);

@@ -732,5 +1007,4 @@ }

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

@@ -746,8 +1020,9 @@ // current result. If isTypeOf returns false, then raise an error rather

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

@@ -760,3 +1035,10 @@ });

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

@@ -769,2 +1051,38 @@ 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;
}
/**

@@ -844,7 +1162,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`.

@@ -855,5 +1173,67 @@ *

*
* 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)
? mapAsyncIterable(resultOrIterable, ensureSingleExecutionResult)
: resultOrIterable,
);
}
return isAsyncIterable(maybePromise)
? mapAsyncIterable(maybePromise, ensureSingleExecutionResult)
: maybePromise;
}
function ensureSingleExecutionResult(result) {
if ('hasNext' in result) {
return {
errors: [new GraphQLError(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,

@@ -874,2 +1254,10 @@ // a "Response" with only errors is returned.

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

@@ -885,4 +1273,8 @@ if (!isAsyncIterable(resultOrStream)) {

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

@@ -949,3 +1341,3 @@ }

}
const rootFields = collectFields(
const { fields: rootFields } = collectFields(
schema,

@@ -957,3 +1349,4 @@ fragments,

);
const [responseName, fieldNodes] = [...rootFields.entries()][0];
const firstRootField = rootFields.entries().next().value;
const [responseName, fieldNodes] = firstRootField;
const fieldName = fieldNodes[0].name.value;

@@ -1012,1 +1405,383 @@ const fieldDef = schema.getField(rootType, fieldName);

}
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,
item,
exeContext,
fieldNodes,
info,
itemType,
label,
parentContext,
) {
const asyncPayloadRecord = new StreamRecord({
label,
path,
parentContext,
exeContext,
});
let completedItem;
let completedItems;
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));
completedItems = handleFieldError(
error,
itemType,
asyncPayloadRecord.errors,
);
}
} catch (error) {
asyncPayloadRecord.errors.push(error);
asyncPayloadRecord.addItems(null);
return asyncPayloadRecord;
}
if (isPromise(completedItem)) {
completedItems = completedItem.then(
(value) => [value],
(error) => {
asyncPayloadRecord.errors.push(error);
return null;
},
);
} else {
completedItems = [completedItem];
}
asyncPayloadRecord.addItems(completedItems);
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,
) {
let index = initialIndex;
let previousAsyncPayloadRecord = parentContext ?? undefined;
// eslint-disable-next-line no-constant-condition
while (true) {
const fieldPath = addPath(path, index, undefined);
const asyncPayloadRecord = new StreamRecord({
label,
path: fieldPath,
parentContext: previousAsyncPayloadRecord,
iterator,
exeContext,
});
const dataPromise = executeStreamIteratorItem(
iterator,
exeContext,
fieldNodes,
info,
itemType,
asyncPayloadRecord,
fieldPath,
);
asyncPayloadRecord.addItems(
dataPromise
.then(({ value }) => value)
.then(
(value) => [value],
(err) => {
asyncPayloadRecord.errors.push(err);
return null;
},
),
);
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
}
previousAsyncPayloadRecord = asyncPayloadRecord;
index++;
}
}
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;
}
incrementalResult.path = asyncPayloadRecord.path
? pathToArray(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 (!incremental.length && !hasNext) {
isDone = true;
return {
value: { hasNext },
done: false,
};
}
if (!hasNext) {
isDone = true;
}
return {
value: { incremental, hasNext },
done: false,
};
}
function returnStreamIterators() {
const promises = [];
exeContext.subsequentPayloads.forEach((asyncPayloadRecord) => {
if (
isStreamPayload(asyncPayloadRecord) &&
asyncPayloadRecord.iterator?.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 = 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 = (promiseOrValue) => {
resolve(promiseOrValue);
};
}).then((data) => {
this.data = data;
this.isCompleted = true;
});
}
addData(data) {
const parentData = this.parentContext?.promise;
if (parentData) {
this._resolve?.(parentData.then(() => data));
return;
}
this._resolve?.(data);
}
}
class StreamRecord {
constructor(opts) {
this.type = 'stream';
this.items = null;
this.label = opts.label;
this.path = 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 = (promiseOrValue) => {
resolve(promiseOrValue);
};
}).then((items) => {
this.items = items;
this.isCompleted = true;
});
}
addItems(items) {
const parentData = this.parentContext?.promise;
if (parentData) {
this._resolve?.(parentData.then(() => items));
return;
}
this._resolve?.(items);
}
setIsCompletedIterator() {
this.isCompletedIterator = true;
}
}
function isStreamPayload(asyncPayload) {
return asyncPayload.type === 'stream';
}

@@ -5,2 +5,3 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path';

execute,
experimentalExecuteIncrementally,
executeSync,

@@ -10,2 +11,3 @@ defaultFieldResolver,

subscribe,
experimentalSubscribeIncrementally,
} from './execute';

@@ -15,3 +17,14 @@ export type {

ExecutionResult,
ExperimentalExecuteIncrementallyResults,
InitialIncrementalExecutionResult,
SubsequentIncrementalExecutionResult,
IncrementalDeferResult,
IncrementalStreamResult,
IncrementalResult,
FormattedExecutionResult,
FormattedInitialIncrementalExecutionResult,
FormattedSubsequentIncrementalExecutionResult,
FormattedIncrementalDeferResult,
FormattedIncrementalStreamResult,
FormattedIncrementalResult,
} from './execute';

@@ -18,0 +31,0 @@ export {

@@ -5,2 +5,3 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path.js';

execute,
experimentalExecuteIncrementally,
executeSync,

@@ -10,2 +11,3 @@ defaultFieldResolver,

subscribe,
experimentalSubscribeIncrementally,
} from './execute.js';

@@ -12,0 +14,0 @@ export {

@@ -71,3 +71,3 @@ import type { Maybe } from '../jsutils/Maybe';

node: {
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
},

@@ -74,0 +74,0 @@ variableValues?: Maybe<ObjMap<unknown>>,

@@ -18,2 +18,4 @@ import type { Maybe } from './jsutils/Maybe';

*
* This function does not support incremental delivery (`@defer` and `@stream`).
*
* Accepts either an object with named arguments, or individual arguments:

@@ -20,0 +22,0 @@ *

@@ -55,2 +55,4 @@ /**

GraphQLSkipDirective,
GraphQLDeferDirective,
GraphQLStreamDirective,
GraphQLDeprecatedDirective,

@@ -205,2 +207,3 @@ GraphQLSpecifiedByDirective,

isSelectionNode,
isNullabilityAssertionNode,
isValueNode,

@@ -233,2 +236,6 @@ isConstValueNode,

ArgumentNode,
NullabilityAssertionNode,
NonNullAssertionNode,
ErrorBoundaryNode,
ListNullabilityOperatorNode,
ConstArgumentNode,

@@ -284,2 +291,3 @@ FragmentSpreadNode,

execute,
experimentalExecuteIncrementally,
executeSync,

@@ -293,2 +301,3 @@ defaultFieldResolver,

subscribe,
experimentalSubscribeIncrementally,
createSourceEventStream,

@@ -299,3 +308,14 @@ } from './execution/index';

ExecutionResult,
ExperimentalExecuteIncrementallyResults,
InitialIncrementalExecutionResult,
SubsequentIncrementalExecutionResult,
IncrementalDeferResult,
IncrementalStreamResult,
IncrementalResult,
FormattedExecutionResult,
FormattedInitialIncrementalExecutionResult,
FormattedSubsequentIncrementalExecutionResult,
FormattedIncrementalDeferResult,
FormattedIncrementalStreamResult,
FormattedIncrementalResult,
} from './execution/index';

@@ -302,0 +322,0 @@ export {

@@ -60,2 +60,4 @@ /**

GraphQLSkipDirective,
GraphQLDeferDirective,
GraphQLStreamDirective,
GraphQLDeprecatedDirective,

@@ -167,2 +169,3 @@ GraphQLSpecifiedByDirective,

isSelectionNode,
isNullabilityAssertionNode,
isValueNode,

@@ -179,2 +182,3 @@ isConstValueNode,

execute,
experimentalExecuteIncrementally,
executeSync,

@@ -188,2 +192,3 @@ defaultFieldResolver,

subscribe,
experimentalSubscribeIncrementally,
createSourceEventStream,

@@ -190,0 +195,0 @@ } from './execution/index.js';

@@ -9,6 +9,8 @@ /**

export function promiseForObject(object) {
return Promise.all(Object.values(object)).then((resolvedValues) => {
const keys = Object.keys(object);
const values = Object.values(object);
return Promise.all(values).then((resolvedValues) => {
const resolvedObject = Object.create(null);
for (const [i, key] of Object.keys(object).entries()) {
resolvedObject[key] = resolvedValues[i];
for (let i = 0; i < keys.length; ++i) {
resolvedObject[keys[i]] = resolvedValues[i];
}

@@ -15,0 +17,0 @@ return resolvedObject;

@@ -137,3 +137,6 @@ import type { Kind } from './kinds';

| EnumTypeExtensionNode
| InputObjectTypeExtensionNode;
| InputObjectTypeExtensionNode
| NonNullAssertionNode
| ErrorBoundaryNode
| ListNullabilityOperatorNode;
/**

@@ -158,3 +161,3 @@ * Utility type listing all nodes indexed by their kind.

readonly kind: Kind.NAME;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: string;

@@ -165,3 +168,3 @@ }

readonly kind: Kind.DOCUMENT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly definitions: ReadonlyArray<DefinitionNode>;

@@ -178,7 +181,9 @@ }

readonly kind: Kind.OPERATION_DEFINITION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly operation: OperationTypeNode;
readonly name?: NameNode;
readonly variableDefinitions?: ReadonlyArray<VariableDefinitionNode>;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly name?: NameNode | undefined;
readonly variableDefinitions?:
| ReadonlyArray<VariableDefinitionNode>
| undefined;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
readonly selectionSet: SelectionSetNode;

@@ -193,11 +198,11 @@ }

readonly kind: Kind.VARIABLE_DEFINITION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly variable: VariableNode;
readonly type: TypeNode;
readonly defaultValue?: ConstValueNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly defaultValue?: ConstValueNode | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface VariableNode {
readonly kind: Kind.VARIABLE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -207,3 +212,3 @@ }

kind: Kind.SELECTION_SET;
loc?: Location;
loc?: Location | undefined;
selections: ReadonlyArray<SelectionNode>;

@@ -217,12 +222,32 @@ }

readonly kind: Kind.FIELD;
readonly loc?: Location;
readonly alias?: NameNode;
readonly loc?: Location | undefined;
readonly alias?: NameNode | undefined;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<ArgumentNode>;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly selectionSet?: SelectionSetNode;
readonly arguments?: ReadonlyArray<ArgumentNode> | undefined;
readonly nullabilityAssertion?: NullabilityAssertionNode | undefined;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
readonly selectionSet?: SelectionSetNode | undefined;
}
export declare type NullabilityAssertionNode =
| NonNullAssertionNode
| ErrorBoundaryNode
| ListNullabilityOperatorNode;
export interface ListNullabilityOperatorNode {
readonly kind: Kind.LIST_NULLABILITY_OPERATOR;
readonly loc?: Location | undefined;
readonly nullabilityAssertion?: NullabilityAssertionNode | undefined;
}
export interface NonNullAssertionNode {
readonly kind: Kind.NON_NULL_ASSERTION;
readonly loc?: Location | undefined;
readonly nullabilityAssertion?: ListNullabilityOperatorNode | undefined;
}
export interface ErrorBoundaryNode {
readonly kind: Kind.ERROR_BOUNDARY;
readonly loc?: Location | undefined;
readonly nullabilityAssertion?: ListNullabilityOperatorNode | undefined;
}
export interface ArgumentNode {
readonly kind: Kind.ARGUMENT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -233,3 +258,3 @@ readonly value: ValueNode;

readonly kind: Kind.ARGUMENT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -241,11 +266,11 @@ readonly value: ConstValueNode;

readonly kind: Kind.FRAGMENT_SPREAD;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
}
export interface InlineFragmentNode {
readonly kind: Kind.INLINE_FRAGMENT;
readonly loc?: Location;
readonly typeCondition?: NamedTypeNode;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly loc?: Location | undefined;
readonly typeCondition?: NamedTypeNode | undefined;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
readonly selectionSet: SelectionSetNode;

@@ -255,8 +280,10 @@ }

readonly kind: Kind.FRAGMENT_DEFINITION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
/** @deprecated variableDefinitions will be removed in v17.0.0 */
readonly variableDefinitions?: ReadonlyArray<VariableDefinitionNode>;
readonly variableDefinitions?:
| ReadonlyArray<VariableDefinitionNode>
| undefined;
readonly typeCondition: NamedTypeNode;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly directives?: ReadonlyArray<DirectiveNode> | undefined;
readonly selectionSet: SelectionSetNode;

@@ -286,3 +313,3 @@ }

readonly kind: Kind.INT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: string;

@@ -292,3 +319,3 @@ }

readonly kind: Kind.FLOAT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: string;

@@ -298,9 +325,9 @@ }

readonly kind: Kind.STRING;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: string;
readonly block?: boolean;
readonly block?: boolean | undefined;
}
export interface BooleanValueNode {
readonly kind: Kind.BOOLEAN;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: boolean;

@@ -310,7 +337,7 @@ }

readonly kind: Kind.NULL;
readonly loc?: Location;
readonly loc?: Location | undefined;
}
export interface EnumValueNode {
readonly kind: Kind.ENUM;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly value: string;

@@ -320,3 +347,3 @@ }

readonly kind: Kind.LIST;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly values: ReadonlyArray<ValueNode>;

@@ -326,3 +353,3 @@ }

readonly kind: Kind.LIST;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly values: ReadonlyArray<ConstValueNode>;

@@ -332,3 +359,3 @@ }

readonly kind: Kind.OBJECT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly fields: ReadonlyArray<ObjectFieldNode>;

@@ -338,3 +365,3 @@ }

readonly kind: Kind.OBJECT;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly fields: ReadonlyArray<ConstObjectFieldNode>;

@@ -344,3 +371,3 @@ }

readonly kind: Kind.OBJECT_FIELD;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -351,3 +378,3 @@ readonly value: ValueNode;

readonly kind: Kind.OBJECT_FIELD;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -359,11 +386,11 @@ readonly value: ConstValueNode;

readonly kind: Kind.DIRECTIVE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<ArgumentNode>;
readonly arguments?: ReadonlyArray<ArgumentNode> | undefined;
}
export interface ConstDirectiveNode {
readonly kind: Kind.DIRECTIVE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<ConstArgumentNode>;
readonly arguments?: ReadonlyArray<ConstArgumentNode> | undefined;
}

@@ -374,3 +401,3 @@ /** Type Reference */

readonly kind: Kind.NAMED_TYPE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;

@@ -380,3 +407,3 @@ }

readonly kind: Kind.LIST_TYPE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly type: TypeNode;

@@ -386,3 +413,3 @@ }

readonly kind: Kind.NON_NULL_TYPE;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly type: NamedTypeNode | ListTypeNode;

@@ -397,5 +424,5 @@ }

readonly kind: Kind.SCHEMA_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly operationTypes: ReadonlyArray<OperationTypeDefinitionNode>;

@@ -405,3 +432,3 @@ }

readonly kind: Kind.OPERATION_TYPE_DEFINITION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly operation: OperationTypeNode;

@@ -420,73 +447,73 @@ readonly type: NamedTypeNode;

readonly kind: Kind.SCALAR_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface ObjectTypeDefinitionNode {
readonly kind: Kind.OBJECT_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly interfaces?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<FieldDefinitionNode>;
readonly interfaces?: ReadonlyArray<NamedTypeNode> | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<FieldDefinitionNode> | undefined;
}
export interface FieldDefinitionNode {
readonly kind: Kind.FIELD_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<InputValueDefinitionNode>;
readonly arguments?: ReadonlyArray<InputValueDefinitionNode> | undefined;
readonly type: TypeNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface InputValueDefinitionNode {
readonly kind: Kind.INPUT_VALUE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly type: TypeNode;
readonly defaultValue?: ConstValueNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly defaultValue?: ConstValueNode | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface InterfaceTypeDefinitionNode {
readonly kind: Kind.INTERFACE_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly interfaces?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<FieldDefinitionNode>;
readonly interfaces?: ReadonlyArray<NamedTypeNode> | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<FieldDefinitionNode> | undefined;
}
export interface UnionTypeDefinitionNode {
readonly kind: Kind.UNION_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly types?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly types?: ReadonlyArray<NamedTypeNode> | undefined;
}
export interface EnumTypeDefinitionNode {
readonly kind: Kind.ENUM_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly values?: ReadonlyArray<EnumValueDefinitionNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly values?: ReadonlyArray<EnumValueDefinitionNode> | undefined;
}
export interface EnumValueDefinitionNode {
readonly kind: Kind.ENUM_VALUE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface InputObjectTypeDefinitionNode {
readonly kind: Kind.INPUT_OBJECT_TYPE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<InputValueDefinitionNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<InputValueDefinitionNode> | undefined;
}

@@ -496,6 +523,6 @@ /** Directive Definitions */

readonly kind: Kind.DIRECTIVE_DEFINITION;
readonly loc?: Location;
readonly description?: StringValueNode;
readonly loc?: Location | undefined;
readonly description?: StringValueNode | undefined;
readonly name: NameNode;
readonly arguments?: ReadonlyArray<InputValueDefinitionNode>;
readonly arguments?: ReadonlyArray<InputValueDefinitionNode> | undefined;
readonly repeatable: boolean;

@@ -510,5 +537,7 @@ readonly locations: ReadonlyArray<NameNode>;

readonly kind: Kind.SCHEMA_EXTENSION;
readonly loc?: Location;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly operationTypes?: ReadonlyArray<OperationTypeDefinitionNode>;
readonly loc?: Location | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly operationTypes?:
| ReadonlyArray<OperationTypeDefinitionNode>
| undefined;
}

@@ -525,42 +554,42 @@ /** Type Extensions */

readonly kind: Kind.SCALAR_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
}
export interface ObjectTypeExtensionNode {
readonly kind: Kind.OBJECT_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly interfaces?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<FieldDefinitionNode>;
readonly interfaces?: ReadonlyArray<NamedTypeNode> | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<FieldDefinitionNode> | undefined;
}
export interface InterfaceTypeExtensionNode {
readonly kind: Kind.INTERFACE_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly interfaces?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<FieldDefinitionNode>;
readonly interfaces?: ReadonlyArray<NamedTypeNode> | undefined;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<FieldDefinitionNode> | undefined;
}
export interface UnionTypeExtensionNode {
readonly kind: Kind.UNION_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly types?: ReadonlyArray<NamedTypeNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly types?: ReadonlyArray<NamedTypeNode> | undefined;
}
export interface EnumTypeExtensionNode {
readonly kind: Kind.ENUM_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly values?: ReadonlyArray<EnumValueDefinitionNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly values?: ReadonlyArray<EnumValueDefinitionNode> | undefined;
}
export interface InputObjectTypeExtensionNode {
readonly kind: Kind.INPUT_OBJECT_TYPE_EXTENSION;
readonly loc?: Location;
readonly loc?: Location | undefined;
readonly name: NameNode;
readonly directives?: ReadonlyArray<ConstDirectiveNode>;
readonly fields?: ReadonlyArray<InputValueDefinitionNode>;
readonly directives?: ReadonlyArray<ConstDirectiveNode> | undefined;
readonly fields?: ReadonlyArray<InputValueDefinitionNode> | undefined;
}

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

@@ -67,4 +68,12 @@ ObjectTypeDefinitionNode,

*/
noLocation?: boolean;
noLocation?: boolean | undefined;
/**
* Parser CPU and memory usage is linear to the number of tokens in a document
* however in extreme cases it becomes quadratic due to memory exhaustion.
* Parsing happens before validation so even invalid queries can burn lots of
* CPU time and memory.
* To prevent this you can set a maximum number of tokens allowed within a document.
*/
maxTokens?: number | undefined;
/**
* @deprecated will be removed in the v17.0.0

@@ -84,3 +93,25 @@ *

*/
allowLegacyFragmentVariables?: boolean;
allowLegacyFragmentVariables?: boolean | undefined;
/**
* 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 | undefined;
}

@@ -93,3 +124,3 @@ /**

source: string | Source,
options?: ParseOptions,
options?: ParseOptions | undefined,
): DocumentNode;

@@ -108,3 +139,3 @@ /**

source: string | Source,
options?: ParseOptions,
options?: ParseOptions | undefined,
): ValueNode;

@@ -117,3 +148,3 @@ /**

source: string | Source,
options?: ParseOptions,
options?: ParseOptions | undefined,
): ConstValueNode;

@@ -132,3 +163,3 @@ /**

source: string | Source,
options?: ParseOptions,
options?: ParseOptions | undefined,
): TypeNode;

@@ -147,4 +178,5 @@ /**

export declare class Parser {
protected _options: Maybe<ParseOptions>;
protected _options: ParseOptions;
protected _lexer: Lexer;
protected _tokenCounter: number;
constructor(source: string | Source, options?: ParseOptions);

@@ -224,2 +256,3 @@ /**

parseField(): FieldNode;
parseNullabilityAssertion(): NullabilityAssertionNode | undefined;
/**

@@ -499,3 +532,3 @@ * Arguments[Const] : ( Argument[?Const]+ )

T extends {
loc?: Location;
loc?: Location | undefined;
},

@@ -564,2 +597,3 @@ >(startToken: Token, node: T): T;

delimitedMany<T>(delimiterKind: TokenKind, parseFn: () => T): Array<T>;
advanceLexer(): void;
}

@@ -73,6 +73,7 @@ import { syntaxError } from '../error/syntaxError.js';

export class Parser {
constructor(source, options) {
constructor(source, options = {}) {
const sourceObj = isSource(source) ? source : new Source(source);
this._lexer = new Lexer(sourceObj);
this._options = options;
this._tokenCounter = 0;
}

@@ -303,2 +304,5 @@ /**

arguments: this.parseArguments(false),
// Experimental support for Client Controlled Nullability changes
// the grammar of Field:
nullabilityAssertion: this.parseNullabilityAssertion(),
directives: this.parseDirectives(false),

@@ -310,2 +314,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) {

@@ -366,3 +400,3 @@ const item = isConst ? this.parseConstArgument : this.parseArgument;

// - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
if (this._options?.allowLegacyFragmentVariables === true) {
if (this._options.allowLegacyFragmentVariables === true) {
return this.node(start, {

@@ -402,3 +436,3 @@ kind: Kind.FRAGMENT_DEFINITION,

case TokenKind.INT:
this._lexer.advance();
this.advanceLexer();
return this.node(token, {

@@ -409,3 +443,3 @@ kind: Kind.INT,

case TokenKind.FLOAT:
this._lexer.advance();
this.advanceLexer();
return this.node(token, {

@@ -419,3 +453,3 @@ kind: Kind.FLOAT,

case TokenKind.NAME:
this._lexer.advance();
this.advanceLexer();
switch (token.value) {

@@ -464,3 +498,3 @@ case 'true':

const token = this._lexer.token;
this._lexer.advance();
this.advanceLexer();
return this.node(token, {

@@ -1140,3 +1174,3 @@ kind: Kind.STRING,

node(startToken, node) {
if (this._options?.noLocation !== true) {
if (this._options.noLocation !== true) {
node.loc = new Location(

@@ -1163,3 +1197,3 @@ startToken,

if (token.kind === kind) {
this._lexer.advance();
this.advanceLexer();
return token;

@@ -1180,3 +1214,3 @@ }

if (token.kind === kind) {
this._lexer.advance();
this.advanceLexer();
return true;

@@ -1193,3 +1227,3 @@ }

if (token.kind === TokenKind.NAME && token.value === value) {
this._lexer.advance();
this.advanceLexer();
} else {

@@ -1210,3 +1244,3 @@ throw syntaxError(

if (token.kind === TokenKind.NAME && token.value === value) {
this._lexer.advance();
this.advanceLexer();
return true;

@@ -1282,2 +1316,16 @@ }

}
advanceLexer() {
const { maxTokens } = this._options;
const token = this._lexer.advance();
if (maxTokens !== undefined && token.kind !== TokenKind.EOF) {
++this._tokenCounter;
if (this._tokenCounter > maxTokens) {
throw syntaxError(
this._lexer.source,
token.start,
`Document contains more that ${maxTokens} tokens. Parsing aborted.`,
);
}
}
}
}

@@ -1284,0 +1332,0 @@ /**

@@ -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'] = '&';

@@ -14,4 +14,4 @@ import type { ASTNode } from './ast';

interface EnterLeaveVisitor<TVisitedNode extends ASTNode> {
readonly enter?: ASTVisitFn<TVisitedNode>;
readonly leave?: ASTVisitFn<TVisitedNode>;
readonly enter?: ASTVisitFn<TVisitedNode> | undefined;
readonly leave?: ASTVisitFn<TVisitedNode> | undefined;
}

@@ -18,0 +18,0 @@ /**

{
"name": "graphql",
"version": "17.0.0-alpha.1.canary.pr.3658.null",
"version": "17.0.0-alpha.1.canary.pr.3659.5dba20aef36112d13569d5f296ef967383e60d0f",
"description": "A Query Language and Runtime which can target any service.",

@@ -35,3 +35,3 @@ "license": "MIT",

"publishConfig": {
"tag": "canary-pr-3658"
"tag": "canary-pr-3659"
},

@@ -50,3 +50,3 @@ "type": "module",

},
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/3658, no gurantees provided so please use your own discretion."
"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."
}

@@ -328,7 +328,7 @@ import type { Maybe } from '../jsutils/Maybe';

/** Serializes an internal value to include in a response. */
serialize?: GraphQLScalarSerializer<TExternal>;
serialize?: GraphQLScalarSerializer<TExternal> | undefined;
/** Parses an externally provided value to use as an input. */
parseValue?: GraphQLScalarValueParser<TInternal>;
parseValue?: GraphQLScalarValueParser<TInternal> | undefined;
/** Parses an externally provided literal value to use as an input. */
parseLiteral?: GraphQLScalarLiteralParser<TInternal>;
parseLiteral?: GraphQLScalarLiteralParser<TInternal> | undefined;
extensions?: Maybe<Readonly<GraphQLScalarTypeExtensions>>;

@@ -430,3 +430,3 @@ astNode?: Maybe<ScalarTypeDefinitionNode>;

description?: Maybe<string>;
interfaces?: ThunkReadonlyArray<GraphQLInterfaceType>;
interfaces?: ThunkReadonlyArray<GraphQLInterfaceType> | undefined;
fields: ThunkObjMap<GraphQLFieldConfig<TSource, TContext>>;

@@ -499,5 +499,5 @@ isTypeOf?: Maybe<GraphQLIsTypeOfFn<TSource, TContext>>;

type: GraphQLOutputType;
args?: GraphQLFieldConfigArgumentMap;
resolve?: GraphQLFieldResolver<TSource, TContext, TArgs>;
subscribe?: GraphQLFieldResolver<TSource, TContext, TArgs>;
args?: GraphQLFieldConfigArgumentMap | undefined;
resolve?: GraphQLFieldResolver<TSource, TContext, TArgs> | undefined;
subscribe?: GraphQLFieldResolver<TSource, TContext, TArgs> | undefined;
deprecationReason?: Maybe<string>;

@@ -539,4 +539,4 @@ extensions?: Maybe<

args: ReadonlyArray<GraphQLArgument>;
resolve?: GraphQLFieldResolver<TSource, TContext, TArgs>;
subscribe?: GraphQLFieldResolver<TSource, TContext, TArgs>;
resolve?: GraphQLFieldResolver<TSource, TContext, TArgs> | undefined;
subscribe?: GraphQLFieldResolver<TSource, TContext, TArgs> | undefined;
deprecationReason: Maybe<string>;

@@ -610,3 +610,3 @@ extensions: Readonly<GraphQLFieldExtensions<TSource, TContext, TArgs>>;

description?: Maybe<string>;
interfaces?: ThunkReadonlyArray<GraphQLInterfaceType>;
interfaces?: ThunkReadonlyArray<GraphQLInterfaceType> | undefined;
fields: ThunkObjMap<GraphQLFieldConfig<TSource, TContext>>;

@@ -613,0 +613,0 @@ /**

@@ -68,2 +68,10 @@ 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.

@@ -70,0 +78,0 @@ */

@@ -11,3 +11,3 @@ import { inspect } from '../jsutils/inspect.js';

} from './definition.js';
import { GraphQLBoolean, GraphQLString } from './scalars.js';
import { GraphQLBoolean, GraphQLInt, GraphQLString } from './scalars.js';
/**

@@ -102,2 +102,50 @@ * 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: new GraphQLNonNull(GraphQLBoolean),
description: 'Deferred when true or undefined.',
defaultValue: true,
},
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: new GraphQLNonNull(GraphQLBoolean),
description: 'Stream when true or undefined.',
defaultValue: true,
},
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.

@@ -104,0 +152,0 @@ */

@@ -116,2 +116,4 @@ export type { Path as ResponsePath } from '../jsutils/Path';

GraphQLSkipDirective,
GraphQLDeferDirective,
GraphQLStreamDirective,
GraphQLDeprecatedDirective,

@@ -118,0 +120,0 @@ GraphQLSpecifiedByDirective,

@@ -76,2 +76,4 @@ export {

GraphQLSkipDirective,
GraphQLDeferDirective,
GraphQLStreamDirective,
GraphQLDeprecatedDirective,

@@ -78,0 +80,0 @@ GraphQLSpecifiedByDirective,

@@ -163,3 +163,3 @@ import type { Maybe } from '../jsutils/Maybe';

*/
assumeValid?: boolean;
assumeValid?: boolean | undefined;
}

@@ -166,0 +166,0 @@ export interface GraphQLSchemaConfig extends GraphQLSchemaValidationOptions {

@@ -144,3 +144,3 @@ import { inspect } from '../jsutils/inspect.js';

this._typeMap = Object.create(null);
this._subTypeMap = Object.create(null);
this._subTypeMap = new Map();
// Keep track of all implementations by interface name.

@@ -228,21 +228,16 @@ this._implementationsMap = Object.create(null);

isSubType(abstractType, maybeSubType) {
let map = this._subTypeMap[abstractType.name];
if (map === undefined) {
map = Object.create(null);
let set = this._subTypeMap.get(abstractType);
if (set === undefined) {
if (isUnionType(abstractType)) {
for (const type of abstractType.getTypes()) {
map[type.name] = true;
}
set = new Set(abstractType.getTypes());
} else {
const implementations = this.getImplementations(abstractType);
for (const type of implementations.objects) {
map[type.name] = true;
}
for (const type of implementations.interfaces) {
map[type.name] = true;
}
set = new Set([
...implementations.objects,
...implementations.interfaces,
]);
}
this._subTypeMap[abstractType.name] = map;
this._subTypeMap.set(abstractType, set);
}
return map[maybeSubType.name] !== undefined;
return set.has(maybeSubType);
}

@@ -249,0 +244,0 @@ getDirectives() {

@@ -95,3 +95,3 @@ import { AccumulatorMap } from '../jsutils/AccumulatorMap.js';

}
for (const [rootType, operationTypes] of rootTypesMap.entries()) {
for (const [rootType, operationTypes] of rootTypesMap) {
if (operationTypes.length > 1) {

@@ -243,3 +243,3 @@ const operationList = andList(operationTypes);

function validateInterfaces(context, type) {
const ifaceTypeNames = Object.create(null);
const ifaceTypeNames = new Set();
for (const iface of type.getInterfaces()) {

@@ -261,3 +261,3 @@ if (!isInterfaceType(iface)) {

}
if (ifaceTypeNames[iface.name]) {
if (ifaceTypeNames.has(iface.name)) {
context.reportError(

@@ -269,3 +269,3 @@ `Type ${type.name} can only implement ${iface.name} once.`,

}
ifaceTypeNames[iface.name] = true;
ifaceTypeNames.add(iface.name);
validateTypeImplementsAncestors(context, type, iface);

@@ -362,5 +362,5 @@ validateTypeImplementsInterface(context, type, iface);

}
const includedTypeNames = Object.create(null);
const includedTypeNames = new Set();
for (const memberType of memberTypes) {
if (includedTypeNames[memberType.name]) {
if (includedTypeNames.has(memberType.name)) {
context.reportError(

@@ -372,3 +372,3 @@ `Union type ${union.name} can only include type ${memberType.name} once.`,

}
includedTypeNames[memberType.name] = true;
includedTypeNames.add(memberType.name);
if (!isObjectType(memberType)) {

@@ -428,3 +428,3 @@ context.reportError(

// are not redundantly reported.
const visitedTypes = Object.create(null);
const visitedTypes = new Set();
// Array of types nodes used to produce meaningful errors

@@ -439,6 +439,6 @@ const fieldPath = [];

function detectCycleRecursive(inputObj) {
if (visitedTypes[inputObj.name]) {
if (visitedTypes.has(inputObj)) {
return;
}
visitedTypes[inputObj.name] = true;
visitedTypes.add(inputObj);
fieldPathIndexByTypeName[inputObj.name] = fieldPath.length;

@@ -445,0 +445,0 @@ const fields = Object.values(inputObj.getFields());

@@ -12,3 +12,3 @@ import type { DocumentNode } from '../language/ast';

*/
assumeValidSDL?: boolean;
assumeValidSDL?: boolean | undefined;
}

@@ -15,0 +15,0 @@ /**

@@ -115,3 +115,3 @@ import { didYouMean } from '../jsutils/didYouMean.js';

let parseResult;
// Scalars and Enums determine if a input value is valid via parseValue(),
// Scalars and Enums determine if an input value is valid via parseValue(),
// which can throw to indicate failure. If it throws, maintain a reference

@@ -118,0 +118,0 @@ // to the original error.

@@ -13,3 +13,3 @@ import type { DocumentNode } from '../language/ast';

*/
assumeValidSDL?: boolean;
assumeValidSDL?: boolean | undefined;
}

@@ -16,0 +16,0 @@ /**

@@ -0,1 +1,2 @@

import { AccumulatorMap } from '../jsutils/AccumulatorMap.js';
import { inspect } from '../jsutils/inspect.js';

@@ -7,6 +8,2 @@ import { invariant } from '../jsutils/invariant.js';

import {
isTypeDefinitionNode,
isTypeExtensionNode,
} from '../language/predicates.js';
import {
GraphQLEnumType,

@@ -76,3 +73,8 @@ GraphQLInputObjectType,

const typeDefs = [];
const typeExtensionsMap = Object.create(null);
const scalarExtensions = new AccumulatorMap();
const objectExtensions = new AccumulatorMap();
const interfaceExtensions = new AccumulatorMap();
const unionExtensions = new AccumulatorMap();
const enumExtensions = new AccumulatorMap();
const inputObjectExtensions = new AccumulatorMap();
// New directives and types are separate because a directives and types can

@@ -84,28 +86,50 @@ // have the same name. For example, a type named "skip".

const schemaExtensions = [];
let isSchemaChanged = false;
for (const def of documentAST.definitions) {
if (def.kind === Kind.SCHEMA_DEFINITION) {
schemaDef = def;
} else if (def.kind === Kind.SCHEMA_EXTENSION) {
schemaExtensions.push(def);
} else if (isTypeDefinitionNode(def)) {
typeDefs.push(def);
} else if (isTypeExtensionNode(def)) {
const extendedTypeName = def.name.value;
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
? existingTypeExtensions.concat([def])
: [def];
} else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
directiveDefs.push(def);
switch (def.kind) {
case Kind.SCHEMA_DEFINITION:
schemaDef = def;
break;
case Kind.SCHEMA_EXTENSION:
schemaExtensions.push(def);
break;
case Kind.DIRECTIVE_DEFINITION:
directiveDefs.push(def);
break;
// Type Definitions
case Kind.SCALAR_TYPE_DEFINITION:
case Kind.OBJECT_TYPE_DEFINITION:
case Kind.INTERFACE_TYPE_DEFINITION:
case Kind.UNION_TYPE_DEFINITION:
case Kind.ENUM_TYPE_DEFINITION:
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
typeDefs.push(def);
break;
// Type System Extensions
case Kind.SCALAR_TYPE_EXTENSION:
scalarExtensions.add(def.name.value, def);
break;
case Kind.OBJECT_TYPE_EXTENSION:
objectExtensions.add(def.name.value, def);
break;
case Kind.INTERFACE_TYPE_EXTENSION:
interfaceExtensions.add(def.name.value, def);
break;
case Kind.UNION_TYPE_EXTENSION:
unionExtensions.add(def.name.value, def);
break;
case Kind.ENUM_TYPE_EXTENSION:
enumExtensions.add(def.name.value, def);
break;
case Kind.INPUT_OBJECT_TYPE_EXTENSION:
inputObjectExtensions.add(def.name.value, def);
break;
default:
continue;
}
isSchemaChanged = true;
}
// If this document contains no new types, extensions, or directives then
// return the same unmodified GraphQLSchema instance.
if (
Object.keys(typeExtensionsMap).length === 0 &&
typeDefs.length === 0 &&
directiveDefs.length === 0 &&
schemaExtensions.length === 0 &&
schemaDef == null
) {
if (!isSchemaChanged) {
return schemaConfig;

@@ -205,3 +229,3 @@ }

const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
const extensions = inputObjectExtensions.get(config.name) ?? [];
return new GraphQLInputObjectType({

@@ -221,3 +245,3 @@ ...config,

const config = type.toConfig();
const extensions = typeExtensionsMap[type.name] ?? [];
const extensions = enumExtensions.get(type.name) ?? [];
return new GraphQLEnumType({

@@ -234,3 +258,3 @@ ...config,

const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
const extensions = scalarExtensions.get(config.name) ?? [];
let specifiedByURL = config.specifiedByURL;

@@ -248,3 +272,3 @@ for (const extensionNode of extensions) {

const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
const extensions = objectExtensions.get(config.name) ?? [];
return new GraphQLObjectType({

@@ -265,3 +289,3 @@ ...config,

const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
const extensions = interfaceExtensions.get(config.name) ?? [];
return new GraphQLInterfaceType({

@@ -282,3 +306,3 @@ ...config,

const config = type.toConfig();
const extensions = typeExtensionsMap[config.name] ?? [];
const extensions = unionExtensions.get(config.name) ?? [];
return new GraphQLUnionType({

@@ -447,5 +471,5 @@ ...config,

const name = astNode.name.value;
const extensionASTNodes = typeExtensionsMap[name] ?? [];
switch (astNode.kind) {
case Kind.OBJECT_TYPE_DEFINITION: {
const extensionASTNodes = objectExtensions.get(name) ?? [];
const allNodes = [astNode, ...extensionASTNodes];

@@ -462,2 +486,3 @@ return new GraphQLObjectType({

case Kind.INTERFACE_TYPE_DEFINITION: {
const extensionASTNodes = interfaceExtensions.get(name) ?? [];
const allNodes = [astNode, ...extensionASTNodes];

@@ -474,2 +499,3 @@ return new GraphQLInterfaceType({

case Kind.ENUM_TYPE_DEFINITION: {
const extensionASTNodes = enumExtensions.get(name) ?? [];
const allNodes = [astNode, ...extensionASTNodes];

@@ -485,2 +511,3 @@ return new GraphQLEnumType({

case Kind.UNION_TYPE_DEFINITION: {
const extensionASTNodes = unionExtensions.get(name) ?? [];
const allNodes = [astNode, ...extensionASTNodes];

@@ -496,2 +523,3 @@ return new GraphQLUnionType({

case Kind.SCALAR_TYPE_DEFINITION: {
const extensionASTNodes = scalarExtensions.get(name) ?? [];
return new GraphQLScalarType({

@@ -506,2 +534,3 @@ name,

case Kind.INPUT_OBJECT_TYPE_DEFINITION: {
const extensionASTNodes = inputObjectExtensions.get(name) ?? [];
const allNodes = [astNode, ...extensionASTNodes];

@@ -508,0 +537,0 @@ return new GraphQLInputObjectType({

@@ -5,2 +5,4 @@ export { validate } from './validate';

export { specifiedRules } from './specifiedRules';
export { DeferStreamDirectiveLabelRule } from './rules/DeferStreamDirectiveLabelRule';
export { DeferStreamDirectiveOnRootFieldRule } from './rules/DeferStreamDirectiveOnRootFieldRule';
export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule';

@@ -23,2 +25,3 @@ export { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule';

export { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule';
export { StreamDirectiveOnListFieldRule } from './rules/StreamDirectiveOnListFieldRule';
export { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule';

@@ -25,0 +28,0 @@ export { UniqueDirectivesPerLocationRule } from './rules/UniqueDirectivesPerLocationRule';

@@ -5,2 +5,6 @@ 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"

@@ -40,2 +44,4 @@ 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"

@@ -42,0 +48,0 @@ export { UniqueArgumentNamesRule } from './rules/UniqueArgumentNamesRule.js';

@@ -20,21 +20,15 @@ import { didYouMean } from '../../jsutils/didYouMean.js';

export function KnownTypeNamesRule(context) {
const schema = context.getSchema();
const existingTypesMap = schema ? schema.getTypeMap() : Object.create(null);
const definedTypes = Object.create(null);
for (const def of context.getDocument().definitions) {
if (isTypeDefinitionNode(def)) {
definedTypes[def.name.value] = true;
}
}
const typeNames = [
const { definitions } = context.getDocument();
const existingTypesMap = context.getSchema()?.getTypeMap() ?? {};
const typeNames = new Set([
...Object.keys(existingTypesMap),
...Object.keys(definedTypes),
];
...definitions.filter(isTypeDefinitionNode).map((def) => def.name.value),
]);
return {
NamedType(node, _1, parent, _2, ancestors) {
const typeName = node.name.value;
if (!existingTypesMap[typeName] && !definedTypes[typeName]) {
if (!typeNames.has(typeName)) {
const definitionNode = ancestors[2] ?? parent;
const isSDL = definitionNode != null && isSDLNode(definitionNode);
if (isSDL && standardTypeNames.includes(typeName)) {
if (isSDL && standardTypeNames.has(typeName)) {
return;

@@ -44,3 +38,3 @@ }

typeName,
isSDL ? standardTypeNames.concat(typeNames) : typeNames,
isSDL ? [...standardTypeNames, ...typeNames] : [...typeNames],
);

@@ -57,4 +51,4 @@ context.reportError(

}
const standardTypeNames = [...specifiedScalarTypes, ...introspectionTypes].map(
(type) => type.name,
const standardTypeNames = new Set(
[...specifiedScalarTypes, ...introspectionTypes].map((type) => type.name),
);

@@ -61,0 +55,0 @@ function isSDLNode(value) {

@@ -13,3 +13,3 @@ import { GraphQLError } from '../../error/GraphQLError.js';

// are not redundantly reported.
const visitedFrags = Object.create(null);
const visitedFrags = new Set();
// Array of AST nodes used to produce meaningful errors

@@ -30,7 +30,7 @@ const spreadPath = [];

function detectCycleRecursive(fragment) {
if (visitedFrags[fragment.name.value]) {
if (visitedFrags.has(fragment.name.value)) {
return;
}
const fragmentName = fragment.name.value;
visitedFrags[fragmentName] = true;
visitedFrags.add(fragmentName);
const spreadNodes = context.getFragmentSpreads(fragment.selectionSet);

@@ -37,0 +37,0 @@ if (spreadNodes.length === 0) {

@@ -11,29 +11,23 @@ import { GraphQLError } from '../../error/GraphQLError.js';

export function NoUndefinedVariablesRule(context) {
let variableNameDefined = Object.create(null);
return {
OperationDefinition: {
enter() {
variableNameDefined = Object.create(null);
},
leave(operation) {
const usages = context.getRecursiveVariableUsages(operation);
for (const { node } of usages) {
const varName = node.name.value;
if (variableNameDefined[varName] !== true) {
context.reportError(
new GraphQLError(
operation.name
? `Variable "$${varName}" is not defined by operation "${operation.name.value}".`
: `Variable "$${varName}" is not defined.`,
{ nodes: [node, operation] },
),
);
}
OperationDefinition(operation) {
const variableNameDefined = new Set(
operation.variableDefinitions?.map((node) => node.variable.name.value),
);
const usages = context.getRecursiveVariableUsages(operation);
for (const { node } of usages) {
const varName = node.name.value;
if (!variableNameDefined.has(varName)) {
context.reportError(
new GraphQLError(
operation.name
? `Variable "$${varName}" is not defined by operation "${operation.name.value}".`
: `Variable "$${varName}" is not defined.`,
{ nodes: [node, operation] },
),
);
}
},
}
},
VariableDefinition(node) {
variableNameDefined[node.variable.name.value] = true;
},
};
}

@@ -11,7 +11,11 @@ import { GraphQLError } from '../../error/GraphQLError.js';

export function NoUnusedFragmentsRule(context) {
const operationDefs = [];
const fragmentNameUsed = new Set();
const fragmentDefs = [];
return {
OperationDefinition(node) {
operationDefs.push(node);
OperationDefinition(operation) {
for (const fragment of context.getRecursivelyReferencedFragments(
operation,
)) {
fragmentNameUsed.add(fragment.name.value);
}
return false;

@@ -25,13 +29,5 @@ },

leave() {
const fragmentNameUsed = Object.create(null);
for (const operation of operationDefs) {
for (const fragment of context.getRecursivelyReferencedFragments(
operation,
)) {
fragmentNameUsed[fragment.name.value] = true;
}
}
for (const fragmentDef of fragmentDefs) {
const fragName = fragmentDef.name.value;
if (fragmentNameUsed[fragName] !== true) {
if (!fragmentNameUsed.has(fragName)) {
context.reportError(

@@ -38,0 +34,0 @@ new GraphQLError(`Fragment "${fragName}" is never used.`, {

@@ -11,33 +11,26 @@ import { GraphQLError } from '../../error/GraphQLError.js';

export function NoUnusedVariablesRule(context) {
let variableDefs = [];
return {
OperationDefinition: {
enter() {
variableDefs = [];
},
leave(operation) {
const variableNameUsed = Object.create(null);
const usages = context.getRecursiveVariableUsages(operation);
for (const { node } of usages) {
variableNameUsed[node.name.value] = true;
OperationDefinition(operation) {
const usages = context.getRecursiveVariableUsages(operation);
const variableNameUsed = new Set(
usages.map(({ node }) => node.name.value),
);
// FIXME: https://github.com/graphql/graphql-js/issues/2203
/* c8 ignore next */
const variableDefinitions = operation.variableDefinitions ?? [];
for (const variableDef of variableDefinitions) {
const variableName = variableDef.variable.name.value;
if (!variableNameUsed.has(variableName)) {
context.reportError(
new GraphQLError(
operation.name
? `Variable "$${variableName}" is never used in operation "${operation.name.value}".`
: `Variable "$${variableName}" is never used.`,
{ nodes: variableDef },
),
);
}
for (const variableDef of variableDefs) {
const variableName = variableDef.variable.name.value;
if (variableNameUsed[variableName] !== true) {
context.reportError(
new GraphQLError(
operation.name
? `Variable "$${variableName}" is never used in operation "${operation.name.value}".`
: `Variable "$${variableName}" is never used.`,
{ nodes: variableDef },
),
);
}
}
},
}
},
VariableDefinition(def) {
variableDefs.push(def);
},
};
}

@@ -530,2 +530,12 @@ 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.

@@ -578,2 +588,18 @@ 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.

@@ -618,3 +644,3 @@ // Composite types are ignored as their individual field types will be compared

const nodeAndDefs = Object.create(null);
const fragmentNames = Object.create(null);
const fragmentNames = new Set();
_collectFieldsAndFragmentNames(

@@ -627,3 +653,3 @@ context,

);
const result = [nodeAndDefs, Object.keys(fragmentNames)];
const result = [nodeAndDefs, [...fragmentNames]];
cachedFieldsAndFragmentNames.set(selectionSet, result);

@@ -677,3 +703,3 @@ return result;

case Kind.FRAGMENT_SPREAD:
fragmentNames[selection.name.value] = true;
fragmentNames.add(selection.name.value);
break;

@@ -680,0 +706,0 @@ case Kind.INLINE_FRAGMENT: {

@@ -28,3 +28,3 @@ import { GraphQLError } from '../../error/GraphQLError.js';

}
const fields = collectFields(
const { fields } = collectFields(
schema,

@@ -50,4 +50,3 @@ fragments,

for (const fieldNodes of fields.values()) {
const field = fieldNodes[0];
const fieldName = field.name.value;
const fieldName = fieldNodes[0].name.value;
if (fieldName.startsWith('__')) {

@@ -54,0 +53,0 @@ context.reportError(

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

// 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"

@@ -44,2 +48,4 @@ 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';

@@ -95,2 +101,5 @@ // Spec Section: "Argument Uniqueness"

UniqueDirectivesPerLocationRule,
DeferStreamDirectiveOnRootFieldRule,
DeferStreamDirectiveLabelRule,
StreamDirectiveOnListFieldRule,
KnownArgumentNamesRule,

@@ -97,0 +106,0 @@ UniqueArgumentNamesRule,

@@ -41,3 +41,5 @@ import { GraphQLError } from '../error/GraphQLError.js';

assertValidSchema(schema);
const abortObj = Object.freeze({});
const abortError = new GraphQLError(
'Too many validation errors, error limit reached. Validation aborted.',
);
const errors = [];

@@ -50,9 +52,3 @@ const context = new ValidationContext(

if (errors.length >= maxErrors) {
errors.push(
new GraphQLError(
'Too many validation errors, error limit reached. Validation aborted.',
),
);
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw abortObj;
throw abortError;
}

@@ -69,3 +65,5 @@ errors.push(error);

} catch (e) {
if (e !== abortObj) {
if (e === abortError) {
errors.push(abortError);
} else {
throw e;

@@ -72,0 +70,0 @@ }

@@ -64,3 +64,3 @@ import { Kind } from '../language/kinds.js';

fragments = [];
const collectedNames = Object.create(null);
const collectedNames = new Set();
const nodesToVisit = [operation.selectionSet];

@@ -71,4 +71,4 @@ let node;

const fragName = spread.name.value;
if (collectedNames[fragName] !== true) {
collectedNames[fragName] = true;
if (!collectedNames.has(fragName)) {
collectedNames.add(fragName);
const fragment = this.getFragment(fragName);

@@ -75,0 +75,0 @@ if (fragment) {

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