graphql

Search for an npm package
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true,
});
exports.extendSchema = extendSchema;
exports.extendSchemaImpl = extendSchemaImpl;
var _devAssert = require('../jsutils/devAssert.js');
var _inspect = require('../jsutils/inspect.js');
var _invariant = require('../jsutils/invariant.js');
var _keyMap = require('../jsutils/keyMap.js');
var _mapValue = require('../jsutils/mapValue.js');
var _kinds = require('../language/kinds.js');
var _predicates = require('../language/predicates.js');
var _definition = require('../type/definition.js');
var _directives = require('../type/directives.js');
var _introspection = require('../type/introspection.js');
var _scalars = require('../type/scalars.js');
var _schema = require('../type/schema.js');
var _validate = require('../validation/validate.js');
var _values = require('../execution/values.js');
var _valueFromAST = require('./valueFromAST.js');
/**
* Produces a new schema given an existing schema and a document which may
* contain GraphQL type extensions and definitions. The original schema will
* remain unaltered.
*
* Because a schema represents a graph of references, a schema cannot be
* extended without effectively making an entire copy. We do not know until it's
* too late if subgraphs remain unchanged.
*
* This algorithm copies the provided schema, applying extensions while
* producing the copy. The original schema remains unaltered.
*/
function extendSchema(schema, documentAST, options) {
(0, _schema.assertSchema)(schema);
(documentAST != null && documentAST.kind === _kinds.Kind.DOCUMENT) ||
(0, _devAssert.devAssert)(false, 'Must provide valid Document AST.');
if (
(options === null || options === void 0 ? void 0 : options.assumeValid) !==
true &&
(options === null || options === void 0
? void 0
: options.assumeValidSDL) !== true
) {
(0, _validate.assertValidSDLExtension)(documentAST, schema);
}
const schemaConfig = schema.toConfig();
const extendedConfig = extendSchemaImpl(schemaConfig, documentAST, options);
return schemaConfig === extendedConfig
? schema
: new _schema.GraphQLSchema(extendedConfig);
}
/**
* @internal
*/
function extendSchemaImpl(schemaConfig, documentAST, options) {
var _schemaDef, _schemaDef$descriptio, _schemaDef2, _options$assumeValid;
// Collect the type definitions and extensions found in the document.
const typeDefs = [];
const typeExtensionsMap = Object.create(null); // New directives and types are separate because a directives and types can
// have the same name. For example, a type named "skip".
const directiveDefs = [];
let schemaDef; // Schema extensions are collected which may add additional operation types.
const schemaExtensions = [];
for (const def of documentAST.definitions) {
if (def.kind === _kinds.Kind.SCHEMA_DEFINITION) {
schemaDef = def;
} else if (def.kind === _kinds.Kind.SCHEMA_EXTENSION) {
schemaExtensions.push(def);
} else if ((0, _predicates.isTypeDefinitionNode)(def)) {
typeDefs.push(def);
} else if ((0, _predicates.isTypeExtensionNode)(def)) {
const extendedTypeName = def.name.value;
const existingTypeExtensions = typeExtensionsMap[extendedTypeName];
typeExtensionsMap[extendedTypeName] = existingTypeExtensions
? existingTypeExtensions.concat([def])
: [def];
} else if (def.kind === _kinds.Kind.DIRECTIVE_DEFINITION) {
directiveDefs.push(def);
}
} // 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
) {
return schemaConfig;
}
const typeMap = Object.create(null);
for (const existingType of schemaConfig.types) {
typeMap[existingType.name] = extendNamedType(existingType);
}
for (const typeNode of typeDefs) {
var _stdTypeMap$name;
const name = typeNode.name.value;
typeMap[name] =
(_stdTypeMap$name = stdTypeMap[name]) !== null &&
_stdTypeMap$name !== void 0
? _stdTypeMap$name
: buildType(typeNode);
}
const operationTypes = {
// Get the extended root operation types.
query: schemaConfig.query && replaceNamedType(schemaConfig.query),
mutation: schemaConfig.mutation && replaceNamedType(schemaConfig.mutation),
subscription:
schemaConfig.subscription && replaceNamedType(schemaConfig.subscription),
// Then, incorporate schema definition and all schema extensions.
...(schemaDef && getOperationTypes([schemaDef])),
...getOperationTypes(schemaExtensions),
}; // Then produce and return a Schema config with these types.
return {
description:
(_schemaDef = schemaDef) === null || _schemaDef === void 0
? void 0
: (_schemaDef$descriptio = _schemaDef.description) === null ||
_schemaDef$descriptio === void 0
? void 0
: _schemaDef$descriptio.value,
...operationTypes,
types: Object.values(typeMap),
directives: [
...schemaConfig.directives.map(replaceDirective),
...directiveDefs.map(buildDirective),
],
extensions: Object.create(null),
astNode:
(_schemaDef2 = schemaDef) !== null && _schemaDef2 !== void 0
? _schemaDef2
: schemaConfig.astNode,
extensionASTNodes: schemaConfig.extensionASTNodes.concat(schemaExtensions),
assumeValid:
(_options$assumeValid =
options === null || options === void 0
? void 0
: options.assumeValid) !== null && _options$assumeValid !== void 0
? _options$assumeValid
: false,
}; // Below are functions used for producing this schema that have closed over
// this scope and have access to the schema, cache, and newly defined types.
function replaceType(type) {
if ((0, _definition.isListType)(type)) {
// @ts-expect-error
return new _definition.GraphQLList(replaceType(type.ofType));
}
if ((0, _definition.isNonNullType)(type)) {
// @ts-expect-error
return new _definition.GraphQLNonNull(replaceType(type.ofType));
} // @ts-expect-error FIXME
return replaceNamedType(type);
}
function replaceNamedType(type) {
// Note: While this could make early assertions to get the correctly
// typed values, that would throw immediately while type system
// validation with validateSchema() will produce more actionable results.
return typeMap[type.name];
}
function replaceDirective(directive) {
const config = directive.toConfig();
return new _directives.GraphQLDirective({
...config,
args: (0, _mapValue.mapValue)(config.args, extendArg),
});
}
function extendNamedType(type) {
if (
(0, _introspection.isIntrospectionType)(type) ||
(0, _scalars.isSpecifiedScalarType)(type)
) {
// Builtin types are not extended.
return type;
}
if ((0, _definition.isScalarType)(type)) {
return extendScalarType(type);
}
if ((0, _definition.isObjectType)(type)) {
return extendObjectType(type);
}
if ((0, _definition.isInterfaceType)(type)) {
return extendInterfaceType(type);
}
if ((0, _definition.isUnionType)(type)) {
return extendUnionType(type);
}
if ((0, _definition.isEnumType)(type)) {
return extendEnumType(type);
}
if ((0, _definition.isInputObjectType)(type)) {
return extendInputObjectType(type);
}
/* c8 ignore next 3 */
// Not reachable, all possible type definition nodes have been considered.
false ||
(0, _invariant.invariant)(
false,
'Unexpected type: ' + (0, _inspect.inspect)(type),
);
}
function extendInputObjectType(type) {
var _typeExtensionsMap$co;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$co = typeExtensionsMap[config.name]) !== null &&
_typeExtensionsMap$co !== void 0
? _typeExtensionsMap$co
: [];
return new _definition.GraphQLInputObjectType({
...config,
fields: () => ({
...(0, _mapValue.mapValue)(config.fields, (field) => ({
...field,
type: replaceType(field.type),
})),
...buildInputFieldMap(extensions),
}),
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendEnumType(type) {
var _typeExtensionsMap$ty;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$ty = typeExtensionsMap[type.name]) !== null &&
_typeExtensionsMap$ty !== void 0
? _typeExtensionsMap$ty
: [];
return new _definition.GraphQLEnumType({
...config,
values: { ...config.values, ...buildEnumValueMap(extensions) },
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendScalarType(type) {
var _typeExtensionsMap$co2;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$co2 = typeExtensionsMap[config.name]) !== null &&
_typeExtensionsMap$co2 !== void 0
? _typeExtensionsMap$co2
: [];
let specifiedByURL = config.specifiedByURL;
for (const extensionNode of extensions) {
var _getSpecifiedByURL;
specifiedByURL =
(_getSpecifiedByURL = getSpecifiedByURL(extensionNode)) !== null &&
_getSpecifiedByURL !== void 0
? _getSpecifiedByURL
: specifiedByURL;
}
return new _definition.GraphQLScalarType({
...config,
specifiedByURL,
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendObjectType(type) {
var _typeExtensionsMap$co3;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$co3 = typeExtensionsMap[config.name]) !== null &&
_typeExtensionsMap$co3 !== void 0
? _typeExtensionsMap$co3
: [];
return new _definition.GraphQLObjectType({
...config,
interfaces: () => [
...type.getInterfaces().map(replaceNamedType),
...buildInterfaces(extensions),
],
fields: () => ({
...(0, _mapValue.mapValue)(config.fields, extendField),
...buildFieldMap(extensions),
}),
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendInterfaceType(type) {
var _typeExtensionsMap$co4;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$co4 = typeExtensionsMap[config.name]) !== null &&
_typeExtensionsMap$co4 !== void 0
? _typeExtensionsMap$co4
: [];
return new _definition.GraphQLInterfaceType({
...config,
interfaces: () => [
...type.getInterfaces().map(replaceNamedType),
...buildInterfaces(extensions),
],
fields: () => ({
...(0, _mapValue.mapValue)(config.fields, extendField),
...buildFieldMap(extensions),
}),
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendUnionType(type) {
var _typeExtensionsMap$co5;
const config = type.toConfig();
const extensions =
(_typeExtensionsMap$co5 = typeExtensionsMap[config.name]) !== null &&
_typeExtensionsMap$co5 !== void 0
? _typeExtensionsMap$co5
: [];
return new _definition.GraphQLUnionType({
...config,
types: () => [
...type.getTypes().map(replaceNamedType),
...buildUnionTypes(extensions),
],
extensionASTNodes: config.extensionASTNodes.concat(extensions),
});
}
function extendField(field) {
return {
...field,
type: replaceType(field.type),
args: field.args && (0, _mapValue.mapValue)(field.args, extendArg),
};
}
function extendArg(arg) {
return { ...arg, type: replaceType(arg.type) };
}
function getOperationTypes(nodes) {
const opTypes = {};
for (const node of nodes) {
var _node$operationTypes;
// FIXME: https://github.com/graphql/graphql-js/issues/2203
const operationTypesNodes =
/* c8 ignore next */
(_node$operationTypes = node.operationTypes) !== null &&
_node$operationTypes !== void 0
? _node$operationTypes
: [];
for (const operationType of operationTypesNodes) {
// Note: While this could make early assertions to get the correctly
// typed values below, that would throw immediately while type system
// validation with validateSchema() will produce more actionable results.
// @ts-expect-error
opTypes[operationType.operation] = getNamedType(operationType.type);
}
}
return opTypes;
}
function getNamedType(node) {
var _stdTypeMap$name2;
const name = node.name.value;
const type =
(_stdTypeMap$name2 = stdTypeMap[name]) !== null &&
_stdTypeMap$name2 !== void 0
? _stdTypeMap$name2
: typeMap[name];
if (type === undefined) {
throw new Error(`Unknown type: "${name}".`);
}
return type;
}
function getWrappedType(node) {
if (node.kind === _kinds.Kind.LIST_TYPE) {
return new _definition.GraphQLList(getWrappedType(node.type));
}
if (node.kind === _kinds.Kind.NON_NULL_TYPE) {
return new _definition.GraphQLNonNull(getWrappedType(node.type));
}
return getNamedType(node);
}
function buildDirective(node) {
var _node$description;
return new _directives.GraphQLDirective({
name: node.name.value,
description:
(_node$description = node.description) === null ||
_node$description === void 0
? void 0
: _node$description.value,
// @ts-expect-error
locations: node.locations.map(({ value }) => value),
isRepeatable: node.repeatable,
args: buildArgumentMap(node.arguments),
astNode: node,
});
}
function buildFieldMap(nodes) {
const fieldConfigMap = Object.create(null);
for (const node of nodes) {
var _node$fields;
// FIXME: https://github.com/graphql/graphql-js/issues/2203
const nodeFields =
/* c8 ignore next */
(_node$fields = node.fields) !== null && _node$fields !== void 0
? _node$fields
: [];
for (const field of nodeFields) {
var _field$description;
fieldConfigMap[field.name.value] = {
// Note: While this could make assertions to get the correctly typed
// value, that would throw immediately while type system validation
// with validateSchema() will produce more actionable results.
type: getWrappedType(field.type),
description:
(_field$description = field.description) === null ||
_field$description === void 0
? void 0
: _field$description.value,
args: buildArgumentMap(field.arguments),
deprecationReason: getDeprecationReason(field),
astNode: field,
};
}
}
return fieldConfigMap;
}
function buildArgumentMap(args) {
// FIXME: https://github.com/graphql/graphql-js/issues/2203
const argsNodes =
/* c8 ignore next */
args !== null && args !== void 0 ? args : [];
const argConfigMap = Object.create(null);
for (const arg of argsNodes) {
var _arg$description;
// Note: While this could make assertions to get the correctly typed
// value, that would throw immediately while type system validation
// with validateSchema() will produce more actionable results.
const type = getWrappedType(arg.type);
argConfigMap[arg.name.value] = {
type,
description:
(_arg$description = arg.description) === null ||
_arg$description === void 0
? void 0
: _arg$description.value,
defaultValue: (0, _valueFromAST.valueFromAST)(arg.defaultValue, type),
deprecationReason: getDeprecationReason(arg),
astNode: arg,
};
}
return argConfigMap;
}
function buildInputFieldMap(nodes) {
const inputFieldMap = Object.create(null);
for (const node of nodes) {
var _node$fields2;
// FIXME: https://github.com/graphql/graphql-js/issues/2203
const fieldsNodes =
/* c8 ignore next */
(_node$fields2 = node.fields) !== null && _node$fields2 !== void 0
? _node$fields2
: [];
for (const field of fieldsNodes) {
var _field$description2;
// Note: While this could make assertions to get the correctly typed
// value, that would throw immediately while type system validation
// with validateSchema() will produce more actionable results.
const type = getWrappedType(field.type);
inputFieldMap[field.name.value] = {
type,
description:
(_field$description2 = field.description) === null ||
_field$description2 === void 0
? void 0
: _field$description2.value,
defaultValue: (0, _valueFromAST.valueFromAST)(
field.defaultValue,
type,
),
deprecationReason: getDeprecationReason(field),
astNode: field,
};
}
}
return inputFieldMap;
}
function buildEnumValueMap(nodes) {
const enumValueMap = Object.create(null);
for (const node of nodes) {
var _node$values;
// FIXME: https://github.com/graphql/graphql-js/issues/2203
const valuesNodes =
/* c8 ignore next */
(_node$values = node.values) !== null && _node$values !== void 0
? _node$values
: [];
for (const value of valuesNodes) {
var _value$description;
enumValueMap[value.name.value] = {
description:
(_value$description = value.description) === null ||
_value$description === void 0
? void 0
: _value$description.value,
deprecationReason: getDeprecationReason(value),
astNode: value,
};
}
}
return enumValueMap;
}
function buildInterfaces(nodes) {
// Note: While this could make assertions to get the correctly typed
// values below, that would throw immediately while type system
// validation with validateSchema() will produce more actionable results.
// @ts-expect-error
return nodes.flatMap(
// FIXME: https://github.com/graphql/graphql-js/issues/2203
(node) => {
var _node$interfaces$map, _node$interfaces;
return (
/* c8 ignore next */
(_node$interfaces$map =
(_node$interfaces = node.interfaces) === null ||
_node$interfaces === void 0
? void 0
: _node$interfaces.map(getNamedType)) !== null &&
_node$interfaces$map !== void 0
? _node$interfaces$map
: []
);
},
);
}
function buildUnionTypes(nodes) {
// Note: While this could make assertions to get the correctly typed
// values below, that would throw immediately while type system
// validation with validateSchema() will produce more actionable results.
// @ts-expect-error
return nodes.flatMap(
// FIXME: https://github.com/graphql/graphql-js/issues/2203
(node) => {
var _node$types$map, _node$types;
return (
/* c8 ignore next */
(_node$types$map =
(_node$types = node.types) === null || _node$types === void 0
? void 0
: _node$types.map(getNamedType)) !== null &&
_node$types$map !== void 0
? _node$types$map
: []
);
},
);
}
function buildType(astNode) {
var _typeExtensionsMap$na;
const name = astNode.name.value;
const extensionASTNodes =
(_typeExtensionsMap$na = typeExtensionsMap[name]) !== null &&
_typeExtensionsMap$na !== void 0
? _typeExtensionsMap$na
: [];
switch (astNode.kind) {
case _kinds.Kind.OBJECT_TYPE_DEFINITION: {
var _astNode$description;
const allNodes = [astNode, ...extensionASTNodes];
return new _definition.GraphQLObjectType({
name,
description:
(_astNode$description = astNode.description) === null ||
_astNode$description === void 0
? void 0
: _astNode$description.value,
interfaces: () => buildInterfaces(allNodes),
fields: () => buildFieldMap(allNodes),
astNode,
extensionASTNodes,
});
}
case _kinds.Kind.INTERFACE_TYPE_DEFINITION: {
var _astNode$description2;
const allNodes = [astNode, ...extensionASTNodes];
return new _definition.GraphQLInterfaceType({
name,
description:
(_astNode$description2 = astNode.description) === null ||
_astNode$description2 === void 0
? void 0
: _astNode$description2.value,
interfaces: () => buildInterfaces(allNodes),
fields: () => buildFieldMap(allNodes),
astNode,
extensionASTNodes,
});
}
case _kinds.Kind.ENUM_TYPE_DEFINITION: {
var _astNode$description3;
const allNodes = [astNode, ...extensionASTNodes];
return new _definition.GraphQLEnumType({
name,
description:
(_astNode$description3 = astNode.description) === null ||
_astNode$description3 === void 0
? void 0
: _astNode$description3.value,
values: buildEnumValueMap(allNodes),
astNode,
extensionASTNodes,
});
}
case _kinds.Kind.UNION_TYPE_DEFINITION: {
var _astNode$description4;
const allNodes = [astNode, ...extensionASTNodes];
return new _definition.GraphQLUnionType({
name,
description:
(_astNode$description4 = astNode.description) === null ||
_astNode$description4 === void 0
? void 0
: _astNode$description4.value,
types: () => buildUnionTypes(allNodes),
astNode,
extensionASTNodes,
});
}
case _kinds.Kind.SCALAR_TYPE_DEFINITION: {
var _astNode$description5;
return new _definition.GraphQLScalarType({
name,
description:
(_astNode$description5 = astNode.description) === null ||
_astNode$description5 === void 0
? void 0
: _astNode$description5.value,
specifiedByURL: getSpecifiedByURL(astNode),
astNode,
extensionASTNodes,
});
}
case _kinds.Kind.INPUT_OBJECT_TYPE_DEFINITION: {
var _astNode$description6;
const allNodes = [astNode, ...extensionASTNodes];
return new _definition.GraphQLInputObjectType({
name,
description:
(_astNode$description6 = astNode.description) === null ||
_astNode$description6 === void 0
? void 0
: _astNode$description6.value,
fields: () => buildInputFieldMap(allNodes),
astNode,
extensionASTNodes,
});
}
}
}
}
const stdTypeMap = (0, _keyMap.keyMap)(
[..._scalars.specifiedScalarTypes, ..._introspection.introspectionTypes],
(type) => type.name,
);
/**
* Given a field or enum value node, returns the string value for the
* deprecation reason.
*/
function getDeprecationReason(node) {
const deprecated = (0, _values.getDirectiveValues)(
_directives.GraphQLDeprecatedDirective,
node,
); // @ts-expect-error validated by `getDirectiveValues`
return deprecated === null || deprecated === void 0
? void 0
: deprecated.reason;
}
/**
* Given a scalar node, returns the string value for the specifiedByURL.
*/
function getSpecifiedByURL(node) {
const specifiedBy = (0, _values.getDirectiveValues)(
_directives.GraphQLSpecifiedByDirective,
node,
); // @ts-expect-error validated by `getDirectiveValues`
return specifiedBy === null || specifiedBy === void 0
? void 0
: specifiedBy.url;
}