UNPKG

@graphql-tools/federation

Version:

Useful tools to create and manipulate GraphQL schemas.

1,349 lines (1,346 loc) • 75.9 kB
import { getTypeInfo, extractUnavailableFieldsFromSelectionSet, subtractSelectionSets, delegateToSchema, isExternalObject, UNPATHED_ERRORS_SYMBOL, createDefaultExecutor } from '@graphql-tools/delegate'; import { parseSelectionSet, memoize1, mergeDeep, mapSchema, MapperKind, getDirectiveExtensions, createGraphQLError, fakePromise } from '@graphql-tools/utils'; import { DisposableSymbols } from '@whatwg-node/disposablestack'; import { CustomEvent } from '@whatwg-node/events'; import { fetch } from '@whatwg-node/fetch'; import { CRITICAL_ERROR } from '@graphql-tools/executor'; import { buildHTTPExecutor } from '@graphql-tools/executor-http'; import { getDefaultFieldConfigMerger, ValidationLevel, stitchSchemas, calculateSelectionScore } from '@graphql-tools/stitch'; import { isPromise, handleMaybePromise } from '@whatwg-node/promise-helpers'; import { Kind, GraphQLList, isObjectType, isInterfaceType, parse, visit, print, buildASTSchema, isInputObjectType, parseType, visitWithTypeInfo } from 'graphql'; import { batchDelegateToSchema } from '@graphql-tools/batch-delegate'; import * as graphqlRelay from 'graphql-relay'; function createNodeDefinitions(subschemas, { nodeIdField = "nodeId" }) { const defs = []; const nodeIdFieldDef = { kind: Kind.FIELD_DEFINITION, name: { kind: Kind.NAME, value: nodeIdField }, description: { kind: Kind.STRING, value: "A globally unique identifier. Can be used in various places throughout the system to identify this single value." }, type: { kind: Kind.NON_NULL_TYPE, type: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: "ID" } } } }; const nodeInterfaceDef = { kind: Kind.INTERFACE_TYPE_DEFINITION, name: { kind: Kind.NAME, value: "Node" }, fields: [nodeIdFieldDef] }; defs.push(nodeInterfaceDef); for (const { typeName, kind } of getDistinctEntities(subschemas)) { const typeExtensionDef = { kind: kind === "object" ? Kind.OBJECT_TYPE_EXTENSION : Kind.INTERFACE_TYPE_EXTENSION, name: { kind: Kind.NAME, value: typeName }, interfaces: [ { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: "Node" } } ], fields: [nodeIdFieldDef] }; defs.push(typeExtensionDef); } const queryExtensionDef = { kind: Kind.OBJECT_TYPE_EXTENSION, name: { kind: Kind.NAME, value: "Query" }, fields: [ { kind: Kind.FIELD_DEFINITION, name: { kind: Kind.NAME, value: "node" }, description: { kind: Kind.STRING, value: "Fetches an object given its globally unique `ID`." }, type: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: "Node" } }, arguments: [ { kind: Kind.INPUT_VALUE_DEFINITION, name: { kind: Kind.NAME, value: nodeIdField }, description: { kind: Kind.STRING, value: "The globally unique `ID`." }, type: { kind: Kind.NON_NULL_TYPE, type: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: "ID" } } } } ] } ] }; defs.push(queryExtensionDef); return defs; } function createResolvers(subschemas, { nodeIdField = "nodeId", fromGlobalId = graphqlRelay.fromGlobalId, toGlobalId = graphqlRelay.toGlobalId }) { const types = getDistinctEntities(subschemas).filter( (t) => t.kind === "object" ); return { ...types.reduce( (resolvers, { typeName, merge, keyFieldNames }) => ({ ...resolvers, [typeName]: { [nodeIdField]: { selectionSet: merge.selectionSet, resolve(source) { if (keyFieldNames.length === 1) { return toGlobalId(typeName, source[keyFieldNames[0]]); } const keyFields = {}; for (const fieldName of keyFieldNames) { keyFields[fieldName] = source[fieldName]; } return toGlobalId(typeName, JSON.stringify(keyFields)); } } } }), {} ), Query: { node(_source, args, context, info) { const stitchingInfo = info.schema.extensions?.["stitchingInfo"]; if (!stitchingInfo) { return null; } const entities = getDistinctEntities( // the stitchingInfo.subschemaMap.values() is different from subschemas. it // contains the actual source of truth with all resolvers prepared - use it stitchingInfo.subschemaMap.values() ).filter((t) => t.kind === "object"); const { id: idOrFields, type: typeName } = fromGlobalId( args[nodeIdField] ); const entity = entities.find((t) => t.typeName === typeName); if (!entity) { return null; } const keyFields = {}; if (entity.keyFieldNames.length === 1) { keyFields[entity.keyFieldNames[0]] = idOrFields; } else { try { const idFields = JSON.parse(idOrFields); for (const fieldName of entity.keyFieldNames) { keyFields[fieldName] = idFields[fieldName]; } } catch { return null; } } return batchDelegateToSchema({ context, info, schema: entity.subschema, fieldName: entity.merge.fieldName, argsFromKeys: entity.merge.argsFromKeys, key: { ...keyFields, __typename: typeName }, // we already have all the necessary keys returnType: new GraphQLList( // wont ever be undefined, we ensured the subschema has the type above entity.subschema.schema.getType(typeName) ), dataLoaderOptions: entity.merge.dataLoaderOptions }); } } }; } function getDistinctEntities(subschemasIter) { const distinctEntities = []; function entityExists(typeName) { return distinctEntities.some( (distinctType) => distinctType.typeName === typeName ); } const subschemas = Array.from(subschemasIter); const types = subschemas.flatMap( (subschema) => Object.values(subschema.schema.getTypeMap()) ); for (const type of types) { if (type.name === "Node") { throw new Error( `The "Node" interface is reserved for Automatic Global Object Identification and should not be defined in subgraphs. Interface is found in the following subgraphs: ${subschemas.filter((s) => s.schema.getType("Node")).map((s) => `"${s.name}"`).join(", ")}` ); } } const objects = types.filter(isObjectType); for (const obj of objects) { if (entityExists(obj.name)) { continue; } let candidate = null; for (const subschema of subschemas) { const merge = subschema.merge?.[obj.name]; if (!merge) { continue; } if (!isMergedEntityConfig(merge)) { continue; } if (merge.canonical) { candidate = { subschema, merge }; break; } if (!candidate) { candidate = { subschema, merge }; continue; } if (merge.selectionSet.length < candidate.merge.selectionSet.length) { candidate = { subschema, merge }; } } if (!candidate) { continue; } distinctEntities.push({ ...candidate, kind: "object", typeName: obj.name, keyFieldNames: function getRootFieldNames(selectionSet) { const fieldNames = []; for (const sel of selectionSet.selections) { if (sel.kind === Kind.FRAGMENT_SPREAD) { throw new Error("Fragment spreads cannot appear in @key fields"); } if (sel.kind === Kind.INLINE_FRAGMENT) { fieldNames.push(...getRootFieldNames(sel.selectionSet)); continue; } fieldNames.push(sel.alias?.value || sel.name.value); } return fieldNames; }(parseSelectionSet(candidate.merge.selectionSet)) }); } if (distinctEntities.length) { const interfaces = types.filter(isInterfaceType); Interfaces: for (const inter of interfaces) { if (entityExists(inter.name)) { continue; } for (const subschema of subschemas) { const impls = subschema.schema.getImplementations(inter); if (impls.interfaces.length) { continue Interfaces; } if (!impls.objects.every(({ name }) => entityExists(name))) { continue Interfaces; } } distinctEntities.push({ kind: "interface", typeName: inter.name }); } } return distinctEntities; } const getArgsFromKeysForFederation = memoize1( function getArgsFromKeysForFederation2(representations) { return { representations }; } ); function projectDataSelectionSet(data, selectionSet) { if (data == null || selectionSet == null || !selectionSet?.selections?.length) { return data; } if (data instanceof Error) { return null; } if (Array.isArray(data)) { return data.map((entry) => projectDataSelectionSet(entry, selectionSet)); } const projectedData = { __typename: data.__typename }; for (const selection of selectionSet.selections) { if (selection.kind === Kind.FIELD) { const fieldName = selection.name.value; const responseKey = selection.alias?.value || selection.name.value; if (Object.prototype.hasOwnProperty.call(data, responseKey)) { const projectedKeyData = projectDataSelectionSet( data[responseKey], selection.selectionSet ); if (projectedData[fieldName]) { if (projectedKeyData != null && !(projectedKeyData instanceof Error)) { projectedData[fieldName] = mergeDeep( [projectedData[fieldName], projectedKeyData], void 0, true, true ); } } else { projectedData[fieldName] = projectedKeyData; } } } else if (selection.kind === Kind.INLINE_FRAGMENT) { if (selection.typeCondition && projectedData["__typename"] != null && projectedData["__typename"] !== selection.typeCondition.name.value) { continue; } Object.assign( projectedData, mergeDeep( [ projectedData, projectDataSelectionSet(data, selection.selectionSet) ], void 0, true, true ) ); } } return projectedData; } function getKeyFnForFederation(typeName, keys) { if (keys.some((key) => key.includes("{") || key.includes("("))) { const parsedSelectionSet = parseSelectionSet(`{${keys.join(" ")}}`, { noLocation: true }); return function keyFn(root) { if (root == null) { return root; } return projectDataSelectionSet( { __typename: typeName, ...root }, parsedSelectionSet ); }; } const allKeyProps = keys.flatMap((key) => key.split(" ")).map((key) => key.trim()); if (allKeyProps.length > 1) { return function keyFn(root) { if (root == null) { return null; } return allKeyProps.reduce( (prev, key) => { if (key !== "__typename") { prev[key] = root[key]; } return prev; }, { __typename: typeName } ); }; } const keyProp = allKeyProps[0]; return memoize1(function keyFn(root) { if (root == null) { return null; } const keyPropVal = root[keyProp]; if (keyPropVal == null) { return null; } return { __typename: typeName, [keyProp]: keyPropVal }; }); } function getCacheKeyFnFromKey(key) { if (key.includes("{") || key.includes("(")) { const parsedSelectionSet = parseSelectionSet(`{${key}}`, { noLocation: true }); return function cacheKeyFn(root) { return JSON.stringify(projectDataSelectionSet(root, parsedSelectionSet)); }; } const keyTrimmed = key.trim(); const keys = keyTrimmed.split(" ").map((key2) => key2.trim()); if (keys.length > 1) { return function cacheKeyFn(root) { return keys.map((key2) => { const keyVal = root[key2]; if (keyVal == null) { return ""; } if (typeof keyVal === "object") { return JSON.stringify(keyVal); } return keyVal; }).join(" "); }; } return memoize1(function cacheKeyFn(root) { const keyVal = root[keyTrimmed]; if (keyVal == null) { return ""; } if (typeof keyVal === "object") { return JSON.stringify(keyVal); } return keyVal; }); } function hasInaccessible(obj) { return getDirectiveExtensions(obj)?.inaccessible?.length; } function filterInternalFieldsAndTypes(finalSchema) { const internalTypeNameRegexp = /^(?:_Entity|_Any|_FieldSet|_Service|link|inaccessible|(?:link__|join__|core__)[\w]*)$/; return mapSchema(finalSchema, { [MapperKind.DIRECTIVE]: (directive) => { if (internalTypeNameRegexp.test(directive.name)) { return null; } return directive; }, [MapperKind.TYPE]: (type) => { if (internalTypeNameRegexp.test(type.name) || hasInaccessible(type)) { return null; } return type; }, [MapperKind.FIELD]: (fieldConfig) => { if (hasInaccessible(fieldConfig)) { return null; } return fieldConfig; }, [MapperKind.QUERY_ROOT_FIELD]: (fieldConfig, fieldName) => { if (fieldName === "_entities" || hasInaccessible(fieldConfig)) { return null; } return fieldConfig; }, [MapperKind.ENUM_VALUE]: (valueConfig) => { if (hasInaccessible(valueConfig)) { return null; } return valueConfig; }, [MapperKind.ARGUMENT]: (argConfig) => { if (hasInaccessible(argConfig)) { return null; } return argConfig; } }); } function getNamedTypeNode(typeNode) { if (typeNode.kind !== Kind.NAMED_TYPE) { return getNamedTypeNode(typeNode.type); } return typeNode; } function ensureSupergraphSDLAst(supergraphSdl) { return typeof supergraphSdl === "string" ? parse(supergraphSdl, { noLocation: true }) : supergraphSdl; } const rootTypeMap = /* @__PURE__ */ new Map([ ["Query", "query"], ["Mutation", "mutation"], ["Subscription", "subscription"] ]); const memoizedASTPrint = memoize1(print); const memoizedTypePrint = memoize1( (type) => type.toString() ); function getStitchingOptionsFromSupergraphSdl(opts) { const supergraphAst = ensureSupergraphSDLAst(opts.supergraphSdl); const subgraphEndpointMap = /* @__PURE__ */ new Map(); const subgraphTypesMap = /* @__PURE__ */ new Map(); const typeNameKeysBySubgraphMap = /* @__PURE__ */ new Map(); const typeNameFieldsKeyBySubgraphMap = /* @__PURE__ */ new Map(); const typeNameCanonicalMap = /* @__PURE__ */ new Map(); const subgraphTypeNameProvidedMap = /* @__PURE__ */ new Map(); const subgraphTypeNameFieldProvidedSelectionMap = /* @__PURE__ */ new Map(); const orphanTypeMap = /* @__PURE__ */ new Map(); const typeFieldASTMap = /* @__PURE__ */ new Map(); for (const definition of supergraphAst.definitions) { if ("fields" in definition) { const fieldMap = /* @__PURE__ */ new Map(); typeFieldASTMap.set(definition.name.value, fieldMap); for (const field of definition.fields || []) { fieldMap.set(field.name.value, field); } } } const subgraphExternalFieldMap = /* @__PURE__ */ new Map(); const subgraphNames = []; visit(supergraphAst, { EnumTypeDefinition(node) { if (node.name.value === "join__Graph") { node.values?.forEach((valueNode) => { subgraphNames.push(valueNode.name.value); }); } } }); function TypeWithFieldsVisitor(typeNode) { if (typeNode.name.value === "Query" || typeNode.name.value === "Mutation" && !typeNode.directives?.some( (directiveNode) => directiveNode.name.value === "join__type" )) { typeNode.directives = [ ...typeNode.directives || [], ...subgraphNames.map((subgraphName) => ({ kind: Kind.DIRECTIVE, name: { kind: Kind.NAME, value: "join__type" }, arguments: [ { kind: Kind.ARGUMENT, name: { kind: Kind.NAME, value: "graph" }, value: { kind: Kind.ENUM, value: subgraphName } } ] })) ]; } let isOrphan = true; const fieldDefinitionNodesByGraphName = /* @__PURE__ */ new Map(); typeNode.directives?.forEach((directiveNode) => { if (typeNode.kind === Kind.OBJECT_TYPE_DEFINITION) { if (directiveNode.name.value === "join__owner") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode.value?.kind === Kind.ENUM) { typeNameCanonicalMap.set( typeNode.name.value, argumentNode.value.value ); } }); } } if (directiveNode.name.value === "join__type") { isOrphan = false; const joinTypeGraphArgNode = directiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "graph" ); if (joinTypeGraphArgNode?.value?.kind === Kind.ENUM) { const graphName = joinTypeGraphArgNode.value.value; if (typeNode.kind === Kind.OBJECT_TYPE_DEFINITION || typeNode.kind === Kind.INTERFACE_TYPE_DEFINITION) { const keyArgumentNode = directiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "key" ); const isResolvable = !directiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "resolvable" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === false ); if (isResolvable && keyArgumentNode?.value?.kind === Kind.STRING) { let typeNameKeysMap = typeNameKeysBySubgraphMap.get(graphName); if (!typeNameKeysMap) { typeNameKeysMap = /* @__PURE__ */ new Map(); typeNameKeysBySubgraphMap.set(graphName, typeNameKeysMap); } let keys = typeNameKeysMap.get(typeNode.name.value); if (!keys) { keys = []; typeNameKeysMap.set(typeNode.name.value, keys); } keys.push(keyArgumentNode.value.value); } } const fieldDefinitionNodesOfSubgraph = []; typeNode.fields?.forEach((fieldNode) => { const joinFieldDirectives = fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value === "join__field" ); let notInSubgraph = true; joinFieldDirectives?.forEach((joinFieldDirectiveNode) => { const joinFieldGraphArgNode = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "graph" ); if (joinFieldGraphArgNode?.value?.kind === Kind.ENUM && joinFieldGraphArgNode.value.value === graphName) { notInSubgraph = false; const isExternal = joinFieldDirectiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "external" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === true ); const isOverridden = joinFieldDirectiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "usedOverridden" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === true ); if (isExternal) { let externalFieldsByType = subgraphExternalFieldMap.get(graphName); if (!externalFieldsByType) { externalFieldsByType = /* @__PURE__ */ new Map(); subgraphExternalFieldMap.set( graphName, externalFieldsByType ); } let externalFields = externalFieldsByType.get( typeNode.name.value ); if (!externalFields) { externalFields = /* @__PURE__ */ new Set(); externalFieldsByType.set( typeNode.name.value, externalFields ); } externalFields.add(fieldNode.name.value); } if (!isExternal && !isOverridden) { const typeArg = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "type" ); const typeNode2 = typeArg?.value.kind === Kind.STRING ? parseType(typeArg.value.value) : fieldNode.type; fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, type: typeNode2, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } const providedExtraField = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "provides" ); if (providedExtraField?.value?.kind === Kind.STRING) { let handleSelection2 = function(fieldNodeTypeName2, selection) { switch (selection.kind) { case Kind.FIELD: { const extraFieldTypeNode = supergraphAst.definitions.find( (def) => "name" in def && def.name?.value === fieldNodeTypeName2 ); const extraFieldNodeInType = extraFieldTypeNode.fields?.find( (fieldNode2) => fieldNode2.name.value === selection.name.value ); if (extraFieldNodeInType) { let typeNameProvidedMap = subgraphTypeNameProvidedMap.get(graphName); if (!typeNameProvidedMap) { typeNameProvidedMap = /* @__PURE__ */ new Map(); subgraphTypeNameProvidedMap.set( graphName, typeNameProvidedMap ); } let providedFields = typeNameProvidedMap.get(fieldNodeTypeName2); if (!providedFields) { providedFields = /* @__PURE__ */ new Set(); typeNameProvidedMap.set( fieldNodeTypeName2, providedFields ); } providedFields.add(selection.name.value); if (selection.selectionSet) { const extraFieldNodeNamedType = getNamedTypeNode( extraFieldNodeInType.type ); const extraFieldNodeTypeName = extraFieldNodeNamedType.name.value; for (const subSelection of selection.selectionSet.selections) { handleSelection2( extraFieldNodeTypeName, subSelection ); } } } } break; case Kind.INLINE_FRAGMENT: { const fragmentType = selection.typeCondition?.name?.value || fieldNodeType.name.value; if (selection.selectionSet) { for (const subSelection of selection.selectionSet.selections) { handleSelection2(fragmentType, subSelection); } } } break; } }; const providesSelectionSet = parseSelectionSet( /* GraphQL */ `{ ${providedExtraField.value.value} }` ); let typeNameFieldProvidedSelectionMap = subgraphTypeNameFieldProvidedSelectionMap.get(graphName); if (!typeNameFieldProvidedSelectionMap) { typeNameFieldProvidedSelectionMap = /* @__PURE__ */ new Map(); subgraphTypeNameFieldProvidedSelectionMap.set( graphName, typeNameFieldProvidedSelectionMap ); } let fieldProvidedSelectionMap = typeNameFieldProvidedSelectionMap.get(typeNode.name.value); if (!fieldProvidedSelectionMap) { fieldProvidedSelectionMap = /* @__PURE__ */ new Map(); typeNameFieldProvidedSelectionMap.set( typeNode.name.value, fieldProvidedSelectionMap ); } fieldProvidedSelectionMap.set( fieldNode.name.value, providesSelectionSet ); const fieldNodeType = getNamedTypeNode(fieldNode.type); const fieldNodeTypeName = fieldNodeType.name.value; for (const selection of providesSelectionSet.selections) { handleSelection2(fieldNodeTypeName, selection); } } const requiresArgumentNode = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "requires" ); if (requiresArgumentNode?.value?.kind === Kind.STRING) { let typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(graphName); if (!typeNameFieldsKeyMap) { typeNameFieldsKeyMap = /* @__PURE__ */ new Map(); typeNameFieldsKeyBySubgraphMap.set( graphName, typeNameFieldsKeyMap ); } let fieldsKeyMap = typeNameFieldsKeyMap.get( typeNode.name.value ); if (!fieldsKeyMap) { fieldsKeyMap = /* @__PURE__ */ new Map(); typeNameFieldsKeyMap.set(typeNode.name.value, fieldsKeyMap); } fieldsKeyMap.set( fieldNode.name.value, requiresArgumentNode.value.value ); } } }); if (!joinFieldDirectives?.length) { fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } else if (notInSubgraph && typeNameKeysBySubgraphMap.get(graphName)?.get(typeNode.name.value)?.some((key) => key.split(" ").includes(fieldNode.name.value))) { fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } }); fieldDefinitionNodesByGraphName.set( graphName, fieldDefinitionNodesOfSubgraph ); } } }); const joinImplementsDirectives = typeNode.directives?.filter( (directiveNode) => directiveNode.name.value === "join__implements" ); fieldDefinitionNodesByGraphName.forEach( (fieldDefinitionNodesOfSubgraph, graphName) => { const interfaces = []; typeNode.interfaces?.forEach((interfaceNode) => { const implementedSubgraphs = joinImplementsDirectives?.filter( (directiveNode) => { const argumentNode = directiveNode.arguments?.find( (argumentNode2) => argumentNode2.name.value === "interface" ); return argumentNode?.value?.kind === Kind.STRING && argumentNode.value.value === interfaceNode.name.value; } ); if (!implementedSubgraphs?.length || implementedSubgraphs.some((directiveNode) => { const argumentNode = directiveNode.arguments?.find( (argumentNode2) => argumentNode2.name.value === "graph" ); return argumentNode?.value?.kind === Kind.ENUM && argumentNode.value.value === graphName; })) { interfaces.push(interfaceNode); } }); if (typeNode.name.value === "Query") { fieldDefinitionNodesOfSubgraph.push(entitiesFieldDefinitionNode); } const objectTypedDefNodeForSubgraph = { ...typeNode, interfaces, fields: fieldDefinitionNodesOfSubgraph, directives: typeNode.directives?.filter( (directiveNode) => directiveNode.name.value !== "join__type" && directiveNode.name.value !== "join__owner" && directiveNode.name.value !== "join__implements" ) }; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push(objectTypedDefNodeForSubgraph); } ); if (isOrphan) { orphanTypeMap.set(typeNode.name.value, typeNode); } } visit(supergraphAst, { ScalarTypeDefinition(node) { let isOrphan = !node.name.value.startsWith("link__") && !node.name.value.startsWith("join__"); node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ) }); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, InputObjectTypeDefinition(node) { let isOrphan = true; node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ) }); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, InterfaceTypeDefinition: TypeWithFieldsVisitor, UnionTypeDefinition(node) { let isOrphan = true; node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; const unionMembers = []; node.directives?.forEach((directiveNode2) => { if (directiveNode2.name.value === "join__unionMember") { const graphArgumentNode = directiveNode2.arguments?.find( (argumentNode2) => argumentNode2.name.value === "graph" ); const memberArgumentNode = directiveNode2.arguments?.find( (argumentNode2) => argumentNode2.name.value === "member" ); if (graphArgumentNode?.value?.kind === Kind.ENUM && graphArgumentNode.value.value === graphName && memberArgumentNode?.value?.kind === Kind.STRING) { unionMembers.push({ kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: memberArgumentNode.value.value } }); } } }); if (unionMembers.length > 0) { let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, types: unionMembers, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" && directiveNode2.name.value !== "join__unionMember" ) }); } } }); } }); if (isOrphan && node.name.value !== "_Entity") { orphanTypeMap.set(node.name.value, node); } }, EnumTypeDefinition(node) { let isOrphan = true; if (node.name.value === "join__Graph") { node.values?.forEach((valueNode) => { isOrphan = false; valueNode.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__graph") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "url" && argumentNode.value?.kind === Kind.STRING) { subgraphEndpointMap.set( valueNode.name.value, argumentNode.value.value ); } }); } }); }); } node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { isOrphan = false; directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode.value?.kind === Kind.ENUM) { const graphName = argumentNode.value.value; const enumValueNodes = []; node.values?.forEach((valueNode) => { const joinEnumValueDirectives = valueNode.directives?.filter( (directiveNode2) => directiveNode2.name.value === "join__enumValue" ); if (joinEnumValueDirectives?.length) { joinEnumValueDirectives.forEach( (joinEnumValueDirectiveNode) => { joinEnumValueDirectiveNode.arguments?.forEach( (argumentNode2) => { if (argumentNode2.name.value === "graph" && argumentNode2.value?.kind === Kind.ENUM && argumentNode2.value.value === graphName) { enumValueNodes.push({ ...valueNode, directives: valueNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__enumValue" ) }); } } ); } ); } else { enumValueNodes.push(valueNode); } }); const enumTypedDefNodeForSubgraph = { ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ), values: enumValueNodes }; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push(enumTypedDefNodeForSubgraph); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, ObjectTypeDefinition: TypeWithFieldsVisitor }); const subschemas = []; for (const [subgraphName, endpoint] of subgraphEndpointMap) { let visitTypeDefinitionsForOrphanTypes2 = function(node) { function visitNamedTypeNode(namedTypeNode) { const typeName = namedTypeNode.name.value; if (specifiedTypeNames.includes(typeName)) { return node; } const orphanType = orphanTypeMap.get(typeName); if (orphanType) { if (!extraOrphanTypesForSubgraph.has(typeName)) { extraOrphanTypesForSubgraph.set(typeName, {}); const extraOrphanType = visitTypeDefinitionsForOrphanTypes2(orphanType); extraOrphanTypesForSubgraph.set(typeName, extraOrphanType); } } else if (!subgraphTypes.some((typeNode) => typeNode.name.value === typeName)) { return null; } return node; } function visitFieldDefs(nodeFields) { const fields = []; for (const field of nodeFields || []) { const isTypeNodeOk = visitNamedTypeNode( getNamedTypeNode(field.type) ); if (!isTypeNodeOk) { continue; } if (field.kind === Kind.FIELD_DEFINITION) { const args = visitFieldDefs( field.arguments ); fields.push({ ...field, arguments: args }); } else { fields.push(field); } } return fields; } function visitObjectAndInterfaceDefs(node2) { const fields = visitFieldDefs(node2.fields); const interfaces = []; for (const iface of node2.interfaces || []) { const isTypeNodeOk = visitNamedTypeNode(iface); if (!isTypeNodeOk) { continue; } interfaces.push(iface); } return { ...node2, fields, interfaces }; } return visit(node, { [Kind.OBJECT_TYPE_DEFINITION]: visitObjectAndInterfaceDefs, [Kind.OBJECT_TYPE_EXTENSION]: visitObjectAndInterfaceDefs, [Kind.INTERFACE_TYPE_DEFINITION]: visitObjectAndInterfaceDefs, [Kind.INTERFACE_TYPE_EXTENSION]: visitObjectAndInterfaceDefs, [Kind.UNION_TYPE_DEFINITION](node2) { const types = []; for (const type of node2.types || []) { const isTypeNodeOk = visitNamedTypeNode(type); if (!isTypeNodeOk) { continue; } types.push(type); } return { ...node2, types }; }, [Kind.UNION_TYPE_EXTENSION](node2) { const types = []; for (const type of node2.types || []) { const isTypeNodeOk = visitNamedTypeNode(type); if (!isTypeNodeOk) { continue; } types.push(type); } return { ...node2, types }; }, [Kind.INPUT_OBJECT_TYPE_DEFINITION](node2) { const fields = visitFieldDefs(node2.fields); return { ...node2, fields }; }, [Kind.INPUT_OBJECT_TYPE_EXTENSION](node2) { const fields = visitFieldDefs(node2.fields); return { ...node2, fields }; } }); }; const mergeConfig = {}; const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName); const unionTypeNodes = []; if (typeNameKeyMap) { const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName); for (const [typeName, keys] of typeNameKeyMap) { let getMergedTypeConfigFromKey2 = function(key) { return { selectionSet: `{ ${key} }`, argsFromKeys: getArgsFromKeysForFederation, key: getKeyFnForFederation(typeName, [key, ...extraKeys]), fieldName: `_entities`, dataLoaderOptions: { cacheKeyFn: getCacheKeyFnFromKey(key), ...opts.batchDelegateOptions || {} } }; }; const mergedTypeConfig = mergeConfig[typeName] = {}; const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName); const extraKeys = /* @__PURE__ */ new Set(); if (fieldsKeyMap) { const fieldsConfig = mergedTypeConfig.fields = {}; for (const [fieldName, fieldNameKey] of fieldsKeyMap) { const selectionSetNode = parseSelectionSet(`{${fieldNameKey}}`); (function aliasFieldsWithArgs(selectionSetNode2) { for (const selection of selectionSetNode2.selections) { if (selection.kind === Kind.FIELD && selection.arguments?.length) { selection.alias = { kind: Kind.NAME, value: "_" + selection.name.value }; } if ("selectionSet" in selection && selection.selectionSet) { aliasFieldsWithArgs(selection.selectionSet); } } })(selectionSetNode); const selectionSet = print(selectionSetNode).replaceAll(/\n/g, " ").replaceAll(/\s+/g, " "); extraKeys.add( // remove first and last characters (curly braces) selectionSet.slice(1, -1) ); fieldsConfig[fieldName] = { selectionSet, computed: true }; } } if (typeNameCanonicalMap.get(typeName) === subgraphName) { mergedTypeConfig.canonical = true; } if (keys.length === 1 && keys[0]) { Object.assign(mergedTypeConfig, getMergedTypeConfigFromKey2(keys[0])); } if (keys.length > 1) { const entryPoints = keys.map( (key) => getMergedTypeConfigFromKey2(key) ); mergedTypeConfig.entryPoints = entryPoints; } unionTypeNodes.push({ kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: typeName } }); opts.onMergedTypeConfig?.(typeName, mergedTypeConfig); } } const typeNameProvidedSelectionMap = subgraphTypeNameFieldProvidedSelectionMap.get(subgraphName); if (typeNameProvidedSelectionMap) { for (const [ typeName, fieldSelectionMap ] of typeNameProvidedSelectionMap) { const mergedTypeConfig = mergeConfig[typeName] ||= {}; const fieldsConfig = mergedTypeConfig.fields ||= {}; for (const [fieldName, selectionSet] of fieldSelectionMap) { fieldsConfig[fieldName] = { provides: selectionSet }; } } } const entitiesUnionTypeDefinitionNode = { name: { kind: Kind.NAME, value: "_Entity" }, kind: Kind.UNION_TYPE_DEFINITION, types: unionTypeNodes }; const extraOrphanTypesForSubgraph = /* @__PURE__ */ new Map(); const subgraphTypes = subgraphTypesMap.get(subgraphName) || []; subgraphTypes.forEach((typeNode) => { visitTypeDefinitionsForOrphanTypes2(typeNode); }); const extendedSubgraphTypes = [ ...subgraphTypes, ...extraOrphanTypesForSubgraph.values() ]; for (const interfaceInSubgraph of extendedSubgraphTypes) { if (interfaceInSubgraph.kind === Kind.INTERFACE_TYPE_DEFINITION) { let isOrphan = true; for (const definitionNode of supergraphAst.definitions) { if (definitionNode.kind === Kind.OBJECT_TYPE_DEFINITION && definitionNode.interfaces?.some( (interfaceNode) => interfaceNode.name.value === interfaceInSubgraph.name.value )) { isOrphan = false; } } if (isOrphan) { interfaceInSubgraph.kind = Kind.OBJECT_TYPE_DEFINITION; } } } let schema; let schemaAst = { kind: Kind.DOCUMENT, definitions: [ ...extendedSubgraphTypes, entitiesUnionTypeDefinitionNode, anyTypeDefinitionNode ] }; if (opts.onSubgraphAST) { schemaAst = opts.onSubgraphAST(subgraphName, schemaAst); } try { schema = buildASTSchema(schemaAst, { assumeValidSDL: true, assumeValid: true }); } catch (e) { throw new Error( `Error building schema for subgraph ${subgraphName}: ${e?.stack || e?.message || e.toString()}` ); } let httpExecutorOpts; if (typeof opts.httpExecutorOpts === "function") { httpExecutorOpts = opts.httpExecutorOpts({ name: subgraphName, endpoint }); } else { httpExecutorOpts = opts.httpExecutorOpts || {}; } let executor = buildHTTPExecutor({ endpoint, ...httpExecutorOpts }); if (globalThis.process?.env?.["DEBUG"]) { const origExecutor = executor; executor = function debugExecutor(execReq) { const prefix = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName}`; console.debug(`${prefix} - subgraph-execute-start`, { document: memoizedASTPrint(execReq.document), variables: JSON.stringify(execReq.variables) }); return handleMaybePromise( () => origExecutor(execReq), (res) => { console.debug( `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName} - subgraph-execute-done`, JSON.stringify(res) ); return res; }, (err) => { console.error( `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName} - subgraph-execute-error`, err ); return err; } ); }; } const typeNameProvidedMap = subgraphTypeNameProvidedMap.get(subgraphName); const externalFieldMap = subgraphExternalFieldMap.get(subgraphName); const transforms = []; if (externalFieldMap?.size && extendedSubgraphTypes.some( (t) => t.kind === Kind.INTERFACE_TYPE_DEFINITION )) { let createUnresolvableError2 = function(fieldName, fieldNode) { return createGraphQLError( `Was not able to find any options for ${fieldName}: This shouldn't have happened.`, { extensions: { [CRITICAL_ERROR]: true }, nodes: [fieldNode] } ); }, createIfaceFieldChecker2 = function(parentType) { const providedInterfaceFields = typeNameProvidedMap?.get( parentType.name ); const implementations = schema.getPossibleTypes(parentType); const ifaceFieldCheckResult = /* @__PURE__ */ new Map(); return function ifaceFieldChecker(fieldName) { let result = ifaceFieldCheckResult.get(fieldName); if (result == null) { result = true; for (const implementation of implementations) { const externalFields = externalFieldMap?.get(implementation.name); const providedFields = typeNameProvidedMap?.get( implementation.name ); if (!providedInterfaceFields?.has(fieldName) && !providedFields?.has(fieldName) && externalFields?.has(fieldName)) { result = false; break; } } ifaceFieldCheckResult.set(fieldName, result); } return result; }; }; const typeInfo = getTypeInfo(schema); const visitorKeys = { Document: ["definitions"], Operatio