UNPKG

@theguild/federation-composition

Version:

Open Source Composition library for Apollo Federation

1,126 lines (1,125 loc) 50.8 kB
import { Kind, OperationTypeNode, } from 'graphql'; import { print } from '../graphql/printer.js'; import { isFederationLink } from '../specifications/federation.js'; import { printOutputType } from './helpers.js'; export var TypeKind; (function (TypeKind) { TypeKind["OBJECT"] = "OBJECT"; TypeKind["INTERFACE"] = "INTERFACE"; TypeKind["ENUM"] = "ENUM"; TypeKind["UNION"] = "UNION"; TypeKind["SCALAR"] = "SCALAR"; TypeKind["INPUT_OBJECT"] = "INPUT_OBJECT"; TypeKind["DIRECTIVE"] = "DIRECTIVE"; })(TypeKind || (TypeKind = {})); const MISSING = 'MISSING'; export function createSubgraphStateBuilder(graph, typeDefs, version, links) { const linksWithDirective = links.filter(link => !isFederationLink(link) && link.imports.some(im => im.kind === 'directive')); const isLinkSpecManuallyProvided = typeDefs.definitions.some(def => def.kind === Kind.DIRECTIVE_DEFINITION && def.name.value === 'link' && def.locations.every(loc => loc.value === 'SCHEMA')); const state = { graph: { ...graph, version, }, types: new Map(), schema: {}, links: linksWithDirective, specs: { tag: false, inaccessible: false, link: isLinkSpecManuallyProvided, }, version, }; const schemaDef = typeDefs.definitions.find(isSchemaDefinition); const expectedQueryTypeName = decideOnRootTypeName(schemaDef, OperationTypeNode.QUERY, 'Query'); const expectedMutationTypeName = decideOnRootTypeName(schemaDef, OperationTypeNode.MUTATION, 'Mutation'); const expectedSubscriptionTypeName = decideOnRootTypeName(schemaDef, OperationTypeNode.SUBSCRIPTION, 'Subscription'); const composedDirectives = new Set(); const directiveBuilder = directiveFactory(state); const scalarTypeBuilder = scalarTypeFactory(state); const objectTypeBuilder = objectTypeFactory(state, renameObjectType); const interfaceTypeBuilder = interfaceTypeFactory(state); const inputObjectTypeBuilder = inputObjectTypeFactory(state); const unionTypeBuilder = unionTypeFactory(state); const enumTypeBuilder = enumTypeFactory(state); function renameObjectType(typeName) { if (typeName === expectedQueryTypeName) { return 'Query'; } if (typeName === expectedMutationTypeName) { return 'Mutation'; } if (typeName === expectedSubscriptionTypeName) { return 'Subscription'; } return typeName; } return { directive: directiveBuilder, scalarType: scalarTypeBuilder, objectType: objectTypeBuilder, interfaceType: interfaceTypeBuilder, inputObjectType: inputObjectTypeBuilder, unionType: unionTypeBuilder, enumType: enumTypeBuilder, composedDirectives, state, markSpecAsUsed(specName) { state.specs[specName] = true; }, visitor(typeNodeInfo) { const enumTypes = typeDefs.definitions.filter(isEnumType); const enumTypesByName = new Set(enumTypes ? enumTypes.map(enumType => enumType.name.value) : []); return { FieldDefinition(node) { const typeDef = typeNodeInfo.getTypeDef(); if (!typeDef) { throw new Error(`Expected to find a type definition or extension`); } const isInterfaceType = typeDef.kind === Kind.INTERFACE_TYPE_DEFINITION || typeDef.kind === Kind.INTERFACE_TYPE_EXTENSION; const isObjectType = typeDef.kind === Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === Kind.OBJECT_TYPE_EXTENSION; const outputTypeName = resolveTypeName(node.type); const referencesEnumType = enumTypesByName.has(outputTypeName); if (referencesEnumType) { enumTypeBuilder.setReferencedByOutputType(outputTypeName, `${typeDef.name.value}.${node.name.value}`); } if (isInterfaceType) { interfaceTypeBuilder.field.setType(typeDef.name.value, node.name.value, printOutputType(node.type)); return; } if (!isObjectType) { throw new Error(`Expected to find an object type`); } objectTypeBuilder.field.setType(typeDef.name.value, node.name.value, printOutputType(node.type)); }, InputValueDefinition(node) { const typeDef = typeNodeInfo.getTypeDef(); const fieldDef = typeNodeInfo.getFieldDef(); if (!typeDef) { return; } const isInputObjectType = typeDef.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION || typeDef.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION; const isObjectType = typeDef.kind === Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === Kind.OBJECT_TYPE_EXTENSION; const isInterfaceType = typeDef.kind === Kind.INTERFACE_TYPE_DEFINITION || typeDef.kind === Kind.INTERFACE_TYPE_EXTENSION; const outputTypeName = resolveTypeName(node.type); const referencesEnumType = enumTypesByName.has(outputTypeName); if (isInputObjectType) { inputObjectTypeBuilder.field.setType(typeDef.name.value, node.name.value, printOutputType(node.type)); if (referencesEnumType) { enumTypeBuilder.setReferencedByInputType(outputTypeName, `${typeDef.name.value}.${node.name.value}`); } if (node.defaultValue) { inputObjectTypeBuilder.field.setDefaultValue(typeDef.name.value, node.name.value, print(node.defaultValue)); } } if (isObjectType && fieldDef) { objectTypeBuilder.field.arg.setType(typeDef.name.value, fieldDef.name.value, node.name.value, printOutputType(node.type)); if (node.defaultValue) { objectTypeBuilder.field.arg.setDefaultValue(typeDef.name.value, fieldDef.name.value, node.name.value, print(node.defaultValue)); } if (referencesEnumType) { enumTypeBuilder.setReferencedByInputType(outputTypeName, `${typeDef.name.value}.${fieldDef.name.value}(${node.name.value}:)`); } } if (isInterfaceType && fieldDef) { interfaceTypeBuilder.field.arg.setType(typeDef.name.value, fieldDef.name.value, node.name.value, printOutputType(node.type)); if (node.defaultValue) { interfaceTypeBuilder.field.arg.setDefaultValue(typeDef.name.value, fieldDef.name.value, node.name.value, print(node.defaultValue)); } } }, ObjectTypeDefinition(node) { objectTypeBuilder.setDefinition(node.name.value); if (node.name.value === expectedQueryTypeName) { state.schema.queryType = renameObjectType(node.name.value); } else if (node.name.value === expectedMutationTypeName) { state.schema.mutationType = renameObjectType(node.name.value); } else if (node.name.value === expectedSubscriptionTypeName) { state.schema.subscriptionType = renameObjectType(node.name.value); } if (node.interfaces) { for (const interfaceNode of node.interfaces) { objectTypeBuilder.setInterface(node.name.value, interfaceNode.name.value); } } }, ObjectTypeExtension(node) { if (node.name.value === expectedQueryTypeName) { state.schema.queryType = renameObjectType(node.name.value); } else if (node.name.value === expectedMutationTypeName) { state.schema.mutationType = renameObjectType(node.name.value); } else if (node.name.value === expectedSubscriptionTypeName) { state.schema.subscriptionType = renameObjectType(node.name.value); } objectTypeBuilder.setExtension(node.name.value, 'extend'); if (node.interfaces) { for (const interfaceNode of node.interfaces) { objectTypeBuilder.setInterface(node.name.value, interfaceNode.name.value); } } }, InterfaceTypeDefinition(node) { interfaceTypeBuilder.setDefinition(node.name.value); if (node.interfaces) { for (const interfaceNode of node.interfaces) { interfaceTypeBuilder.setInterface(node.name.value, interfaceNode.name.value); } } }, InterfaceTypeExtension(node) { if (version !== 'v1.0') { interfaceTypeBuilder.setExtension(node.name.value); } if (node.interfaces) { for (const interfaceNode of node.interfaces) { interfaceTypeBuilder.setInterface(node.name.value, interfaceNode.name.value); } } }, UnionTypeDefinition(node) { unionTypeBuilder.setDefinition(node.name.value); if (node.types) { for (const member of node.types) { unionTypeBuilder.setMember(node.name.value, member.name.value); } } }, UnionTypeExtension(node) { if (node.types) { for (const member of node.types) { unionTypeBuilder.setMember(node.name.value, member.name.value); } } }, EnumTypeDefinition(node) { enumTypeBuilder.setDefinition(node.name.value); }, EnumValueDefinition(node) { const typeDef = typeNodeInfo.getTypeDef(); if (!typeDef) { return; } enumTypeBuilder.value.setValue(typeDef.name.value, node.name.value); }, InputObjectTypeDefinition(node) { inputObjectTypeBuilder.setDefinition(node.name.value); }, ScalarTypeDefinition(node) { scalarTypeBuilder.setDefinition(node.name.value); }, ScalarTypeExtension() { }, Directive(node) { if (composedDirectives.has(node.name.value)) { const typeDef = typeNodeInfo.getTypeDef(); const fieldDef = typeNodeInfo.getFieldDef(); const argDef = typeNodeInfo.getArgumentDef(); if (!typeDef) { return; } switch (typeDef.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.OBJECT_TYPE_EXTENSION: if (argDef) { objectTypeBuilder.field.arg.setDirective(typeDef.name.value, fieldDef.name.value, argDef.name.value, node); } else if (fieldDef) { objectTypeBuilder.field.setDirective(typeDef.name.value, fieldDef.name.value, node); } else { objectTypeBuilder.setDirective(typeDef.name.value, node); } break; case Kind.INTERFACE_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_EXTENSION: if (argDef) { interfaceTypeBuilder.field.arg.setDirective(typeDef.name.value, fieldDef.name.value, argDef.name.value, node); } else if (fieldDef) { interfaceTypeBuilder.field.setDirective(typeDef.name.value, fieldDef.name.value, node); } else { interfaceTypeBuilder.setDirective(typeDef.name.value, node); } break; case Kind.SCALAR_TYPE_DEFINITION: case Kind.SCALAR_TYPE_EXTENSION: { scalarTypeBuilder.setDirective(typeDef.name.value, node); break; } case Kind.DIRECTIVE_DEFINITION: { if (fieldDef) { directiveBuilder.arg.setDirective(typeDef.name.value, fieldDef.name.value, node); } break; } default: throw new Error(`Directives on "${typeDef.kind}" types are not supported yet`); } } else if (node.name.value === 'specifiedBy') { const typeDef = typeNodeInfo.getTypeDef(); if (typeDef && (typeDef.kind === Kind.SCALAR_TYPE_DEFINITION || typeDef.kind === Kind.SCALAR_TYPE_EXTENSION)) { const urlValue = node.arguments?.find(arg => arg.name.value === 'url')?.value; if (urlValue?.kind === Kind.STRING) { scalarTypeBuilder.setSpecifiedBy(typeDef.name.value, urlValue.value); } } } else if (node.name.value === 'deprecated') { const typeDef = typeNodeInfo.getTypeDef(); const fieldDef = typeNodeInfo.getFieldDef(); const argDef = typeNodeInfo.getArgumentDef(); if (!typeDef) { return; } const reasonValue = node.arguments?.find(arg => arg.name.value === 'reason')?.value; const reason = reasonValue?.kind === Kind.STRING ? reasonValue.value : undefined; switch (typeDef.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.OBJECT_TYPE_EXTENSION: { if (!fieldDef) { return; } if (argDef) { objectTypeBuilder.field.arg.setDeprecated(typeDef.name.value, fieldDef.name.value, argDef.name.value, reason); } else { objectTypeBuilder.field.setDeprecated(typeDef.name.value, fieldDef.name.value, reason); } break; } case Kind.INTERFACE_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_EXTENSION: { if (!fieldDef) { return; } if (argDef) { interfaceTypeBuilder.field.arg.setDeprecated(typeDef.name.value, fieldDef.name.value, argDef.name.value, reason); } else { interfaceTypeBuilder.field.setDeprecated(typeDef.name.value, fieldDef.name.value, reason); } break; } case Kind.ENUM_TYPE_DEFINITION: case Kind.ENUM_TYPE_EXTENSION: { const valueDef = typeNodeInfo.getValueDef(); if (!valueDef) { return; } enumTypeBuilder.value.setDeprecated(typeDef.name.value, valueDef.name.value, reason); break; } case Kind.INPUT_OBJECT_TYPE_DEFINITION: case Kind.INPUT_OBJECT_TYPE_EXTENSION: { if (!fieldDef) { return; } inputObjectTypeBuilder.field.setDeprecated(typeDef.name.value, fieldDef.name.value, reason); break; } } } }, DirectiveDefinition(node) { const directiveName = node.name.value; if (node.repeatable) { directiveBuilder.setRepeatable(directiveName); } if (node.locations) { for (const location of node.locations) { directiveBuilder.setLocation(directiveName, location.value); } } if (node.arguments?.length) { for (const arg of node.arguments) { directiveBuilder.arg.setType(directiveName, arg.name.value, printOutputType(arg.type)); if (typeof arg.defaultValue !== 'undefined') { directiveBuilder.arg.setDefaultValue(directiveName, arg.name.value, print(arg.defaultValue)); } } } }, StringValue(node, _, parent) { const typeDef = typeNodeInfo.getTypeDef(); const fieldDef = typeNodeInfo.getFieldDef(); const argDef = typeNodeInfo.getArgumentDef(); const description = { value: node.value, block: node.block === true, }; if (parent && 'kind' in parent) { if (parent.kind === Kind.SCALAR_TYPE_DEFINITION || parent.kind === Kind.SCALAR_TYPE_EXTENSION) { scalarTypeBuilder.setDescription(parent.name.value, description); } if (parent.kind === Kind.INPUT_VALUE_DEFINITION && parent.defaultValue?.kind === Kind.STRING && parent.defaultValue === node) { return; } } if (!typeDef) { return; } function matchesParent(node) { return node === parent; } switch (typeDef.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.OBJECT_TYPE_EXTENSION: { if (argDef && matchesParent(argDef)) { objectTypeBuilder.field.arg.setDescription(typeDef.name.value, fieldDef.name.value, argDef.name.value, description); } else if (fieldDef && matchesParent(fieldDef)) { objectTypeBuilder.field.setDescription(typeDef.name.value, fieldDef.name.value, description); } else if (matchesParent(typeDef)) { objectTypeBuilder.setDescription(typeDef.name.value, description); } break; } case Kind.INTERFACE_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_EXTENSION: { if (argDef && matchesParent(argDef)) { interfaceTypeBuilder.field.arg.setDescription(typeDef.name.value, fieldDef.name.value, argDef.name.value, description); } else if (fieldDef && matchesParent(fieldDef)) { interfaceTypeBuilder.field.setDescription(typeDef.name.value, fieldDef.name.value, description); } else if (matchesParent(typeDef)) { interfaceTypeBuilder.setDescription(typeDef.name.value, description); } break; } case Kind.INPUT_OBJECT_TYPE_DEFINITION: case Kind.INPUT_OBJECT_TYPE_EXTENSION: { if (fieldDef && matchesParent(fieldDef)) { inputObjectTypeBuilder.field.setDescription(typeDef.name.value, fieldDef.name.value, description); } else if (matchesParent(typeDef)) { inputObjectTypeBuilder.setDescription(typeDef.name.value, description); } break; } case Kind.ENUM_TYPE_DEFINITION: case Kind.ENUM_TYPE_EXTENSION: { if (parent && 'kind' in parent && parent.kind === Kind.ENUM_VALUE_DEFINITION) { enumTypeBuilder.value.setDescription(typeDef.name.value, parent.name.value, description); } else if (matchesParent(typeDef)) { enumTypeBuilder.setDescription(typeDef.name.value, description); } break; } case Kind.UNION_TYPE_DEFINITION: case Kind.UNION_TYPE_EXTENSION: { if (matchesParent(typeDef)) { unionTypeBuilder.setDescription(typeDef.name.value, description); } break; } } }, }; }, }; } function directiveFactory(state) { return { setComposed(directiveName) { getOrCreateDirective(state, directiveName).composed = true; }, setLocation(directiveName, location) { getOrCreateDirective(state, directiveName).locations.add(location); }, setRepeatable(directiveName) { getOrCreateDirective(state, directiveName).repeatable = true; }, arg: { setTag(directiveName, argName, tag) { getOrCreateDirectiveArg(state, directiveName, argName).tags.add(tag); }, setType(directiveName, argName, argType) { getOrCreateDirectiveArg(state, directiveName, argName).type = argType; }, setDirective(typeName, argName, directive) { getOrCreateDirectiveArg(state, typeName, argName).ast.directives.push(directive); }, setDefaultValue(typeName, argName, defaultValue) { getOrCreateDirectiveArg(state, typeName, argName).defaultValue = defaultValue; }, setInaccessible(typeName, argName) { getOrCreateDirectiveArg(state, typeName, argName).inaccessible = true; }, }, }; } export function cleanSubgraphStateFromFederationSpec(state) { state.types.delete('_FieldSet'); state.types.delete('federation__FieldSet'); return state; } export function cleanSubgraphStateFromLinkSpec(state) { state.types.delete('link__Import'); state.types.delete('link__Purpose'); state.types.delete('link__Import'); state.types.delete('link'); return state; } function scalarTypeFactory(state) { return { setDefinition(typeName) { getOrCreateScalarType(state, typeName); }, setInaccessible(typeName) { getOrCreateScalarType(state, typeName).inaccessible = true; }, setTag(typeName, tag) { getOrCreateScalarType(state, typeName).tags.add(tag); }, setDirective(typeName, directive) { getOrCreateScalarType(state, typeName).ast.directives.push(directive); }, setDescription(typeName, description) { getOrCreateScalarType(state, typeName).description = description; }, setSpecifiedBy(typeName, url) { getOrCreateScalarType(state, typeName).specifiedBy = url; }, }; } function objectTypeFactory(state, renameObject) { return { setDefinition(typeName) { getOrCreateObjectType(state, renameObject, typeName).isDefinition = true; }, setExtension(typeName, extensionType) { const objectType = getOrCreateObjectType(state, renameObject, typeName); objectType.extension = true; if (objectType.extensionType !== '@extends') { objectType.extensionType = extensionType; } }, setDescription(typeName, description) { getOrCreateObjectType(state, renameObject, typeName).description = description; }, setExternal(typeName) { const objectType = getOrCreateObjectType(state, renameObject, typeName); objectType.external = true; for (const field of objectType.fields.values()) { field.external = true; } }, setInterface(typeName, interfaceName) { getOrCreateObjectType(state, renameObject, typeName).interfaces.add(interfaceName); getOrCreateInterfaceType(state, interfaceName).implementedBy.add(typeName); }, setKey(typeName, fields, fieldsUsedInKey, resolvable) { const objectType = getOrCreateObjectType(state, renameObject, typeName); objectType.keys.push({ fields, resolvable }); for (const field of fieldsUsedInKey) { objectType.fieldsUsedAsKeys.add(field); } }, setInaccessible(typeName) { const objectType = getOrCreateObjectType(state, renameObject, typeName); objectType.inaccessible = true; }, setShareable(typeName) { getOrCreateObjectType(state, renameObject, typeName).shareable = true; }, setTag(typeName, tag) { getOrCreateObjectType(state, renameObject, typeName).tags.add(tag); }, setDirective(typeName, directive) { getOrCreateObjectType(state, renameObject, typeName).ast.directives.push(directive); }, field: { setType(typeName, fieldName, fieldType) { getOrCreateObjectField(state, renameObject, typeName, fieldName).type = fieldType; }, setDirective(typeName, fieldName, directive) { getOrCreateObjectField(state, renameObject, typeName, fieldName).ast.directives.push(directive); }, setDescription(typeName, fieldName, description) { getOrCreateObjectField(state, renameObject, typeName, fieldName).description = description; }, setDeprecated(typeName, fieldName, reason) { getOrCreateObjectField(state, renameObject, typeName, fieldName).deprecated = { reason, deprecated: true, }; }, setExternal(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).external = true; }, setInaccessible(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).inaccessible = true; }, setOverride(typeName, fieldName, override) { getOrCreateObjectField(state, renameObject, typeName, fieldName).override = override; }, setProvides(typeName, fieldName, provides) { getOrCreateObjectField(state, renameObject, typeName, fieldName).provides = provides; }, setRequires(typeName, fieldName, requires) { getOrCreateObjectField(state, renameObject, typeName, fieldName).requires = requires; }, markAsProvided(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).provided = true; }, markedAsRequired(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).required = true; }, setShareable(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).shareable = true; }, setTag(typeName, fieldName, tag) { getOrCreateObjectField(state, renameObject, typeName, fieldName).tags.add(tag); }, setUsed(typeName, fieldName) { getOrCreateObjectField(state, renameObject, typeName, fieldName).used = true; }, arg: { setType(typeName, fieldName, argName, argType) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).type = argType; }, setDescription(typeName, fieldName, argName, description) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).description = description; }, setDeprecated(typeName, fieldName, argName, reason) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).deprecated = { reason, deprecated: true, }; }, setDirective(typeName, fieldName, argName, directive) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).ast.directives.push(directive); }, setDefaultValue(typeName, fieldName, argName, defaultValue) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).defaultValue = defaultValue; }, setInaccessible(typeName, fieldName, argName) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).inaccessible = true; }, setTag(typeName, fieldName, argName, tag) { getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName).tags.add(tag); }, }, }, }; } function interfaceTypeFactory(state) { return { setDefinition(typeName) { getOrCreateInterfaceType(state, typeName).isDefinition = true; }, setExtension(typeName) { getOrCreateInterfaceType(state, typeName).extension = true; }, setInterface(typeName, interfaceName) { getOrCreateInterfaceType(state, typeName).interfaces.add(interfaceName); }, setKey(typeName, fields, fieldsUsedInKey, resolvable) { getOrCreateInterfaceType(state, typeName).keys.push({ fields, resolvable }); }, setInaccessible(typeName) { const objectType = getOrCreateInterfaceType(state, typeName); objectType.inaccessible = true; }, setTag(typeName, tag) { getOrCreateInterfaceType(state, typeName).tags.add(tag); }, setDirective(typeName, directive) { getOrCreateInterfaceType(state, typeName).ast.directives.push(directive); }, setDescription(typeName, description) { getOrCreateInterfaceType(state, typeName).description = description; }, field: { setType(typeName, fieldName, fieldType) { getOrCreateInterfaceField(state, typeName, fieldName).type = fieldType; }, setExternal(typeName, fieldName) { getOrCreateInterfaceField(state, typeName, fieldName).external = true; }, setInaccessible(typeName, fieldName) { getOrCreateInterfaceField(state, typeName, fieldName).inaccessible = true; }, setOverride(typeName, fieldName, override) { getOrCreateInterfaceField(state, typeName, fieldName).override = override; }, setRequires(typeName, fieldName, requires) { getOrCreateInterfaceField(state, typeName, fieldName).requires = requires; }, setShareable(typeName, fieldName) { getOrCreateInterfaceField(state, typeName, fieldName).shareable = true; }, setTag(typeName, fieldName, tag) { getOrCreateInterfaceField(state, typeName, fieldName).tags.add(tag); }, setUsed(typeName, fieldName) { getOrCreateInterfaceField(state, typeName, fieldName).used = true; }, setDirective(typeName, fieldName, directive) { getOrCreateInterfaceField(state, typeName, fieldName).ast.directives.push(directive); }, setDescription(typeName, fieldName, description) { getOrCreateInterfaceField(state, typeName, fieldName).description = description; }, setDeprecated(typeName, fieldName, reason) { getOrCreateInterfaceField(state, typeName, fieldName).deprecated = { reason, deprecated: true, }; }, arg: { setType(typeName, fieldName, argName, argType) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).type = argType; }, setDefaultValue(typeName, fieldName, argName, defaultValue) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).defaultValue = defaultValue; }, setDeprecated(typeName, fieldName, argName, reason) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).deprecated = { reason, deprecated: true, }; }, setDescription(typeName, fieldName, argName, description) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).description = description; }, setTag(typeName, fieldName, argName, tag) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).tags.add(tag); }, setInaccessible(typeName, fieldName, argName) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).inaccessible = true; }, setDirective(typeName, fieldName, argName, directive) { getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName).ast.directives.push(directive); }, }, }, }; } function inputObjectTypeFactory(state) { return { setDefinition(typeName) { getOrCreateInputObjectType(state, typeName).isDefinition = true; }, setExtension(typeName) { getOrCreateInputObjectType(state, typeName).extension = true; }, setDescription(typeName, description) { getOrCreateInputObjectType(state, typeName).description = description; }, setInaccessible(typeName) { const objectType = getOrCreateInputObjectType(state, typeName); objectType.inaccessible = true; for (const field of objectType.fields.values()) { field.inaccessible = true; } }, setTag(typeName, tag) { getOrCreateInputObjectType(state, typeName).tags.add(tag); }, field: { setType(typeName, fieldName, fieldType) { getOrCreateInputObjectField(state, typeName, fieldName).type = fieldType; }, setDescription(typeName, fieldName, description) { getOrCreateInputObjectField(state, typeName, fieldName).description = description; }, setDeprecated(typeName, fieldName, reason) { getOrCreateInputObjectField(state, typeName, fieldName).deprecated = { reason, deprecated: true, }; }, setDefaultValue(typeName, fieldName, defaultValue) { getOrCreateInputObjectField(state, typeName, fieldName).defaultValue = defaultValue; }, setInaccessible(typeName, fieldName) { getOrCreateInputObjectField(state, typeName, fieldName).inaccessible = true; }, setTag(typeName, fieldName, tag) { getOrCreateInputObjectField(state, typeName, fieldName).tags.add(tag); }, }, }; } function unionTypeFactory(state) { return { setDefinition(typeName) { getOrCreateUnionType(state, typeName).isDefinition = true; }, setInaccessible(typeName) { getOrCreateUnionType(state, typeName).inaccessible = true; }, setTag(typeName, tag) { getOrCreateUnionType(state, typeName).tags.add(tag); }, setDescription(typeName, description) { getOrCreateUnionType(state, typeName).description = description; }, setMember(typeName, member) { getOrCreateUnionType(state, typeName).members.add(member); }, }; } function enumTypeFactory(state) { return { setDefinition(typeName) { getOrCreateEnumType(state, typeName).isDefinition = true; }, setInaccessible(typeName) { getOrCreateEnumType(state, typeName).inaccessible = true; }, setDescription(typeName, description) { getOrCreateEnumType(state, typeName).description = description; }, setTag(typeName, tag) { getOrCreateEnumType(state, typeName).tags.add(tag); }, setReferencedByInputType(typeName, schemaCoordinate) { getOrCreateEnumType(state, typeName).referencedByInputType = true; getOrCreateEnumType(state, typeName).inputTypeReferences.add(schemaCoordinate); }, setReferencedByOutputType(typeName, schemaCoordinate) { getOrCreateEnumType(state, typeName).referencedByOutputType = true; getOrCreateEnumType(state, typeName).outputTypeReferences.add(schemaCoordinate); }, value: { setValue(typeName, valueName) { getOrCreateEnumValue(state, typeName, valueName); }, setDescription(typeName, valueName, description) { getOrCreateEnumValue(state, typeName, valueName).description = description; }, setInaccessible(typeName, valueName) { getOrCreateEnumValue(state, typeName, valueName).inaccessible = true; }, setTag(typeName, valueName, tag) { getOrCreateEnumValue(state, typeName, valueName).tags.add(tag); }, setDeprecated(typeName, valueName, reason) { getOrCreateEnumValue(state, typeName, valueName).deprecated = { reason, deprecated: true, }; }, }, }; } function getOrCreateDirective(state, directiveName) { const existing = state.types.get(directiveName); if (existing) { if (existing.kind !== TypeKind.DIRECTIVE) { throw new Error(`Expected ${directiveName} to be a directive`); } return existing; } const directive = { kind: TypeKind.DIRECTIVE, name: directiveName, locations: new Set(), repeatable: false, composed: false, args: new Map(), }; state.types.set(directiveName, directive); return directive; } function getOrCreateDirectiveArg(state, directiveName, argName) { const directive = getOrCreateDirective(state, directiveName); const existing = directive.args.get(argName); if (existing) { return existing; } const arg = { name: argName, type: MISSING, inaccessible: false, tags: new Set(), ast: { directives: [], }, }; directive.args.set(argName, arg); return arg; } function getOrCreateScalarType(state, typeName) { const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.SCALAR) { throw new Error(`Expected ${typeName} to be a scalar type`); } return existing; } const scalarType = { kind: TypeKind.SCALAR, name: typeName, tags: new Set(), inaccessible: false, ast: { directives: [], }, }; state.types.set(typeName, scalarType); return scalarType; } function getOrCreateObjectType(state, renameObject, typeName) { typeName = renameObject(typeName); const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.OBJECT) { throw new Error(`Expected ${typeName} to be an object type`); } return existing; } const objectType = { kind: TypeKind.OBJECT, name: typeName, fields: new Map(), fieldsUsedAsKeys: new Set(), extension: false, external: false, keys: [], inaccessible: false, shareable: false, tags: new Set(), interfaces: new Set(), isDefinition: false, ast: { directives: [], }, }; state.types.set(typeName, objectType); return objectType; } function getOrCreateInterfaceType(state, typeName) { const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.INTERFACE) { throw new Error(`Expected ${typeName} to be an interface type`); } return existing; } const interfaceType = { kind: TypeKind.INTERFACE, name: typeName, fields: new Map(), extension: false, keys: [], inaccessible: false, tags: new Set(), interfaces: new Set(), implementedBy: new Set(), isDefinition: false, ast: { directives: [], }, }; state.types.set(typeName, interfaceType); return interfaceType; } function getOrCreateInputObjectType(state, typeName) { const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.INPUT_OBJECT) { throw new Error(`Expected ${typeName} to be an input object type`); } return existing; } const inputObjectType = { kind: TypeKind.INPUT_OBJECT, name: typeName, fields: new Map(), extension: false, inaccessible: false, tags: new Set(), isDefinition: false, }; state.types.set(typeName, inputObjectType); return inputObjectType; } function getOrCreateEnumType(state, typeName) { const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.ENUM) { throw new Error(`Expected ${typeName} to be an enum type`); } return existing; } const enumType = { kind: TypeKind.ENUM, name: typeName, values: new Map(), inaccessible: false, tags: new Set(), isDefinition: false, referencedByInputType: false, referencedByOutputType: false, inputTypeReferences: new Set(), outputTypeReferences: new Set(), }; state.types.set(typeName, enumType); return enumType; } function getOrCreateUnionType(state, typeName) { const existing = state.types.get(typeName); if (existing) { if (existing.kind !== TypeKind.UNION) { throw new Error(`Expected ${typeName} to be a union type`); } return existing; } const unionType = { kind: TypeKind.UNION, name: typeName, members: new Set(), inaccessible: false, tags: new Set(), isDefinition: false, }; state.types.set(typeName, unionType); return unionType; } function getOrCreateObjectField(state, renameObject, typeName, fieldName) { const objectType = getOrCreateObjectType(state, renameObject, typeName); const existing = objectType.fields.get(fieldName); if (existing) { return existing; } const field = { name: fieldName, type: MISSING, external: false, inaccessible: false, used: false, required: false, provided: false, override: null, provides: null, requires: null, shareable: state.version === 'v1.0' ? true : false, tags: new Set(), args: new Map(), ast: { directives: [], }, }; objectType.fields.set(fieldName, field); return field; } function getOrCreateInterfaceField(state, typeName, fieldName) { const interfaceType = getOrCreateInterfaceType(state, typeName); const existing = interfaceType.fields.get(fieldName); if (existing) { return existing; } const field = { name: fieldName, type: MISSING, external: false, inaccessible: false, used: false, override: null, provides: null, requires: null, required: false, provided: false, shareable: false, tags: new Set(), args: new Map(), ast: { directives: [], }, }; interfaceType.fields.set(fieldName, field); return field; } function getOrCreateInputObjectField(state, typeName, fieldName) { const inputObjectType = getOrCreateInputObjectType(state, typeName); const existing = inputObjectType.fields.get(fieldName); if (existing) { return existing; } const field = { name: fieldName, type: MISSING, inaccessible: false, tags: new Set(), }; inputObjectType.fields.set(fieldName, field); return field; } function getOrCreateEnumValue(state, typeName, enumValueName) { const enumType = getOrCreateEnumType(state, typeName); const existing = enumType.values.get(enumValueName); if (existing) { return existing; } const enumValue = { name: enumValueName, inaccessible: false, tags: new Set(), }; enumType.values.set(enumValueName, enumValue); return enumValue; } function getOrCreateObjectFieldArgument(state, renameObject, typeName, fieldName, argName) { const fieldState = getOrCreateObjectField(state, renameObject, typeName, fieldName); const existing = fieldState.args.get(argName); if (existing) { return existing; } const arg = { name: argName, type: MISSING, inaccessible: false, tags: new Set(), ast: { directives: [], }, }; fieldState.args.set(argName, arg); return arg; } function getOrCreateInterfaceFieldArgument(state, typeName, fieldName, argName) { const fieldState = getOrCreateInterfaceField(state, typeName, fieldName); const existing = fieldState.args.get(argName); if (existing) { return existing; } const arg = { name: argName, type: MISSING, inaccessible: false, tags: new Set(), ast: { directives: [], }, }; fieldState.args.set(argName, arg); return arg;