Comparing version 17.0.0-alpha.1.canary.pr.3601.9a812ce71ad05c9dc089b40129f8295ca733e108 to 17.0.0-alpha.1.canary.pr.3651.57364d3f9da445b2bba520d3b886e07dc2af10e2
@@ -72,2 +72,3 @@ import { AccumulatorMap } from '../jsutils/AccumulatorMap.js'; | ||
} | ||
// eslint-disable-next-line max-params | ||
function collectFieldsImpl( | ||
@@ -74,0 +75,0 @@ schema, |
@@ -116,15 +116,2 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
/** | ||
* Essential assertions before executing to provide developer feedback for | ||
* improper use of the GraphQL library. | ||
* | ||
* @internal | ||
*/ | ||
export declare function assertValidExecutionArguments( | ||
schema: GraphQLSchema, | ||
document: DocumentNode, | ||
rawVariableValues: Maybe<{ | ||
readonly [variable: string]: unknown; | ||
}>, | ||
): void; | ||
/** | ||
* Constructs a ExecutionContext object from the arguments passed to | ||
@@ -135,2 +122,3 @@ * execute, which we will pass throughout the other execution methods. | ||
* | ||
* TODO: consider no longer exporting this function | ||
* @internal | ||
@@ -142,2 +130,3 @@ */ | ||
/** | ||
* TODO: consider no longer exporting this function | ||
* @internal | ||
@@ -174,16 +163,57 @@ */ | ||
/** | ||
* This method looks up the field on the given type definition. | ||
* It has special casing for the three introspection fields, | ||
* __schema, __type and __typename. __typename is special because | ||
* it can always be queried as a field, even in situations where no | ||
* other fields are allowed, like on a Union. __schema and __type | ||
* could get automatically added to the query type, but that would | ||
* require mutating type definitions, which would cause issues. | ||
* Implements the "Subscribe" algorithm described in the GraphQL specification. | ||
* | ||
* @internal | ||
* 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 ExecutionResults representing the response stream. | ||
* | ||
* Accepts either an object with named arguments, or individual arguments. | ||
*/ | ||
export declare function getFieldDef( | ||
schema: GraphQLSchema, | ||
parentType: GraphQLObjectType, | ||
fieldNode: FieldNode, | ||
): Maybe<GraphQLField<unknown, unknown>>; | ||
export declare function subscribe( | ||
args: ExecutionArgs, | ||
): PromiseOrValue< | ||
AsyncGenerator<ExecutionResult, void, void> | ExecutionResult | ||
>; | ||
/** | ||
* Implements the "CreateSourceEventStream" algorithm described in the | ||
* GraphQL specification, resolving the subscription source event stream. | ||
* | ||
* Returns a Promise which resolves to either an AsyncIterable (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 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 the AsyncIterable for the | ||
* event stream returned by the resolver. | ||
* | ||
* A Source Event Stream represents a sequence of events, each of which triggers | ||
* a GraphQL execution for that event. | ||
* | ||
* This may be useful when hosting the stateful subscription service in a | ||
* different process or machine than the stateless GraphQL execution engine, | ||
* or otherwise separating these two steps. For more on this, see the | ||
* "Supporting Subscriptions at Scale" information in the GraphQL specification. | ||
*/ | ||
export declare function createSourceEventStream( | ||
args: ExecutionArgs, | ||
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult>; |
@@ -1,4 +0,4 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { inspect } from '../jsutils/inspect.js'; | ||
import { invariant } from '../jsutils/invariant.js'; | ||
import { isAsyncIterable } from '../jsutils/isAsyncIterable.js'; | ||
import { isIterableObject } from '../jsutils/isIterableObject.js'; | ||
@@ -22,7 +22,2 @@ import { isObjectLike } from '../jsutils/isObjectLike.js'; | ||
} from '../type/definition.js'; | ||
import { | ||
SchemaMetaFieldDef, | ||
TypeMetaFieldDef, | ||
TypeNameMetaFieldDef, | ||
} from '../type/introspection.js'; | ||
import { assertValidSchema } from '../type/validate.js'; | ||
@@ -33,3 +28,7 @@ import { | ||
} from './collectFields.js'; | ||
import { mapAsyncIterator } from './mapAsyncIterator.js'; | ||
import { getArgumentValues, getVariableValues } from './values.js'; | ||
/* eslint-disable max-params */ | ||
// This file contains a lot of such errors but we plan to refactor it anyway | ||
// so just disable it for entire file. | ||
/** | ||
@@ -60,5 +59,2 @@ * A memoized collection of relevant subfields with regard to the return | ||
export function execute(args) { | ||
const { schema, document, variableValues, rootValue } = args; | ||
// If arguments are missing or incorrect, throw an error. | ||
assertValidExecutionArguments(schema, document, variableValues); | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
@@ -71,2 +67,5 @@ // a "Response" with only errors is returned. | ||
} | ||
return executeImpl(exeContext); | ||
} | ||
function executeImpl(exeContext) { | ||
// Return a Promise that will eventually resolve to the data described by | ||
@@ -84,4 +83,3 @@ // The "Response" section of the GraphQL specification. | ||
try { | ||
const { operation } = exeContext; | ||
const result = executeOperation(exeContext, operation, rootValue); | ||
const result = executeOperation(exeContext); | ||
if (isPromise(result)) { | ||
@@ -123,24 +121,2 @@ return result.then( | ||
/** | ||
* Essential assertions before executing to provide developer feedback for | ||
* improper use of the GraphQL library. | ||
* | ||
* @internal | ||
*/ | ||
export function assertValidExecutionArguments( | ||
schema, | ||
document, | ||
rawVariableValues, | ||
) { | ||
document != null || devAssert(false, 'Must provide document.'); | ||
// If the schema used for execution is invalid, throw an error. | ||
assertValidSchema(schema); | ||
// Variables, if provided, must be an object. | ||
rawVariableValues == null || | ||
isObjectLike(rawVariableValues) || | ||
devAssert( | ||
false, | ||
'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.', | ||
); | ||
} | ||
/** | ||
* Constructs a ExecutionContext object from the arguments passed to | ||
@@ -151,2 +127,3 @@ * execute, which we will pass throughout the other execution methods. | ||
* | ||
* TODO: consider no longer exporting this function | ||
* @internal | ||
@@ -166,2 +143,4 @@ */ | ||
} = args; | ||
// If the schema used for execution is invalid, throw an error. | ||
assertValidSchema(schema); | ||
let operation; | ||
@@ -223,7 +202,16 @@ const fragments = Object.create(null); | ||
} | ||
function buildPerEventExecutionContext(exeContext, payload) { | ||
return { | ||
...exeContext, | ||
rootValue: payload, | ||
errors: [], | ||
}; | ||
} | ||
/** | ||
* Implements the "Executing operations" section of the spec. | ||
*/ | ||
function executeOperation(exeContext, operation, rootValue) { | ||
const rootType = exeContext.schema.getRootType(operation.operation); | ||
function executeOperation(exeContext) { | ||
const { operation, schema, fragments, variableValues, rootValue } = | ||
exeContext; | ||
const rootType = schema.getRootType(operation.operation); | ||
if (rootType == null) { | ||
@@ -236,5 +224,5 @@ throw new GraphQLError( | ||
const rootFields = collectFields( | ||
exeContext.schema, | ||
exeContext.fragments, | ||
exeContext.variableValues, | ||
schema, | ||
fragments, | ||
variableValues, | ||
rootType, | ||
@@ -305,17 +293,27 @@ operation.selectionSet, | ||
let containsPromise = false; | ||
for (const [responseName, fieldNodes] of fields.entries()) { | ||
const fieldPath = addPath(path, responseName, parentType.name); | ||
const result = executeField( | ||
exeContext, | ||
parentType, | ||
sourceValue, | ||
fieldNodes, | ||
fieldPath, | ||
); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if (isPromise(result)) { | ||
containsPromise = true; | ||
try { | ||
for (const [responseName, fieldNodes] of fields.entries()) { | ||
const fieldPath = addPath(path, responseName, parentType.name); | ||
const result = executeField( | ||
exeContext, | ||
parentType, | ||
sourceValue, | ||
fieldNodes, | ||
fieldPath, | ||
); | ||
if (result !== undefined) { | ||
results[responseName] = result; | ||
if (isPromise(result)) { | ||
containsPromise = true; | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
if (containsPromise) { | ||
// Ensure that any promises returned by other fields are handled, as they may also reject. | ||
return promiseForObject(results).finally(() => { | ||
throw error; | ||
}); | ||
} | ||
throw error; | ||
} | ||
@@ -338,3 +336,4 @@ // If there are no promises, we can just return the object | ||
function executeField(exeContext, parentType, source, fieldNodes, path) { | ||
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); | ||
const fieldName = fieldNodes[0].name.value; | ||
const fieldDef = exeContext.schema.getField(parentType, fieldName); | ||
if (!fieldDef) { | ||
@@ -397,2 +396,3 @@ return; | ||
/** | ||
* TODO: consider no longer exporting this function | ||
* @internal | ||
@@ -529,2 +529,59 @@ */ | ||
/** | ||
* 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, | ||
) { | ||
let containsPromise = false; | ||
const completedResults = []; | ||
let index = 0; | ||
// eslint-disable-next-line no-constant-condition | ||
while (true) { | ||
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, | ||
); | ||
if (isPromise(completedItem)) { | ||
containsPromise = true; | ||
} | ||
completedResults.push(completedItem); | ||
} catch (rawError) { | ||
completedResults.push(null); | ||
const error = locatedError( | ||
rawError, | ||
fieldNodes, | ||
pathToArray(fieldPath), | ||
); | ||
handleFieldError(error, itemType, exeContext); | ||
} | ||
} catch (rawError) { | ||
completedResults.push(null); | ||
const error = locatedError(rawError, fieldNodes, pathToArray(fieldPath)); | ||
handleFieldError(error, itemType, exeContext); | ||
break; | ||
} | ||
index += 1; | ||
} | ||
return containsPromise ? Promise.all(completedResults) : completedResults; | ||
} | ||
/** | ||
* Complete a list value by completing each item in the list with the | ||
@@ -541,2 +598,14 @@ * inner type | ||
) { | ||
const itemType = returnType.ofType; | ||
if (isAsyncIterable(result)) { | ||
const iterator = result[Symbol.asyncIterator](); | ||
return completeAsyncIteratorValue( | ||
exeContext, | ||
itemType, | ||
fieldNodes, | ||
info, | ||
path, | ||
iterator, | ||
); | ||
} | ||
if (!isIterableObject(result)) { | ||
@@ -549,3 +618,2 @@ throw new GraphQLError( | ||
// where the list contains no Promises by avoiding creating another Promise. | ||
const itemType = returnType.ofType; | ||
let containsPromise = false; | ||
@@ -822,28 +890,171 @@ const completedResults = Array.from(result, (item, index) => { | ||
/** | ||
* This method looks up the field on the given type definition. | ||
* It has special casing for the three introspection fields, | ||
* __schema, __type and __typename. __typename is special because | ||
* it can always be queried as a field, even in situations where no | ||
* other fields are allowed, like on a Union. __schema and __type | ||
* could get automatically added to the query type, but that would | ||
* require mutating type definitions, which would cause issues. | ||
* Implements the "Subscribe" algorithm described in the GraphQL specification. | ||
* | ||
* @internal | ||
* 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 ExecutionResults representing the response stream. | ||
* | ||
* Accepts either an object with named arguments, or individual arguments. | ||
*/ | ||
export function getFieldDef(schema, parentType, fieldNode) { | ||
const fieldName = fieldNode.name.value; | ||
if ( | ||
fieldName === SchemaMetaFieldDef.name && | ||
schema.getQueryType() === parentType | ||
) { | ||
return SchemaMetaFieldDef; | ||
} else if ( | ||
fieldName === TypeMetaFieldDef.name && | ||
schema.getQueryType() === parentType | ||
) { | ||
return TypeMetaFieldDef; | ||
} else if (fieldName === TypeNameMetaFieldDef.name) { | ||
return TypeNameMetaFieldDef; | ||
export function subscribe(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
// a "Response" with only errors is returned. | ||
const exeContext = buildExecutionContext(args); | ||
// Return early errors if execution context failed. | ||
if (!('schema' in exeContext)) { | ||
return { errors: exeContext }; | ||
} | ||
return parentType.getFields()[fieldName]; | ||
const resultOrStream = createSourceEventStreamImpl(exeContext); | ||
if (isPromise(resultOrStream)) { | ||
return resultOrStream.then((resolvedResultOrStream) => | ||
mapSourceToResponse(exeContext, resolvedResultOrStream), | ||
); | ||
} | ||
return mapSourceToResponse(exeContext, resultOrStream); | ||
} | ||
function mapSourceToResponse(exeContext, resultOrStream) { | ||
if (!isAsyncIterable(resultOrStream)) { | ||
return resultOrStream; | ||
} | ||
// For each payload yielded from a subscription, map it over the normal | ||
// GraphQL `execute` function, with `payload` as the rootValue. | ||
// This implements the "MapSourceToResponseEvent" algorithm described in | ||
// the GraphQL specification. The `execute` function provides the | ||
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the | ||
// "ExecuteQuery" algorithm, for which `execute` is also used. | ||
return mapAsyncIterator(resultOrStream, (payload) => | ||
executeImpl(buildPerEventExecutionContext(exeContext, payload)), | ||
); | ||
} | ||
/** | ||
* Implements the "CreateSourceEventStream" algorithm described in the | ||
* GraphQL specification, resolving the subscription source event stream. | ||
* | ||
* Returns a Promise which resolves to either an AsyncIterable (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 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 the AsyncIterable for the | ||
* event stream returned by the resolver. | ||
* | ||
* A Source Event Stream represents a sequence of events, each of which triggers | ||
* a GraphQL execution for that event. | ||
* | ||
* This may be useful when hosting the stateful subscription service in a | ||
* different process or machine than the stateless GraphQL execution engine, | ||
* or otherwise separating these two steps. For more on this, see the | ||
* "Supporting Subscriptions at Scale" information in the GraphQL specification. | ||
*/ | ||
export function createSourceEventStream(args) { | ||
// If a valid execution context cannot be created due to incorrect arguments, | ||
// a "Response" with only errors is returned. | ||
const exeContext = buildExecutionContext(args); | ||
// Return early errors if execution context failed. | ||
if (!('schema' in exeContext)) { | ||
return { errors: exeContext }; | ||
} | ||
return createSourceEventStreamImpl(exeContext); | ||
} | ||
function createSourceEventStreamImpl(exeContext) { | ||
try { | ||
const eventStream = executeSubscription(exeContext); | ||
if (isPromise(eventStream)) { | ||
return eventStream.then(undefined, (error) => ({ errors: [error] })); | ||
} | ||
return eventStream; | ||
} catch (error) { | ||
return { errors: [error] }; | ||
} | ||
} | ||
function executeSubscription(exeContext) { | ||
const { schema, fragments, operation, variableValues, rootValue } = | ||
exeContext; | ||
const rootType = schema.getSubscriptionType(); | ||
if (rootType == null) { | ||
throw new GraphQLError( | ||
'Schema is not configured to execute subscription operation.', | ||
{ nodes: operation }, | ||
); | ||
} | ||
const rootFields = collectFields( | ||
schema, | ||
fragments, | ||
variableValues, | ||
rootType, | ||
operation.selectionSet, | ||
); | ||
const [responseName, fieldNodes] = [...rootFields.entries()][0]; | ||
const fieldName = fieldNodes[0].name.value; | ||
const fieldDef = schema.getField(rootType, fieldName); | ||
if (!fieldDef) { | ||
throw new GraphQLError( | ||
`The subscription field "${fieldName}" is not defined.`, | ||
{ nodes: fieldNodes }, | ||
); | ||
} | ||
const path = addPath(undefined, responseName, rootType.name); | ||
const info = buildResolveInfo( | ||
exeContext, | ||
fieldDef, | ||
fieldNodes, | ||
rootType, | ||
path, | ||
); | ||
try { | ||
// Implements the "ResolveFieldEventStream" algorithm from GraphQL specification. | ||
// It differs from "ResolveFieldValue" due to providing a different `resolveFn`. | ||
// Build a JS object of arguments from the field.arguments AST, using the | ||
// variables scope to fulfill any variable references. | ||
const args = getArgumentValues(fieldDef, fieldNodes[0], variableValues); | ||
// The resolve function's optional third argument is a context value that | ||
// is provided to every resolve function within an execution. It is commonly | ||
// used to represent an authenticated user, or request-specific caches. | ||
const contextValue = exeContext.contextValue; | ||
// Call the `subscribe()` resolver or the default resolver to produce an | ||
// AsyncIterable yielding raw payloads. | ||
const resolveFn = fieldDef.subscribe ?? exeContext.subscribeFieldResolver; | ||
const result = resolveFn(rootValue, args, contextValue, info); | ||
if (isPromise(result)) { | ||
return result.then(assertEventStream).then(undefined, (error) => { | ||
throw locatedError(error, fieldNodes, pathToArray(path)); | ||
}); | ||
} | ||
return assertEventStream(result); | ||
} catch (error) { | ||
throw locatedError(error, fieldNodes, pathToArray(path)); | ||
} | ||
} | ||
function assertEventStream(result) { | ||
if (result instanceof Error) { | ||
throw result; | ||
} | ||
// Assert field returned an event stream, otherwise yield an error. | ||
if (!isAsyncIterable(result)) { | ||
throw new GraphQLError( | ||
'Subscription field must return Async Iterable. ' + | ||
`Received: ${inspect(result)}.`, | ||
); | ||
} | ||
return result; | ||
} |
export { pathToArray as responsePathAsArray } from '../jsutils/Path'; | ||
export { | ||
createSourceEventStream, | ||
execute, | ||
@@ -7,2 +8,3 @@ executeSync, | ||
defaultTypeResolver, | ||
subscribe, | ||
} from './execute'; | ||
@@ -14,3 +16,2 @@ export type { | ||
} from './execute'; | ||
export { subscribe, createSourceEventStream } from './subscribe'; | ||
export { | ||
@@ -17,0 +18,0 @@ getArgumentValues, |
export { pathToArray as responsePathAsArray } from '../jsutils/Path.js'; | ||
export { | ||
createSourceEventStream, | ||
execute, | ||
@@ -7,4 +8,4 @@ executeSync, | ||
defaultTypeResolver, | ||
subscribe, | ||
} from './execute.js'; | ||
export { subscribe, createSourceEventStream } from './subscribe.js'; | ||
export { | ||
@@ -11,0 +12,0 @@ getArgumentValues, |
@@ -204,2 +204,3 @@ /** | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -232,2 +233,6 @@ isConstValueNode, | ||
ArgumentNode, | ||
NullabilityAssertionNode, | ||
NonNullAssertionNode, | ||
ErrorBoundaryNode, | ||
ListNullabilityOperatorNode, | ||
ConstArgumentNode, | ||
@@ -234,0 +239,0 @@ FragmentSpreadNode, |
@@ -166,2 +166,3 @@ /** | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -168,0 +169,0 @@ isConstValueNode, |
@@ -137,3 +137,6 @@ import type { Kind } from './kinds'; | ||
| EnumTypeExtensionNode | ||
| InputObjectTypeExtensionNode; | ||
| InputObjectTypeExtensionNode | ||
| NonNullAssertionNode | ||
| ErrorBoundaryNode | ||
| ListNullabilityOperatorNode; | ||
/** | ||
@@ -216,5 +219,25 @@ * Utility type listing all nodes indexed by their kind. | ||
readonly arguments?: ReadonlyArray<ArgumentNode>; | ||
readonly nullabilityAssertion?: NullabilityAssertionNode; | ||
readonly directives?: ReadonlyArray<DirectiveNode>; | ||
readonly selectionSet?: SelectionSetNode; | ||
} | ||
export declare type NullabilityAssertionNode = | ||
| NonNullAssertionNode | ||
| ErrorBoundaryNode | ||
| ListNullabilityOperatorNode; | ||
export interface ListNullabilityOperatorNode { | ||
readonly kind: Kind.LIST_NULLABILITY_OPERATOR; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: NullabilityAssertionNode; | ||
} | ||
export interface NonNullAssertionNode { | ||
readonly kind: Kind.NON_NULL_ASSERTION; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: ListNullabilityOperatorNode; | ||
} | ||
export interface ErrorBoundaryNode { | ||
readonly kind: Kind.ERROR_BOUNDARY; | ||
readonly loc?: Location; | ||
readonly nullabilityAssertion?: ListNullabilityOperatorNode; | ||
} | ||
export interface ArgumentNode { | ||
@@ -221,0 +244,0 @@ readonly kind: Kind.ARGUMENT; |
@@ -25,2 +25,3 @@ /** | ||
export class Token { | ||
// eslint-disable-next-line max-params | ||
constructor(kind, start, end, line, column, value) { | ||
@@ -64,4 +65,18 @@ this.kind = kind; | ||
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 +83,0 @@ InlineFragment: ['typeCondition', 'directives', 'selectionSet'], |
@@ -27,2 +27,6 @@ export { Source } from './source'; | ||
FieldNode, | ||
NullabilityAssertionNode, | ||
NonNullAssertionNode, | ||
ErrorBoundaryNode, | ||
ListNullabilityOperatorNode, | ||
ArgumentNode, | ||
@@ -81,2 +85,3 @@ ConstArgumentNode, | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -83,0 +88,0 @@ isConstValueNode, |
@@ -20,2 +20,3 @@ export { Source } from './source.js'; | ||
isSelectionNode, | ||
isNullabilityAssertionNode, | ||
isValueNode, | ||
@@ -22,0 +23,0 @@ isConstValueNode, |
@@ -14,2 +14,6 @@ /** | ||
ARGUMENT = 'Argument', | ||
/** Nullability Modifiers */ | ||
LIST_NULLABILITY_OPERATOR = 'ListNullabilityOperator', | ||
NON_NULL_ASSERTION = 'NonNullAssertion', | ||
ERROR_BOUNDARY = 'ErrorBoundary', | ||
/** Fragments */ | ||
@@ -16,0 +20,0 @@ FRAGMENT_SPREAD = 'FragmentSpread', |
@@ -15,2 +15,6 @@ /** | ||
Kind['ARGUMENT'] = 'Argument'; | ||
/** Nullability Modifiers */ | ||
Kind['LIST_NULLABILITY_OPERATOR'] = 'ListNullabilityOperator'; | ||
Kind['NON_NULL_ASSERTION'] = 'NonNullAssertion'; | ||
Kind['ERROR_BOUNDARY'] = 'ErrorBoundary'; | ||
/** Fragments */ | ||
@@ -17,0 +21,0 @@ Kind['FRAGMENT_SPREAD'] = 'FragmentSpread'; |
@@ -64,2 +64,3 @@ import { syntaxError } from '../error/syntaxError.js'; | ||
kind === TokenKind.BANG || | ||
kind === TokenKind.QUESTION_MARK || | ||
kind === TokenKind.DOLLAR || | ||
@@ -238,2 +239,9 @@ kind === TokenKind.AMP || | ||
return createToken(lexer, TokenKind.BRACE_R, position, position + 1); | ||
case 0x003f: // ? | ||
return createToken( | ||
lexer, | ||
TokenKind.QUESTION_MARK, | ||
position, | ||
position + 1, | ||
); | ||
// StringValue | ||
@@ -240,0 +248,0 @@ case 0x0022: // " |
@@ -31,2 +31,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
NameNode, | ||
NullabilityAssertionNode, | ||
ObjectFieldNode, | ||
@@ -84,2 +85,24 @@ ObjectTypeDefinitionNode, | ||
allowLegacyFragmentVariables?: boolean; | ||
/** | ||
* EXPERIMENTAL: | ||
* | ||
* If enabled, the parser will understand and parse Client Controlled Nullability | ||
* Designators contained in Fields. They'll be represented in the | ||
* `nullabilityAssertion` field of the FieldNode. | ||
* | ||
* The syntax looks like the following: | ||
* | ||
* ```graphql | ||
* { | ||
* nullableField! | ||
* nonNullableField? | ||
* nonNullableSelectionSet? { | ||
* childField! | ||
* } | ||
* } | ||
* ``` | ||
* Note: this feature is experimental and may change or be removed in the | ||
* future. | ||
*/ | ||
experimentalClientControlledNullability?: boolean; | ||
} | ||
@@ -218,2 +241,3 @@ /** | ||
parseField(): FieldNode; | ||
parseNullabilityAssertion(): NullabilityAssertionNode | undefined; | ||
/** | ||
@@ -220,0 +244,0 @@ * Arguments[Const] : ( Argument[?Const]+ ) |
@@ -302,2 +302,5 @@ import { syntaxError } from '../error/syntaxError.js'; | ||
arguments: this.parseArguments(false), | ||
// Experimental support for Client Controlled Nullability changes | ||
// the grammar of Field: | ||
nullabilityAssertion: this.parseNullabilityAssertion(), | ||
directives: this.parseDirectives(false), | ||
@@ -309,2 +312,32 @@ selectionSet: this.peek(TokenKind.BRACE_L) | ||
} | ||
// TODO: add grammar comment after it finalizes | ||
parseNullabilityAssertion() { | ||
// Note: Client Controlled Nullability is experimental and may be changed or | ||
// removed in the future. | ||
if (this._options?.experimentalClientControlledNullability !== true) { | ||
return undefined; | ||
} | ||
const start = this._lexer.token; | ||
let nullabilityAssertion; | ||
if (this.expectOptionalToken(TokenKind.BRACKET_L)) { | ||
const innerModifier = this.parseNullabilityAssertion(); | ||
this.expectToken(TokenKind.BRACKET_R); | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.LIST_NULLABILITY_OPERATOR, | ||
nullabilityAssertion: innerModifier, | ||
}); | ||
} | ||
if (this.expectOptionalToken(TokenKind.BANG)) { | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.NON_NULL_ASSERTION, | ||
nullabilityAssertion, | ||
}); | ||
} else if (this.expectOptionalToken(TokenKind.QUESTION_MARK)) { | ||
nullabilityAssertion = this.node(start, { | ||
kind: Kind.ERROR_BOUNDARY, | ||
nullabilityAssertion, | ||
}); | ||
} | ||
return nullabilityAssertion; | ||
} | ||
parseArguments(isConst) { | ||
@@ -311,0 +344,0 @@ const item = isConst ? this.parseConstArgument : this.parseArgument; |
@@ -6,2 +6,3 @@ import type { | ||
ExecutableDefinitionNode, | ||
NullabilityAssertionNode, | ||
SelectionNode, | ||
@@ -20,2 +21,5 @@ TypeDefinitionNode, | ||
export declare function isSelectionNode(node: ASTNode): node is SelectionNode; | ||
export declare function isNullabilityAssertionNode( | ||
node: ASTNode, | ||
): node is NullabilityAssertionNode; | ||
export declare function isValueNode(node: ASTNode): node is ValueNode; | ||
@@ -22,0 +26,0 @@ export declare function isConstValueNode(node: ASTNode): node is ConstValueNode; |
@@ -22,2 +22,9 @@ import { Kind } from './kinds.js'; | ||
} | ||
export function isNullabilityAssertionNode(node) { | ||
return ( | ||
node.kind === Kind.LIST_NULLABILITY_OPERATOR || | ||
node.kind === Kind.NON_NULL_ASSERTION || | ||
node.kind === Kind.ERROR_BOUNDARY | ||
); | ||
} | ||
export function isValueNode(node) { | ||
@@ -24,0 +31,0 @@ return ( |
@@ -45,4 +45,11 @@ import { printBlockString } from './blockString.js'; | ||
Field: { | ||
leave({ alias, name, arguments: args, directives, selectionSet }) { | ||
const prefix = wrap('', alias, ': ') + name; | ||
leave({ | ||
alias, | ||
name, | ||
arguments: args, | ||
nullabilityAssertion, | ||
directives, | ||
selectionSet, | ||
}) { | ||
const prefix = join([wrap('', alias, ': '), name], ''); | ||
let argsLine = prefix + wrap('(', join(args, ', '), ')'); | ||
@@ -52,6 +59,29 @@ if (argsLine.length > MAX_LINE_LENGTH) { | ||
} | ||
return join([argsLine, join(directives, ' '), selectionSet], ' '); | ||
return join([ | ||
argsLine, | ||
// Note: Client Controlled Nullability is experimental and may be | ||
// changed or removed in the future. | ||
nullabilityAssertion, | ||
wrap(' ', join(directives, ' ')), | ||
wrap(' ', selectionSet), | ||
]); | ||
}, | ||
}, | ||
Argument: { leave: ({ name, value }) => name + ': ' + value }, | ||
// Nullability Modifiers | ||
ListNullabilityOperator: { | ||
leave({ nullabilityAssertion }) { | ||
return join(['[', nullabilityAssertion, ']']); | ||
}, | ||
}, | ||
NonNullAssertion: { | ||
leave({ nullabilityAssertion }) { | ||
return join([nullabilityAssertion, '!']); | ||
}, | ||
}, | ||
ErrorBoundary: { | ||
leave({ nullabilityAssertion }) { | ||
return join([nullabilityAssertion, '?']); | ||
}, | ||
}, | ||
// Fragments | ||
@@ -58,0 +88,0 @@ FragmentSpread: { |
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { inspect } from '../jsutils/inspect.js'; | ||
import { instanceOf } from '../jsutils/instanceOf.js'; | ||
@@ -17,4 +16,2 @@ /** | ||
) { | ||
typeof body === 'string' || | ||
devAssert(false, `Body must be a string. Received: ${inspect(body)}.`); | ||
this.body = body; | ||
@@ -21,0 +18,0 @@ this.name = name; |
@@ -9,2 +9,3 @@ /** | ||
BANG = '!', | ||
QUESTION_MARK = '?', | ||
DOLLAR = '$', | ||
@@ -11,0 +12,0 @@ AMP = '&', |
@@ -10,2 +10,3 @@ /** | ||
TokenKind['BANG'] = '!'; | ||
TokenKind['QUESTION_MARK'] = '?'; | ||
TokenKind['DOLLAR'] = '$'; | ||
@@ -12,0 +13,0 @@ TokenKind['AMP'] = '&'; |
{ | ||
"name": "graphql", | ||
"version": "17.0.0-alpha.1.canary.pr.3601.9a812ce71ad05c9dc089b40129f8295ca733e108", | ||
"version": "17.0.0-alpha.1.canary.pr.3651.57364d3f9da445b2bba520d3b886e07dc2af10e2", | ||
"description": "A Query Language and Runtime which can target any service.", | ||
@@ -35,3 +35,3 @@ "license": "MIT", | ||
"publishConfig": { | ||
"tag": "canary-pr-3601" | ||
"tag": "canary-pr-3651" | ||
}, | ||
@@ -50,3 +50,3 @@ "type": "module", | ||
}, | ||
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/3601, no gurantees provided so please use your own discretion." | ||
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/3651, no gurantees provided so please use your own discretion." | ||
} |
@@ -1,2 +0,1 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { GraphQLError } from '../error/GraphQLError.js'; | ||
@@ -8,4 +7,2 @@ import { isNameContinue, isNameStart } from '../language/characterClasses.js'; | ||
export function assertName(name) { | ||
name != null || devAssert(false, 'Must provide name.'); | ||
typeof name === 'string' || devAssert(false, 'Expected name to be a string.'); | ||
if (name.length === 0) { | ||
@@ -12,0 +9,0 @@ throw new GraphQLError('Expected name to be a non-empty string.'); |
@@ -30,6 +30,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
*/ | ||
export declare type GraphQLType = | ||
| GraphQLNamedType | ||
| GraphQLList<GraphQLType> | ||
| GraphQLNonNull<GraphQLNullableType>; | ||
export declare type GraphQLType = GraphQLNamedType | GraphQLWrappingType; | ||
export declare function isType(type: unknown): type is GraphQLType; | ||
@@ -81,3 +78,3 @@ export declare function assertType(type: unknown): GraphQLType; | ||
type: unknown, | ||
): GraphQLNonNull<GraphQLType>; | ||
): GraphQLNonNull<GraphQLNullableType>; | ||
/** | ||
@@ -193,3 +190,3 @@ * These types may be used as input types for arguments and directives. | ||
| GraphQLList<GraphQLType> | ||
| GraphQLNonNull<GraphQLType>; | ||
| GraphQLNonNull<GraphQLNullableType>; | ||
export declare function isWrappingType( | ||
@@ -196,0 +193,0 @@ type: unknown, |
@@ -6,3 +6,2 @@ import { devAssert } from '../jsutils/devAssert.js'; | ||
import { instanceOf } from '../jsutils/instanceOf.js'; | ||
import { isObjectLike } from '../jsutils/isObjectLike.js'; | ||
import { keyMap } from '../jsutils/keyMap.js'; | ||
@@ -195,4 +194,2 @@ import { keyValMap } from '../jsutils/keyValMap.js'; | ||
constructor(ofType) { | ||
isType(ofType) || | ||
devAssert(false, `Expected ${inspect(ofType)} to be a GraphQL type.`); | ||
this.ofType = ofType; | ||
@@ -233,7 +230,2 @@ } | ||
constructor(ofType) { | ||
isNullableType(ofType) || | ||
devAssert( | ||
false, | ||
`Expected ${inspect(ofType)} to be a GraphQL nullable type.`, | ||
); | ||
this.ofType = ofType; | ||
@@ -350,15 +342,2 @@ } | ||
this.extensionASTNodes = config.extensionASTNodes ?? []; | ||
config.specifiedByURL == null || | ||
typeof config.specifiedByURL === 'string' || | ||
devAssert( | ||
false, | ||
`${this.name} must provide "specifiedByURL" as a string, ` + | ||
`but got: ${inspect(config.specifiedByURL)}.`, | ||
); | ||
config.serialize == null || | ||
typeof config.serialize === 'function' || | ||
devAssert( | ||
false, | ||
`${this.name} must provide "serialize" function. If this custom Scalar is also used as an input type, ensure "parseValue" and "parseLiteral" functions are also provided.`, | ||
); | ||
if (config.parseLiteral) { | ||
@@ -446,9 +425,2 @@ (typeof config.parseValue === 'function' && | ||
this._interfaces = () => defineInterfaces(config); | ||
config.isTypeOf == null || | ||
typeof config.isTypeOf === 'function' || | ||
devAssert( | ||
false, | ||
`${this.name} must provide "isTypeOf" as a function, ` + | ||
`but got: ${inspect(config.isTypeOf)}.`, | ||
); | ||
} | ||
@@ -490,36 +462,8 @@ get [Symbol.toStringTag]() { | ||
function defineInterfaces(config) { | ||
const interfaces = resolveReadonlyArrayThunk(config.interfaces ?? []); | ||
Array.isArray(interfaces) || | ||
devAssert( | ||
false, | ||
`${config.name} interfaces must be an Array or a function which returns an Array.`, | ||
); | ||
return interfaces; | ||
return resolveReadonlyArrayThunk(config.interfaces ?? []); | ||
} | ||
function defineFieldMap(config) { | ||
const fieldMap = resolveObjMapThunk(config.fields); | ||
isPlainObj(fieldMap) || | ||
devAssert( | ||
false, | ||
`${config.name} fields must be an object with field names as keys or a function which returns such an object.`, | ||
); | ||
return mapValue(fieldMap, (fieldConfig, fieldName) => { | ||
isPlainObj(fieldConfig) || | ||
devAssert( | ||
false, | ||
`${config.name}.${fieldName} field config must be an object.`, | ||
); | ||
fieldConfig.resolve == null || | ||
typeof fieldConfig.resolve === 'function' || | ||
devAssert( | ||
false, | ||
`${config.name}.${fieldName} field resolver must be a function if ` + | ||
`provided, but got: ${inspect(fieldConfig.resolve)}.`, | ||
); | ||
const argsConfig = fieldConfig.args ?? {}; | ||
isPlainObj(argsConfig) || | ||
devAssert( | ||
false, | ||
`${config.name}.${fieldName} args must be an object with argument names as keys.`, | ||
); | ||
return { | ||
@@ -549,5 +493,2 @@ name: assertName(fieldName), | ||
} | ||
function isPlainObj(obj) { | ||
return isObjectLike(obj) && !Array.isArray(obj); | ||
} | ||
function fieldsToFieldsConfig(fields) { | ||
@@ -614,9 +555,2 @@ return mapValue(fields, (field) => ({ | ||
this._interfaces = defineInterfaces.bind(undefined, config); | ||
config.resolveType == null || | ||
typeof config.resolveType === 'function' || | ||
devAssert( | ||
false, | ||
`${this.name} must provide "resolveType" as a function, ` + | ||
`but got: ${inspect(config.resolveType)}.`, | ||
); | ||
} | ||
@@ -690,9 +624,2 @@ get [Symbol.toStringTag]() { | ||
this._types = defineTypes.bind(undefined, config); | ||
config.resolveType == null || | ||
typeof config.resolveType === 'function' || | ||
devAssert( | ||
false, | ||
`${this.name} must provide "resolveType" as a function, ` + | ||
`but got: ${inspect(config.resolveType)}.`, | ||
); | ||
} | ||
@@ -727,9 +654,3 @@ get [Symbol.toStringTag]() { | ||
function defineTypes(config) { | ||
const types = resolveReadonlyArrayThunk(config.types); | ||
Array.isArray(types) || | ||
devAssert( | ||
false, | ||
`Must provide Array of types or a function which returns such an array for Union ${config.name}.`, | ||
); | ||
return types; | ||
return resolveReadonlyArrayThunk(config.types); | ||
} | ||
@@ -766,3 +687,12 @@ /** | ||
this.extensionASTNodes = config.extensionASTNodes ?? []; | ||
this._values = defineEnumValues(this.name, config.values); | ||
this._values = Object.entries(config.values).map( | ||
([valueName, valueConfig]) => ({ | ||
name: assertEnumValueName(valueName), | ||
description: valueConfig.description, | ||
value: valueConfig.value !== undefined ? valueConfig.value : valueName, | ||
deprecationReason: valueConfig.deprecationReason, | ||
extensions: toObjMap(valueConfig.extensions), | ||
astNode: valueConfig.astNode, | ||
}), | ||
); | ||
this._valueLookup = new Map( | ||
@@ -862,25 +792,2 @@ this._values.map((enumValue) => [enumValue.value, enumValue]), | ||
} | ||
function defineEnumValues(typeName, valueMap /* <T> */) { | ||
isPlainObj(valueMap) || | ||
devAssert( | ||
false, | ||
`${typeName} values must be an object with value names as keys.`, | ||
); | ||
return Object.entries(valueMap).map(([valueName, valueConfig]) => { | ||
isPlainObj(valueConfig) || | ||
devAssert( | ||
false, | ||
`${typeName}.${valueName} must refer to an object with a "value" key ` + | ||
`representing an internal value but got: ${inspect(valueConfig)}.`, | ||
); | ||
return { | ||
name: assertEnumValueName(valueName), | ||
description: valueConfig.description, | ||
value: valueConfig.value !== undefined ? valueConfig.value : valueName, | ||
deprecationReason: valueConfig.deprecationReason, | ||
extensions: toObjMap(valueConfig.extensions), | ||
astNode: valueConfig.astNode, | ||
}; | ||
}); | ||
} | ||
/** | ||
@@ -952,7 +859,2 @@ * Input Object Type Definition | ||
const fieldMap = resolveObjMapThunk(config.fields); | ||
isPlainObj(fieldMap) || | ||
devAssert( | ||
false, | ||
`${config.name} fields must be an object with field names as keys or a function which returns such an object.`, | ||
); | ||
return mapValue(fieldMap, (fieldConfig, fieldName) => { | ||
@@ -959,0 +861,0 @@ !('resolve' in fieldConfig) || |
@@ -1,5 +0,3 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { inspect } from '../jsutils/inspect.js'; | ||
import { instanceOf } from '../jsutils/instanceOf.js'; | ||
import { isObjectLike } from '../jsutils/isObjectLike.js'; | ||
import { toObjMap } from '../jsutils/toObjMap.js'; | ||
@@ -40,10 +38,3 @@ import { DirectiveLocation } from '../language/directiveLocation.js'; | ||
this.astNode = config.astNode; | ||
Array.isArray(config.locations) || | ||
devAssert(false, `@${config.name} locations must be an Array.`); | ||
const args = config.args ?? {}; | ||
(isObjectLike(args) && !Array.isArray(args)) || | ||
devAssert( | ||
false, | ||
`@${config.name} args must be an object with argument names as keys.`, | ||
); | ||
this.args = defineArguments(args); | ||
@@ -50,0 +41,0 @@ } |
@@ -11,2 +11,4 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
GraphQLAbstractType, | ||
GraphQLCompositeType, | ||
GraphQLField, | ||
GraphQLInterfaceType, | ||
@@ -136,2 +138,17 @@ GraphQLNamedType, | ||
getDirective(name: string): Maybe<GraphQLDirective>; | ||
/** | ||
* This method looks up the field on the given type definition. | ||
* It has special casing for the three introspection fields, `__schema`, | ||
* `__type` and `__typename`. | ||
* | ||
* `__typename` is special because it can always be queried as a field, even | ||
* in situations where no other fields are allowed, like on a Union. | ||
* | ||
* `__schema` and `__type` could get automatically added to the query type, | ||
* but that would require mutating type definitions, which would cause issues. | ||
*/ | ||
getField( | ||
parentType: GraphQLCompositeType, | ||
fieldName: string, | ||
): GraphQLField<unknown, unknown> | undefined; | ||
toConfig(): GraphQLSchemaNormalizedConfig; | ||
@@ -138,0 +155,0 @@ } |
@@ -1,5 +0,3 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { inspect } from '../jsutils/inspect.js'; | ||
import { instanceOf } from '../jsutils/instanceOf.js'; | ||
import { isObjectLike } from '../jsutils/isObjectLike.js'; | ||
import { toObjMap } from '../jsutils/toObjMap.js'; | ||
@@ -15,3 +13,8 @@ import { OperationTypeNode } from '../language/ast.js'; | ||
import { isDirective, specifiedDirectives } from './directives.js'; | ||
import { __Schema } from './introspection.js'; | ||
import { | ||
__Schema, | ||
SchemaMetaFieldDef, | ||
TypeMetaFieldDef, | ||
TypeNameMetaFieldDef, | ||
} from './introspection.js'; | ||
/** | ||
@@ -102,18 +105,2 @@ * Test if the given value is a GraphQL schema. | ||
this.__validationErrors = config.assumeValid === true ? [] : undefined; | ||
// Check for common mistakes during construction to produce early errors. | ||
isObjectLike(config) || | ||
devAssert(false, 'Must provide configuration object.'); | ||
!config.types || | ||
Array.isArray(config.types) || | ||
devAssert( | ||
false, | ||
`"types" must be Array if provided but got: ${inspect(config.types)}.`, | ||
); | ||
!config.directives || | ||
Array.isArray(config.directives) || | ||
devAssert( | ||
false, | ||
'"directives" must be Array if provided but got: ' + | ||
`${inspect(config.directives)}.`, | ||
); | ||
this.description = config.description; | ||
@@ -167,7 +154,2 @@ this.extensions = toObjMap(config.extensions); | ||
const typeName = namedType.name; | ||
typeName != null || | ||
devAssert( | ||
false, | ||
'One of the provided types for building the Schema is missing a name.', | ||
); | ||
if (this._typeMap[typeName] !== undefined) { | ||
@@ -274,2 +256,33 @@ throw new Error( | ||
} | ||
/** | ||
* This method looks up the field on the given type definition. | ||
* It has special casing for the three introspection fields, `__schema`, | ||
* `__type` and `__typename`. | ||
* | ||
* `__typename` is special because it can always be queried as a field, even | ||
* in situations where no other fields are allowed, like on a Union. | ||
* | ||
* `__schema` and `__type` could get automatically added to the query type, | ||
* but that would require mutating type definitions, which would cause issues. | ||
*/ | ||
getField(parentType, fieldName) { | ||
switch (fieldName) { | ||
case SchemaMetaFieldDef.name: | ||
return this.getQueryType() === parentType | ||
? SchemaMetaFieldDef | ||
: undefined; | ||
case TypeMetaFieldDef.name: | ||
return this.getQueryType() === parentType | ||
? TypeMetaFieldDef | ||
: undefined; | ||
case TypeNameMetaFieldDef.name: | ||
return TypeNameMetaFieldDef; | ||
} | ||
// this function is part "hot" path inside executor and check presence | ||
// of 'getFields' is faster than to use `!isUnionType` | ||
if ('getFields' in parentType) { | ||
return parentType.getFields()[fieldName]; | ||
} | ||
return undefined; | ||
} | ||
toConfig() { | ||
@@ -276,0 +289,0 @@ return { |
@@ -1,3 +0,1 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { Kind } from '../language/kinds.js'; | ||
import { parse } from '../language/parser.js'; | ||
@@ -19,4 +17,2 @@ import { specifiedDirectives } from '../type/directives.js'; | ||
export function buildASTSchema(documentAST, options) { | ||
(documentAST != null && documentAST.kind === Kind.DOCUMENT) || | ||
devAssert(false, 'Must provide valid Document AST.'); | ||
if (options?.assumeValid !== true && options?.assumeValidSDL !== true) { | ||
@@ -23,0 +19,0 @@ assertValidSDL(documentAST); |
@@ -39,2 +39,4 @@ import { devAssert } from '../jsutils/devAssert.js'; | ||
export function buildClientSchema(introspection, options) { | ||
// Even even though `introspection` argument is typed in most cases it's received | ||
// as untyped value from server, so we will do an additional check here. | ||
(isObjectLike(introspection) && isObjectLike(introspection.__schema)) || | ||
@@ -41,0 +43,0 @@ devAssert( |
@@ -1,2 +0,1 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { inspect } from '../jsutils/inspect.js'; | ||
@@ -33,2 +32,3 @@ import { invariant } from '../jsutils/invariant.js'; | ||
GraphQLSpecifiedByDirective, | ||
isSpecifiedDirective, | ||
} from '../type/directives.js'; | ||
@@ -61,4 +61,2 @@ import { | ||
assertSchema(schema); | ||
(documentAST != null && documentAST.kind === Kind.DOCUMENT) || | ||
devAssert(false, 'Must provide valid Document AST.'); | ||
if (options?.assumeValid !== true && options?.assumeValidSDL !== true) { | ||
@@ -167,2 +165,6 @@ assertValidSDLExtension(documentAST, schema); | ||
function replaceDirective(directive) { | ||
if (isSpecifiedDirective(directive)) { | ||
// Builtin directives are not extended. | ||
return directive; | ||
} | ||
const config = directive.toConfig(); | ||
@@ -169,0 +171,0 @@ return new GraphQLDirective({ |
@@ -56,3 +56,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
schema: GraphQLSchema, | ||
parentType: GraphQLType, | ||
parentType: GraphQLCompositeType, | ||
fieldNode: FieldNode, | ||
@@ -59,0 +59,0 @@ ) => Maybe<GraphQLField<unknown, unknown>>; |
@@ -11,3 +11,2 @@ import { isNode } from '../language/ast.js'; | ||
isInputType, | ||
isInterfaceType, | ||
isListType, | ||
@@ -17,7 +16,2 @@ isObjectType, | ||
} from '../type/definition.js'; | ||
import { | ||
SchemaMetaFieldDef, | ||
TypeMetaFieldDef, | ||
TypeNameMetaFieldDef, | ||
} from '../type/introspection.js'; | ||
import { typeFromAST } from './typeFromAST.js'; | ||
@@ -250,24 +244,4 @@ /** | ||
} | ||
/** | ||
* Not exactly the same as the executor's definition of getFieldDef, in this | ||
* statically evaluated environment we do not always have an Object type, | ||
* and need to handle Interface and Union types. | ||
*/ | ||
function getFieldDef(schema, parentType, fieldNode) { | ||
const name = fieldNode.name.value; | ||
if ( | ||
name === SchemaMetaFieldDef.name && | ||
schema.getQueryType() === parentType | ||
) { | ||
return SchemaMetaFieldDef; | ||
} | ||
if (name === TypeMetaFieldDef.name && schema.getQueryType() === parentType) { | ||
return TypeMetaFieldDef; | ||
} | ||
if (name === TypeNameMetaFieldDef.name && isCompositeType(parentType)) { | ||
return TypeNameMetaFieldDef; | ||
} | ||
if (isObjectType(parentType) || isInterfaceType(parentType)) { | ||
return parentType.getFields()[name]; | ||
} | ||
return schema.getField(parentType, fieldNode.name.value); | ||
} | ||
@@ -274,0 +248,0 @@ /** |
@@ -11,3 +11,2 @@ export { validate } from './validate'; | ||
export { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule'; | ||
export { KnownOperationTypesRule } from './rules/KnownOperationTypesRule'; | ||
export { KnownTypeNamesRule } from './rules/KnownTypeNamesRule'; | ||
@@ -14,0 +13,0 @@ export { LoneAnonymousOperationRule } from './rules/LoneAnonymousOperationRule'; |
@@ -17,4 +17,2 @@ export { validate } from './validate.js'; | ||
export { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js'; | ||
// Spec Section: "Operation Type Existence" | ||
export { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js'; | ||
// Spec Section: "Fragment Spread Type Existence" | ||
@@ -21,0 +19,0 @@ export { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js'; |
@@ -15,2 +15,5 @@ import { inspect } from '../../jsutils/inspect.js'; | ||
import { typeFromAST } from '../../utilities/typeFromAST.js'; | ||
/* eslint-disable max-params */ | ||
// This file contains a lot of such errors but we plan to refactor it anyway | ||
// so just disable it for entire file. | ||
function reasonMessage(reason) { | ||
@@ -17,0 +20,0 @@ if (Array.isArray(reason)) { |
@@ -16,4 +16,2 @@ // Spec Section: "Executable Definitions" | ||
import { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js'; | ||
// Spec Section: "Operation Type Existence" | ||
import { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js'; | ||
// Spec Section: "Fragment Spread Type Existence" | ||
@@ -79,3 +77,2 @@ import { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js'; | ||
ExecutableDefinitionsRule, | ||
KnownOperationTypesRule, | ||
UniqueOperationNamesRule, | ||
@@ -82,0 +79,0 @@ LoneAnonymousOperationRule, |
@@ -1,2 +0,1 @@ | ||
import { devAssert } from '../jsutils/devAssert.js'; | ||
import { GraphQLError } from '../error/GraphQLError.js'; | ||
@@ -40,3 +39,2 @@ import { visit, visitInParallel } from '../language/visitor.js'; | ||
const maxErrors = options?.maxErrors ?? 100; | ||
documentAST != null || devAssert(false, 'Must provide document.'); | ||
// If the schema used for validation is invalid, throw an error. | ||
@@ -43,0 +41,0 @@ assertValidSchema(schema); |
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
679342
258
20804