@graphql-tools/federation
Version:
Useful tools to create and manipulate GraphQL schemas.
1,349 lines (1,346 loc) • 75.9 kB
JavaScript
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