UNPKG

@apollo/federation-internals

Version:
1,152 lines 130 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CoreFeature = exports.defaultSchemaBlueprint = exports.SchemaBlueprint = exports.NamedSchemaElementWithType = exports.NamedSchemaElement = exports.SchemaElement = exports.Extension = exports.sourceASTs = exports.DirectiveTargetElement = exports.isLeafType = exports.typeFromAST = exports.typeToAST = exports.isTypeSystemDirectiveLocation = exports.typeSystemDirectiveLocations = exports.isExecutableDirectiveLocation = exports.executableDirectiveLocations = exports.isConditionalDirective = exports.supertypes = exports.runtimeTypesIntersects = exports.possibleRuntimeTypes = exports.isCompositeType = exports.isAbstractType = exports.isNullableType = exports.baseType = exports.filterTypesOfKind = exports.isTypeOfKind = exports.isInputType = exports.isOutputType = exports.isInputObjectType = exports.isUnionType = exports.isEnumType = exports.isInterfaceType = exports.isObjectType = exports.isIDType = exports.isBooleanType = exports.isFloatType = exports.isStringType = exports.isIntType = exports.isCustomScalarType = exports.isScalarType = exports.isNonNullType = exports.isListType = exports.isWrapperType = exports.isNamedType = exports.isSchemaRootType = exports.defaultRootName = exports.allSchemaRootKinds = exports.typenameFieldName = exports.ErrGraphQLAPISchemaValidationFailed = exports.ErrGraphQLValidationFailed = void 0; exports.isElementNamedType = exports.isFieldDefinition = exports.copyDirectiveDefinitionToSchema = exports.newNamedType = exports.variableDefinitionFromAST = exports.variableDefinitionsFromAST = exports.VariableDefinitions = exports.VariableDefinition = exports.isVariable = exports.VariableCollector = exports.Variable = exports.directiveApplicationsSubstraction = exports.isDirectiveApplicationsSubset = exports.sameDirectiveApplications = exports.sameDirectiveApplication = exports.directivesToDirectiveNodes = exports.directivesToString = exports.Directive = exports.DirectiveDefinition = exports.EnumValue = exports.ArgumentDefinition = exports.InputFieldDefinition = exports.FieldDefinition = exports.NonNullType = exports.ListType = exports.InputObjectType = exports.EnumType = exports.UnionType = exports.UnionMember = exports.InterfaceType = exports.ObjectType = exports.InterfaceImplementation = exports.ScalarType = exports.SchemaDefinition = exports.RootType = exports.Schema = exports.CoreFeatures = void 0; const graphql_1 = require("graphql"); const coreSpec_1 = require("./specs/coreSpec"); const utils_1 = require("./utils"); const values_1 = require("./values"); const tagSpec_1 = require("./specs/tagSpec"); const inaccessibleSpec_1 = require("./specs/inaccessibleSpec"); const print_1 = require("./print"); const types_1 = require("./types"); const introspection_1 = require("./introspection"); const validate_1 = require("graphql/validation/validate"); const specifiedRules_1 = require("graphql/validation/specifiedRules"); const validate_2 = require("./validate"); const directiveAndTypeSpecification_1 = require("./directiveAndTypeSpecification"); const suggestions_1 = require("./suggestions"); const error_1 = require("./error"); const knownCoreFeatures_1 = require("./knownCoreFeatures"); const validationErrorCode = 'GraphQLValidationFailed'; const DEFAULT_VALIDATION_ERROR_MESSAGE = 'The schema is not a valid GraphQL schema.'; const EMPTY_SET = new Set(); const ErrGraphQLValidationFailed = (causes, message = DEFAULT_VALIDATION_ERROR_MESSAGE) => (0, error_1.aggregateError)(validationErrorCode, message, causes); exports.ErrGraphQLValidationFailed = ErrGraphQLValidationFailed; const apiSchemaValidationErrorCode = 'GraphQLAPISchemaValidationFailed'; const ErrGraphQLAPISchemaValidationFailed = (causes) => (0, error_1.aggregateError)(apiSchemaValidationErrorCode, 'The supergraph schema failed to produce a valid API schema', causes); exports.ErrGraphQLAPISchemaValidationFailed = ErrGraphQLAPISchemaValidationFailed; exports.typenameFieldName = '__typename'; exports.allSchemaRootKinds = ['query', 'mutation', 'subscription']; function defaultRootName(rootKind) { return rootKind.charAt(0).toUpperCase() + rootKind.slice(1); } exports.defaultRootName = defaultRootName; function checkDefaultSchemaRoot(type) { if (type.kind !== 'ObjectType') { return undefined; } switch (type.name) { case 'Query': return 'query'; case 'Mutation': return 'mutation'; case 'Subscription': return 'subscription'; default: return undefined; } } function isSchemaRootType(type) { return isObjectType(type) && type.isRootType(); } exports.isSchemaRootType = isSchemaRootType; function isNamedType(type) { return type instanceof BaseNamedType; } exports.isNamedType = isNamedType; function isWrapperType(type) { return isListType(type) || isNonNullType(type); } exports.isWrapperType = isWrapperType; function isListType(type) { return type.kind == 'ListType'; } exports.isListType = isListType; function isNonNullType(type) { return type.kind == 'NonNullType'; } exports.isNonNullType = isNonNullType; function isScalarType(type) { return type.kind == 'ScalarType'; } exports.isScalarType = isScalarType; function isCustomScalarType(type) { return isScalarType(type) && !graphQLBuiltInTypes.includes(type.name); } exports.isCustomScalarType = isCustomScalarType; function isIntType(type) { return type === type.schema().intType(); } exports.isIntType = isIntType; function isStringType(type) { return type === type.schema().stringType(); } exports.isStringType = isStringType; function isFloatType(type) { return type === type.schema().floatType(); } exports.isFloatType = isFloatType; function isBooleanType(type) { return type === type.schema().booleanType(); } exports.isBooleanType = isBooleanType; function isIDType(type) { return type === type.schema().idType(); } exports.isIDType = isIDType; function isObjectType(type) { return type.kind == 'ObjectType'; } exports.isObjectType = isObjectType; function isInterfaceType(type) { return type.kind == 'InterfaceType'; } exports.isInterfaceType = isInterfaceType; function isEnumType(type) { return type.kind == 'EnumType'; } exports.isEnumType = isEnumType; function isUnionType(type) { return type.kind == 'UnionType'; } exports.isUnionType = isUnionType; function isInputObjectType(type) { return type.kind == 'InputObjectType'; } exports.isInputObjectType = isInputObjectType; function isOutputType(type) { switch (baseType(type).kind) { case 'ScalarType': case 'ObjectType': case 'UnionType': case 'EnumType': case 'InterfaceType': return true; default: return false; } } exports.isOutputType = isOutputType; function isInputType(type) { switch (baseType(type).kind) { case 'ScalarType': case 'EnumType': case 'InputObjectType': return true; default: return false; } } exports.isInputType = isInputType; function isTypeOfKind(type, kind) { return type.kind === kind; } exports.isTypeOfKind = isTypeOfKind; function filterTypesOfKind(types, kind) { return types.reduce((acc, type) => { if (isTypeOfKind(type, kind)) { acc.push(type); } return acc; }, []); } exports.filterTypesOfKind = filterTypesOfKind; function baseType(type) { return isWrapperType(type) ? type.baseType() : type; } exports.baseType = baseType; function isNullableType(type) { return !isNonNullType(type); } exports.isNullableType = isNullableType; function isAbstractType(type) { return isInterfaceType(type) || isUnionType(type); } exports.isAbstractType = isAbstractType; function isCompositeType(type) { return isObjectType(type) || isInterfaceType(type) || isUnionType(type); } exports.isCompositeType = isCompositeType; function possibleRuntimeTypes(type) { switch (type.kind) { case 'InterfaceType': return type.possibleRuntimeTypes(); case 'UnionType': return type.types(); case 'ObjectType': return [type]; } } exports.possibleRuntimeTypes = possibleRuntimeTypes; function runtimeTypesIntersects(t1, t2) { if (t1 === t2) { return true; } const rt1 = possibleRuntimeTypes(t1); const rt2 = possibleRuntimeTypes(t2); for (const obj1 of rt1) { if (rt2.some(obj2 => obj1.name === obj2.name)) { return true; } } return false; } exports.runtimeTypesIntersects = runtimeTypesIntersects; function supertypes(type) { switch (type.kind) { case 'InterfaceType': return type.interfaces(); case 'UnionType': return []; case 'ObjectType': return type.interfaces().concat(type.unionsWhereMember()); } } exports.supertypes = supertypes; function isConditionalDirective(directive) { return ['include', 'skip'].includes(directive.name); } exports.isConditionalDirective = isConditionalDirective; exports.executableDirectiveLocations = [ graphql_1.DirectiveLocation.QUERY, graphql_1.DirectiveLocation.MUTATION, graphql_1.DirectiveLocation.SUBSCRIPTION, graphql_1.DirectiveLocation.FIELD, graphql_1.DirectiveLocation.FRAGMENT_DEFINITION, graphql_1.DirectiveLocation.FRAGMENT_SPREAD, graphql_1.DirectiveLocation.INLINE_FRAGMENT, graphql_1.DirectiveLocation.VARIABLE_DEFINITION, ]; const executableDirectiveLocationsSet = new Set(exports.executableDirectiveLocations); function isExecutableDirectiveLocation(loc) { return executableDirectiveLocationsSet.has(loc); } exports.isExecutableDirectiveLocation = isExecutableDirectiveLocation; exports.typeSystemDirectiveLocations = [ graphql_1.DirectiveLocation.SCHEMA, graphql_1.DirectiveLocation.SCALAR, graphql_1.DirectiveLocation.OBJECT, graphql_1.DirectiveLocation.FIELD_DEFINITION, graphql_1.DirectiveLocation.ARGUMENT_DEFINITION, graphql_1.DirectiveLocation.INTERFACE, graphql_1.DirectiveLocation.UNION, graphql_1.DirectiveLocation.ENUM, graphql_1.DirectiveLocation.ENUM_VALUE, graphql_1.DirectiveLocation.INPUT_OBJECT, graphql_1.DirectiveLocation.INPUT_FIELD_DEFINITION, ]; const typeSystemDirectiveLocationsSet = new Set(exports.typeSystemDirectiveLocations); function isTypeSystemDirectiveLocation(loc) { return typeSystemDirectiveLocationsSet.has(loc); } exports.isTypeSystemDirectiveLocation = isTypeSystemDirectiveLocation; function typeToAST(type) { switch (type.kind) { case 'ListType': return { kind: graphql_1.Kind.LIST_TYPE, type: typeToAST(type.ofType) }; case 'NonNullType': return { kind: graphql_1.Kind.NON_NULL_TYPE, type: typeToAST(type.ofType) }; default: return { kind: graphql_1.Kind.NAMED_TYPE, name: { kind: graphql_1.Kind.NAME, value: type.name } }; } } exports.typeToAST = typeToAST; function typeFromAST(schema, node) { switch (node.kind) { case graphql_1.Kind.LIST_TYPE: return new ListType(typeFromAST(schema, node.type)); case graphql_1.Kind.NON_NULL_TYPE: return new NonNullType(typeFromAST(schema, node.type)); default: const type = schema.type(node.name.value); if (!type) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Unknown type "${node.name.value}"`, { nodes: node }); } return type; } } exports.typeFromAST = typeFromAST; function isLeafType(type) { return isScalarType(type) || isEnumType(type); } exports.isLeafType = isLeafType; class DirectiveTargetElement { constructor(_schema, directives = []) { this._schema = _schema; this.appliedDirectives = directives.map((d) => this.attachDirective(d)); } schema() { return this._schema; } attachDirective(directive) { const toAdd = directive.isAttached() ? new Directive(directive.name, directive.arguments()) : directive; Element.prototype['setParent'].call(toAdd, this); return toAdd; } appliedDirectivesOf(nameOrDefinition) { const directiveName = typeof nameOrDefinition === 'string' ? nameOrDefinition : nameOrDefinition.name; return this.appliedDirectives.filter(d => d.name == directiveName); } hasAppliedDirective(nameOrDefinition) { const directiveName = typeof nameOrDefinition === 'string' ? nameOrDefinition : nameOrDefinition.name; return this.appliedDirectives.some(d => d.name == directiveName); } appliedDirectivesToDirectiveNodes() { return directivesToDirectiveNodes(this.appliedDirectives); } appliedDirectivesToString() { return directivesToString(this.appliedDirectives); } collectVariablesInAppliedDirectives(collector) { for (const applied of this.appliedDirectives) { collector.collectInArguments(applied.arguments()); } } } exports.DirectiveTargetElement = DirectiveTargetElement; function sourceASTs(...elts) { return elts.map(elt => elt === null || elt === void 0 ? void 0 : elt.sourceAST).filter((elt) => elt !== undefined); } exports.sourceASTs = sourceASTs; class Element { schema() { const schema = this.schemaInternal(); (0, utils_1.assert)(schema, 'requested schema does not exist. Probably because the element is unattached'); return schema; } schemaInternal() { if (!this._parent) { return undefined; } else if (this._parent instanceof Schema) { return this._parent; } else if (this._parent instanceof SchemaElement) { return this._parent.schemaInternal(); } else if (this._parent instanceof DirectiveTargetElement) { return this._parent.schema(); } (0, utils_1.assert)(false, 'unreachable code. parent is of unknown type'); } get parent() { (0, utils_1.assert)(this._parent, 'trying to access non-existent parent'); return this._parent; } isAttached() { return !!this._parent; } setParent(parent) { (0, utils_1.assert)(!this._parent, "Cannot set parent of an already attached element"); this._parent = parent; this.onAttached(); } onAttached() { } checkUpdate() { (0, utils_1.assert)(this.isAttached(), () => `Cannot modify detached element ${this}`); } } class Extension { get extendedElement() { return this._extendedElement; } setExtendedElement(element) { (0, utils_1.assert)(!this._extendedElement, "Cannot attached already attached extension"); this._extendedElement = element; } } exports.Extension = Extension; class SchemaElement extends Element { addUnappliedDirective({ nameOrDef, args, extension, directive }) { const toAdd = { nameOrDef, args: args !== null && args !== void 0 ? args : {}, extension, directive, }; if (this._unappliedDirectives) { this._unappliedDirectives.push(toAdd); } else { this._unappliedDirectives = [toAdd]; } } processUnappliedDirectives() { var _a; for (const { nameOrDef, args, extension, directive } of (_a = this._unappliedDirectives) !== null && _a !== void 0 ? _a : []) { const d = this.applyDirective(nameOrDef, args); d.setOfExtension(extension); d.sourceAST = directive; } this._unappliedDirectives = undefined; } get appliedDirectives() { var _a; return (_a = this._appliedDirectives) !== null && _a !== void 0 ? _a : []; } appliedDirectivesOf(nameOrDefinition) { const directiveName = typeof nameOrDefinition === 'string' ? nameOrDefinition : nameOrDefinition.name; return this.appliedDirectives.filter(d => d.name == directiveName); } hasAppliedDirective(nameOrDefinition) { return (typeof nameOrDefinition === 'string' ? this.appliedDirectivesOf(nameOrDefinition) : this.appliedDirectivesOf(nameOrDefinition)).length !== 0; } applyDirective(nameOrDef, args, asFirstDirective = false) { var _a; let toAdd; if (typeof nameOrDef === 'string') { this.checkUpdate(); toAdd = new Directive(nameOrDef, args !== null && args !== void 0 ? args : Object.create(null)); const def = (_a = this.schema().directive(nameOrDef)) !== null && _a !== void 0 ? _a : this.schema().blueprint.onMissingDirectiveDefinition(this.schema(), toAdd); if (!def) { throw this.schema().blueprint.onGraphQLJSValidationError(this.schema(), error_1.ERRORS.INVALID_GRAPHQL.err(`Unknown directive "@${nameOrDef}".`)); } if (Array.isArray(def)) { throw (0, exports.ErrGraphQLValidationFailed)(def); } } else { this.checkUpdate(nameOrDef); toAdd = new Directive(nameOrDef.name, args !== null && args !== void 0 ? args : Object.create(null)); } Element.prototype['setParent'].call(toAdd, this); if (this._appliedDirectives) { if (asFirstDirective) { this._appliedDirectives.unshift(toAdd); } else { this._appliedDirectives.push(toAdd); } } else { this._appliedDirectives = [toAdd]; } DirectiveDefinition.prototype['addReferencer'].call(toAdd.definition, toAdd); this.onModification(); return toAdd; } removeAppliedDirectives() { if (!this._appliedDirectives) { return; } const applied = this._appliedDirectives.concat(); applied.forEach(d => d.remove()); } onModification() { const schema = this.schemaInternal(); if (schema) { Schema.prototype['onModification'].call(schema); } } isElementBuiltIn() { return false; } removeTypeReferenceInternal(type) { this.removeTypeReference(type); } checkRemoval() { (0, utils_1.assert)(!this.isElementBuiltIn() || Schema.prototype['canModifyBuiltIn'].call(this.schema()), () => `Cannot modify built-in ${this}`); } checkUpdate(addedElement) { super.checkUpdate(); if (!Schema.prototype['canModifyBuiltIn'].call(this.schema())) { let thisElement = this; while (thisElement && thisElement instanceof SchemaElement) { (0, utils_1.assert)(!thisElement.isElementBuiltIn(), () => `Cannot modify built-in (or part of built-in) ${this}`); thisElement = thisElement.parent; } } if (addedElement && addedElement.isAttached()) { const thatSchema = addedElement.schema(); (0, utils_1.assert)(!thatSchema || thatSchema === this.schema(), () => `Cannot add element ${addedElement} to ${this} as it is attached to another schema`); } } } exports.SchemaElement = SchemaElement; class NamedSchemaElement extends SchemaElement { constructor(name) { super(); this._name = name; } get name() { return this._name; } } exports.NamedSchemaElement = NamedSchemaElement; class BaseNamedType extends NamedSchemaElement { constructor(name, isBuiltIn = false) { super(name); this.isBuiltIn = isBuiltIn; this.preserveEmptyDefinition = false; } addReferencer(referencer) { var _a; (_a = this._referencers) !== null && _a !== void 0 ? _a : (this._referencers = new Set()); this._referencers.add(referencer); } removeReferencer(referencer) { var _a; (_a = this._referencers) === null || _a === void 0 ? void 0 : _a.delete(referencer); } get coordinate() { return this.name; } *allChildElements() { } extensions() { var _a; return (_a = this._extensions) !== null && _a !== void 0 ? _a : []; } hasExtension(extension) { var _a, _b; return (_b = (_a = this._extensions) === null || _a === void 0 ? void 0 : _a.includes(extension)) !== null && _b !== void 0 ? _b : false; } newExtension() { return this.addExtension(new Extension()); } addExtension(extension) { this.checkUpdate(); if (this.hasExtension(extension)) { return extension; } (0, utils_1.assert)(!extension.extendedElement, () => `Cannot add extension to type ${this}: it is already added to another type`); if (this._extensions) { this._extensions.push(extension); } else { this._extensions = [extension]; } Extension.prototype['setExtendedElement'].call(extension, this); this.onModification(); return extension; } removeExtensions() { if (!this._extensions) { return; } this._extensions = undefined; for (const directive of this.appliedDirectives) { directive.removeOfExtension(); } this.removeInnerElementsExtensions(); } isIntrospectionType() { return (0, introspection_1.isIntrospectionName)(this.name); } hasExtensionElements() { return !!this._extensions; } hasNonExtensionElements() { return this.preserveEmptyDefinition || this.appliedDirectives.some(d => d.ofExtension() === undefined) || this.hasNonExtensionInnerElements(); } isElementBuiltIn() { return this.isBuiltIn; } rename(newName) { this.checkUpdate(); const oldName = this._name; this._name = newName; Schema.prototype['renameTypeInternal'].call(this._parent, oldName, newName); this.onModification(); } remove() { var _a; if (!this._parent) { return []; } this.checkRemoval(); this.onModification(); this.sourceAST = undefined; this.removeAppliedDirectives(); this.removeInnerElements(); const toReturn = []; (_a = this._referencers) === null || _a === void 0 ? void 0 : _a.forEach(r => { SchemaElement.prototype['removeTypeReferenceInternal'].call(r, this); toReturn.push(r); }); this._referencers = undefined; Schema.prototype['removeTypeInternal'].call(this._parent, this); this._parent = undefined; return toReturn; } removeRecursive() { this.remove().forEach(ref => this.removeReferenceRecursive(ref)); } referencers() { var _a; return (_a = this._referencers) !== null && _a !== void 0 ? _a : EMPTY_SET; } isReferenced() { return !!this._referencers; } toString() { return this.name; } } class NamedSchemaElementWithType extends NamedSchemaElement { get type() { return this._type; } set type(type) { if (type) { this.checkUpdate(type); } else { this.checkRemoval(); } if (this._type) { removeReferenceToType(this, this._type); } this._type = type; if (type) { addReferenceToType(this, type); } } removeTypeReference(type) { (0, utils_1.assert)(this._type && baseType(this._type) === type, () => `Cannot remove reference to type ${type} on ${this} as its type is ${this._type}`); this._type = undefined; } } exports.NamedSchemaElementWithType = NamedSchemaElementWithType; class BaseExtensionMember extends Element { ofExtension() { return this._extension; } removeOfExtension() { this._extension = undefined; } setOfExtension(extension) { var _a; this.checkUpdate(); (0, utils_1.assert)(!extension || ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.hasExtension(extension)), () => `Cannot set object as part of the provided extension: it is not an extension of parent ${this.parent}`); this._extension = extension; } remove() { this.removeInner(); Schema.prototype['onModification'].call(this.schema()); this._extension = undefined; this._parent = undefined; } } class SchemaBlueprint { onMissingDirectiveDefinition(_schema, _directive) { return undefined; } onDirectiveDefinitionAndSchemaParsed(_) { return []; } ignoreParsedField(_type, _fieldName) { return false; } onConstructed(_) { } onAddedCoreFeature(_schema, _feature) { } onInvalidation(_) { } onValidation(_schema) { return []; } validationRules() { return specifiedRules_1.specifiedSDLRules; } onGraphQLJSValidationError(schema, error) { var _a; const matcher = /^Unknown directive "@(?<directive>[_A-Za-z][_0-9A-Za-z]*)"\.$/.exec(error.message); const name = (_a = matcher === null || matcher === void 0 ? void 0 : matcher.groups) === null || _a === void 0 ? void 0 : _a.directive; if (!name) { return error; } const allDefinedDirectiveNames = schema.allDirectives().map((d) => d.name); const suggestions = (0, suggestions_1.suggestionList)(name, allDefinedDirectiveNames); if (suggestions.length === 0) { return this.onUnknownDirectiveValidationError(schema, name, error); } else { return (0, error_1.withModifiedErrorMessage)(error, `${error.message}${(0, suggestions_1.didYouMean)(suggestions.map((s) => '@' + s))}`); } } onUnknownDirectiveValidationError(_schema, _unknownDirectiveName, error) { return error; } applyDirectivesAfterParsing() { return false; } } exports.SchemaBlueprint = SchemaBlueprint; exports.defaultSchemaBlueprint = new SchemaBlueprint(); class CoreFeature { constructor(url, nameInSchema, directive, imports, purpose) { this.url = url; this.nameInSchema = nameInSchema; this.directive = directive; this.imports = imports; this.purpose = purpose; } isFeatureDefinition(element) { const importName = element.kind === 'DirectiveDefinition' ? '@' + element.name : element.name; return element.name.startsWith(this.nameInSchema + '__') || (element.kind === 'DirectiveDefinition' && element.name === this.nameInSchema) || !!this.imports.find((i) => { var _a; return importName === ((_a = i.as) !== null && _a !== void 0 ? _a : i.name); }); } directiveNameInSchema(name) { return CoreFeature.directiveNameInSchemaForCoreArguments(this.url, this.nameInSchema, this.imports, name); } static directiveNameInSchemaForCoreArguments(specUrl, specNameInSchema, imports, directiveNameInSpec) { var _a, _b; const elementImport = imports.find((i) => i.name.charAt(0) === '@' && i.name.slice(1) === directiveNameInSpec); return elementImport ? ((_b = (_a = elementImport.as) === null || _a === void 0 ? void 0 : _a.slice(1)) !== null && _b !== void 0 ? _b : directiveNameInSpec) : (directiveNameInSpec === specUrl.name ? specNameInSchema : specNameInSchema + '__' + directiveNameInSpec); } typeNameInSchema(name) { var _a; const elementImport = this.imports.find((i) => i.name === name); return elementImport ? ((_a = elementImport.as) !== null && _a !== void 0 ? _a : name) : this.nameInSchema + '__' + name; } minimumFederationVersion() { var _a; return (_a = (0, knownCoreFeatures_1.coreFeatureDefinitionIfKnown)(this.url)) === null || _a === void 0 ? void 0 : _a.minimumFederationVersion; } } exports.CoreFeature = CoreFeature; class CoreFeatures { constructor(coreItself) { this.coreItself = coreItself; this.byAlias = new Map(); this.byIdentity = new Map(); this.byImportName = new Map(); this.conflictsByAlias = new utils_1.SetMultiMap(); this.add(coreItself); const coreDef = (0, coreSpec_1.findCoreSpecVersion)(coreItself.url); if (!coreDef) { throw error_1.ERRORS.UNKNOWN_LINK_VERSION.err(`Schema uses unknown version ${coreItself.url.version} of the ${coreItself.url.name} spec`); } this.coreDefinition = coreDef; } getByIdentity(identity) { var _a; return (_a = this.byIdentity.get(identity)) === null || _a === void 0 ? void 0 : _a[0]; } allFeatures() { return [...this.byIdentity.values()].map(([feature]) => feature); } removeFeature(featureIdentity) { const entry = this.byIdentity.get(featureIdentity); if (entry) { const [feature] = entry; this.byIdentity.delete(featureIdentity); const alias = feature.nameInSchema; this.byAlias.delete(alias); for (const { name: importInSpec, as } of feature.imports) { const importInSchema = as !== null && as !== void 0 ? as : importInSpec; const isDirective = importInSpec.charAt(0) === "@"; const nameInSchema = isDirective ? importInSchema.slice(1) : importInSchema; this.byImportName.delete(importInSchema); const split = CoreFeatures.splitPrefixedName(nameInSchema); if (!split) { continue; } const [splitAlias] = split; if (splitAlias === alias) { continue; } let conflicts = this.conflictsByAlias.get(importInSchema); if (!conflicts) { continue; } conflicts.delete(importInSchema); if (conflicts.size) { continue; } this.conflictsByAlias.delete(importInSchema); } } } maybeAddFeature(directive) { var _a, _b; if (((_a = directive.definition) === null || _a === void 0 ? void 0 : _a.name) !== this.coreItself.nameInSchema) { return undefined; } const typedDirective = directive; const args = typedDirective.arguments(); const url = this.coreDefinition.extractFeatureUrl(args); const imports = (0, coreSpec_1.extractCoreFeatureImports)(url, typedDirective); const feature = new CoreFeature(url, (_b = args.as) !== null && _b !== void 0 ? _b : url.name, directive, imports, args.for); this.add(feature); directive.schema().blueprint.onAddedCoreFeature(directive.schema(), feature); return feature; } add(feature) { var _a, _b; const identity = feature.url.identity; if (this.byIdentity.has(identity)) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot link feature "${identity}" since it has already been linked in the schema.`); } const alias = feature.nameInSchema; if (!(identity === tagSpec_1.tagIdentity && alias === 'federation__tag' && feature.imports.length === 0) && !(identity === inaccessibleSpec_1.inaccessibleIdentity && alias === 'federation__inaccessible' && feature.imports.length === 0)) { if (alias.indexOf('__') !== -1) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot link feature "${identity}" as "${alias}" since it contains "__". Please rename to a compliant name via "as".`); } } if (alias.charAt(alias.length - 1) === '_') { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot link feature "${identity}" as "${alias}" since it ends in "_". Please rename to a compliant name via "as".`); } if (!aliasRegexp.test(alias)) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot link feature "${identity}" as "${alias}" since it is not a valid GraphQL name. Please rename to a compliant name via "as".`); } const conflicts = this.conflictsByAlias.get(alias); if (conflicts) { const importInSchema = (_b = (_a = conflicts === null || conflicts === void 0 ? void 0 : conflicts.values()) === null || _a === void 0 ? void 0 : _a.next()) === null || _b === void 0 ? void 0 : _b.value; (0, utils_1.assert)(importInSchema !== undefined, `Unexpectedly empty conflicts set`); const entry = this.byImportName.get(importInSchema); (0, utils_1.assert)(entry, `Unexpectedly cannot find feature for import`); const [conflictFeature, importInSpec] = entry; const conflictIdentity = conflictFeature.url.identity; this.checkTagInaccessibleConflict(conflictIdentity, identity); const importInErrorMessage = importInSchema !== importInSpec ? `"${importInSpec}" as "${importInSchema}"` : `"${importInSpec}"`; throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${conflictIdentity}" since it can be confused with a namespaced name from another linked feature "${identity}". Please rename the import or feature to avoid conflicts via "as".`); } const importInSchema = "@" + alias; const entry = this.byImportName.get(importInSchema); if (entry) { const [conflictFeature, importInSpec] = entry; const conflictIdentity = conflictFeature.url.identity; this.checkTagInaccessibleConflict(conflictIdentity, identity); const importInErrorMessage = importInSchema !== importInSpec ? `"${importInSpec}" as "${importInSchema}"` : `"${importInSpec}"`; throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${conflictIdentity}" since it can be confused with a namespaced name from another linked feature "${identity}". Please rename the import or feature to avoid conflicts via "as".`); } const existingFeature = this.byAlias.get(alias); if (existingFeature !== undefined) { const existingIdentity = existingFeature.url.identity; this.checkTagInaccessibleConflict(existingIdentity, identity); throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot link feature ${identity} as "${alias}" since another feature "${existingIdentity}" already uses that alias. Please rename the feature to avoid conflicts via "as".`); } const importsMap = new Map(); for (const { name: importInSpec, as } of feature.imports) { const importInSchema = as !== null && as !== void 0 ? as : importInSpec; const importInErrorMessage = importInSchema !== importInSpec ? `"${importInSpec}" as "${importInSchema}"` : `"${importInSpec}"`; const isDirective = importInSpec.charAt(0) === "@"; const nameInSpec = isDirective ? importInSpec.slice(1) : importInSpec; const nameInSchema = isDirective ? importInSchema.slice(1) : importInSchema; const split = CoreFeatures.splitPrefixedName(nameInSchema); if (split) { const [splitAlias, splitNameInSpec] = split; if (splitAlias === alias) { if (splitNameInSpec !== nameInSpec) { const splitImportInSpec = isDirective ? "@" + splitNameInSpec : splitNameInSpec; throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it can be confused with the namespaced name for "${splitImportInSpec}". Please rename the import to avoid conflicts via "as".`); } } else { const conflictFeature = this.byAlias.get(splitAlias); if (conflictFeature) { const conflictIdentity = conflictFeature.url.identity; this.checkTagInaccessibleConflict(conflictIdentity, identity); throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it can be confused with a namespaced name from another linked feature "${conflictIdentity}". Please rename the import or feature to avoid conflicts via "as".`); } else { this.conflictsByAlias.add(splitAlias, importInSchema); } } } if (isDirective) { if (nameInSchema === alias) { if (nameInSpec !== feature.url.name) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it can be confused with the namespaced name for "@${feature.url.name}". Please rename the import to avoid conflicts via "as".`); } } else { const conflictFeature = this.byAlias.get(nameInSchema); if (conflictFeature) { const conflictIdentity = conflictFeature.url.identity; this.checkTagInaccessibleConflict(conflictIdentity, identity); throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it can be confused with a namespaced name from another linked feature "${conflictIdentity}". Please rename the import or feature to avoid conflicts via "as".`); } } } const existingImportInSchema = importsMap.get(importInSpec); if (existingImportInSchema === undefined) { importsMap.set(importInSpec, importInSchema); } else { if (existingImportInSchema !== importInSchema) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it was previously imported as "${existingImportInSchema}". Please remove one of these imports.`); } } const entry = this.byImportName.get(importInSchema); if (entry === undefined) { this.byImportName.set(importInSchema, [feature, importInSpec]); } else { const [existingFeature, existingImportInSpec] = entry; const existingIdentity = existingFeature.url.identity; if (existingIdentity !== identity) { this.checkTagInaccessibleConflict(existingIdentity, identity); throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it was previously imported from feature "${existingIdentity}". Please rename the import to avoid conflicts via "as".`); } if (existingImportInSpec !== importInSpec) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${identity}" since it was previously imported for "${existingImportInSpec}". Please rename the import to avoid conflicts via "as".`); } } } this.byAlias.set(alias, feature); this.byIdentity.set(identity, [feature, importsMap]); } isAliasValid(alias, identity, importConflictsByIdentity) { if (alias.indexOf('__') !== -1) { return false; } if (alias.charAt(alias.length - 1) === '_') { return false; } if (!nameRegexp.test(alias)) { return false; } for (const [otherIdentity, importConflicts] of importConflictsByIdentity.entries()) { if (identity === otherIdentity) { if (importConflicts.self.has(alias)) { return false; } } else { if (importConflicts.other.has(alias)) { return false; } } } if (this.byAlias.has(alias)) { return false; } return true; } static computeAliasConflicts(specAliases, elementNames) { const trieNames = elementNames; const importConflictsByIdentity = new Map(); for (const { url, alias, imports } of specAliases) { trieNames.add(alias); const self = new Set(); const other = new Set(); for (const { name: importInSpec, as } of imports) { const importInSchema = as !== null && as !== void 0 ? as : importInSpec; const isDirective = importInSpec.charAt(0) === "@"; const nameInSpec = isDirective ? importInSpec.slice(1) : importInSpec; const nameInSchema = isDirective ? importInSchema.slice(1) : importInSchema; trieNames.add(nameInSchema); const split = CoreFeatures.splitPrefixedName(nameInSchema); if (split) { const [splitAlias, splitNameInSpec] = split; if (splitNameInSpec !== nameInSpec) { self.add(splitAlias); } other.add(splitAlias); } if (isDirective) { if (nameInSpec !== url.name) { self.add(nameInSchema); } other.add(nameInSchema); } } importConflictsByIdentity.set(url.identity, { self, other }); } let prefix = null; let index = 0; let computeUniqueAlias = (specName) => { if (prefix === null) { const aliasStart = [ '_', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', ].join(''); const aliasContinue = [ 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', '0123456789', ].join(''); const root = { children: new Map(), parent: null, char: '' }; for (const name of trieNames) { let node = root; for (const char of name) { let child = node.children.get(char); if (!child) { child = { children: new Map(), parent: node, char }; node.children.set(char, child); } node = child; } } const queue = [root]; let head = 0; while (prefix === null) { const possibleChars = head === 0 ? aliasStart : aliasContinue; const node = queue[head++]; for (const char of possibleChars) { const child = node.children.get(char); if (child) { queue.push(child); } else { const chars = [char]; for (let cur = node; cur === null || cur === void 0 ? void 0 : cur.parent; cur = cur.parent) { chars.push(cur.char); } prefix = chars.reverse().join(''); break; } } } } const suffix = specName.replace(/[^a-zA-Z]/g, ''); return `${prefix}${index++}${suffix}`; }; return { importConflictsByIdentity, computeUniqueAlias, }; } checkTagInaccessibleConflict(identity1, identity2) { const federationIdentity = 'https://specs.apollo.dev/federation'; const identities = new Set([identity1, identity2]); if (!identities.has(federationIdentity)) { return; } const [directive, identity] = identities.has(tagSpec_1.tagIdentity) ? ['tag', tagSpec_1.tagIdentity] : identities.has(inaccessibleSpec_1.inaccessibleIdentity) ? ['inaccessible', inaccessibleSpec_1.inaccessibleIdentity] : [undefined, undefined]; if (directive && identity) { throw error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Please import "@${directive}" from the feature "${federationIdentity}" instead of using "${identity}" to avoid potential unexpected behavior in the future.`); } } sourceFeature(element) { const isDirective = element instanceof DirectiveDefinition || element instanceof Directive; const importName = isDirective ? '@' + element.name : element.name; const entry = this.byImportName.get(importName); if (entry) { const [feature, importInSpec] = entry; return { feature, nameInFeature: isDirective ? importInSpec.slice(1) : importInSpec, isImported: true, }; } const defaultEntry = this.sourceDefaultName(isDirective, element.name); if (!defaultEntry) { return undefined; } const [feature, nameInSpec] = defaultEntry; const importInSpec = isDirective ? '@' + nameInSpec : nameInSpec; return { feature, nameInFeature: this.getImportName(feature, importInSpec) === undefined ? nameInSpec : null, isImported: false, }; } validateNoShadowingImports(schema) { const errors = []; for (const element of [...schema.allTypes(), ...schema.allDirectives()]) { const shadowingImport = this.getShadowingImport(element); if (!shadowingImport) { continue; } const isUsed = element instanceof DirectiveDefinition ? element.applications().size !== 0 : this.getReferencingRootElements(element) .some((referencer) => { return referencer.kind === 'SchemaDefinition' ? true : !this.getShadowingImport(referencer); }); if (!isUsed) { continue; } const { feature, importInSpec, importInSchema } = shadowingImport; const importInErrorMessage = importInSchema !== importInSpec ? `"${importInSpec}" as "${importInSchema}"` : `"${importInSpec}"`; errors.push(error_1.ERRORS.INVALID_LINK_DIRECTIVE_USAGE.err(`Cannot import ${importInErrorMessage} from feature "${feature.url.identity}" since there's a used definition for the namespaced name "${element.coordinate}". Please switch usages of the namespaced name to the import name and remove the definition.`)); } return errors; } getShadowingImport(element) { const isDirective = element instanceof DirectiveDefinition || element instanceof Directive; const defaultEntry = this.sourceDefaultName(isDirective, element.name); if (!defaultEntry) { return undefined; } const importName = isDirective ? '@' + element.name : element.name; const [feature, nameInSpec] = defaultEntry; const importInSpec = isDirective ? '@' + nameInSpec : nameInSpec; const importInSchema = this.getImportName(feature, importInSpec); return importInSchema !== undefined && importInSchema !== importName ? { feature, importInSpec, importInSchema, } : undefined; } getReferencingRootElements(element) { const referencers = []; for (const referencer of element.referencers()) { switch (referencer.kind) { case 'ObjectType': referencers.push(referencer); break; case 'InterfaceType': referencers.push(referencer); break; case 'UnionType': referencers.push(referencer); break; case 'SchemaDefinition': referencers.push(referencer); break; case 'FieldDefinition': referencers.push(referencer.parent); break; case 'InputFieldDefinition': referencers.push(referencer.parent); break; case 'ArgumentDefinition': const parent = referencer.parent; switch (parent.kind) { case 'DirectiveDefi