@graphql-tools/federation
Version:
Useful tools to create and manipulate GraphQL schemas.
1,346 lines (1,337 loc) • 72.4 kB
JavaScript
import { createDefaultExecutor, getTypeInfo, extractUnavailableFieldsFromSelectionSet, subtractSelectionSets, delegateToSchema, isExternalObject, UNPATHED_ERRORS_SYMBOL } from '@graphql-tools/delegate';
import { buildHTTPExecutor } from '@graphql-tools/executor-http';
import { mergeTypeDefs, mergeResolvers } from '@graphql-tools/merge';
import { stitchSchemas, getDefaultFieldConfigMerger, ValidationLevel, calculateSelectionScore } from '@graphql-tools/stitch';
import { printSchemaWithDirectives, mapMaybePromise, mergeDeep, parseSelectionSet, memoize1, mapSchema, MapperKind, getDirectiveExtensions, createGraphQLError, inspect, getDocumentNodeFromSchema, isPromise } from '@graphql-tools/utils';
import { visit, Kind, buildASTSchema, parse, print, isObjectType, isInterfaceType, isInputObjectType, parseType, visitWithTypeInfo } from 'graphql';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { fetch } from '@whatwg-node/fetch';
const SubgraphBaseSDL = (
/* GraphQL */
`
scalar _Any
scalar _FieldSet
scalar link__Import
enum link__Purpose {
SECURITY
EXECUTION
}
type _Service {
sdl: String!
}
type Query {
_service: _Service!
}
directive @external on FIELD_DEFINITION | OBJECT
directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
directive @key(
fields: _FieldSet!
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive @link(
url: String!
as: String
for: link__Purpose
import: [link__Import]
) repeatable on SCHEMA
directive @shareable repeatable on OBJECT | FIELD_DEFINITION
directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive @tag(
name: String!
) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
directive @override(from: String!) on FIELD_DEFINITION
directive @composeDirective(name: String!) repeatable on SCHEMA
directive @extends on OBJECT | INTERFACE
`
);
function buildSubgraphSchema(optsOrModules) {
const opts = Array.isArray(optsOrModules) ? {
typeDefs: optsOrModules.map((opt) => opt.typeDefs),
resolvers: optsOrModules.map((opt) => opt.resolvers).flat()
} : optsOrModules;
const entityTypeNames = [];
function handleEntity(node) {
if (node.directives?.some((directive) => directive.name.value === "key")) {
entityTypeNames.push(node.name.value);
}
}
const typeDefs = visit(mergeTypeDefs([SubgraphBaseSDL, opts.typeDefs]), {
ObjectTypeDefinition: (node) => {
handleEntity(node);
},
ObjectTypeExtension: (node) => {
handleEntity(node);
return {
...node,
kind: Kind.OBJECT_TYPE_DEFINITION,
directives: [
...node.directives || [],
{
kind: "Directive",
name: {
kind: "Name",
value: "extends"
}
}
]
};
}
});
const givenResolvers = mergeResolvers(opts.resolvers);
const allTypeDefs = [typeDefs];
const allResolvers = [sdlResolvers, givenResolvers];
if (entityTypeNames.length > 0) {
allTypeDefs.push(`union _Entity = ${entityTypeNames.join(" | ")}`);
allTypeDefs.push(`extend type Query {
_entities(representations: [_Any!]!): [_Entity]!
}`);
allResolvers.push({
_Entity: {
__resolveType: entityTypeResolver
},
Query: {
_entities: (_root, args, context, info) => args.representations.map(
(representation) => mapMaybePromise(
givenResolvers[representation.__typename]?.__resolveReference?.(
representation,
context,
info
),
(resolvedEntity) => {
if (!resolvedEntity) {
return representation;
}
if (!resolvedEntity.__typename) {
resolvedEntity.__typename = representation.__typename;
}
return resolvedEntity;
}
)
)
}
});
}
return makeExecutableSchema({
assumeValid: true,
assumeValidSDL: true,
...opts,
typeDefs: allTypeDefs,
resolvers: allResolvers
});
}
function entityTypeResolver(obj) {
return obj.__typename;
}
const sdlResolvers = {
Query: {
_service: () => ({})
},
_Service: {
sdl: (_root, _args, _context, info) => printSchemaWithDirectives(info.schema)
}
};
function getArgsFromKeysForFederation(representations) {
return { representations };
}
function getKeyForFederation(root) {
return root;
}
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;
}
class EventEmitter {
#listeners = /* @__PURE__ */ new Map();
on(eventName, listener) {
const listeners = this.#listeners.get(eventName);
if (!listeners) {
this.#listeners.set(eventName, [listener]);
} else {
listeners.push(listener);
}
return this;
}
once(eventName, listener) {
const selfRemovingListener = (...args) => {
this.off(eventName, selfRemovingListener);
listener(...args);
};
this.on(eventName, selfRemovingListener);
return this;
}
off(eventName, listener) {
const listeners = this.#listeners.get(eventName);
if (listeners) {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
}
return this;
}
emit(eventName, ...args) {
const listeners = this.#listeners.get(eventName);
if (!listeners) {
return false;
}
for (let i = listeners.length - 1; i >= 0; i--) {
listeners[i]?.(...args);
}
return true;
}
}
const SubgraphSDLQuery = (
/* GraphQL */
`
query SubgraphSDL {
_service {
sdl
}
}
`
);
async function getSubschemaForFederationWithURL(config) {
const executor = buildHTTPExecutor(config);
const subschemaConfig = await getSubschemaForFederationWithExecutor(executor);
return {
batch: true,
...subschemaConfig
};
}
function getSubschemaForFederationWithTypeDefs(typeDefs) {
const subschemaConfig = {};
const typeMergingConfig = subschemaConfig.merge = subschemaConfig.merge || {};
const entityTypes = [];
const visitor = (node) => {
if (node.directives) {
const typeName = node.name.value;
const selections = [];
for (const directive of node.directives) {
const directiveArgs = directive.arguments || [];
switch (directive.name.value) {
case "key": {
if (directiveArgs.some(
(arg) => arg.name.value === "resolvable" && arg.value.kind === Kind.BOOLEAN && arg.value.value === false
)) {
continue;
}
const selectionValueNode = directiveArgs.find(
(arg) => arg.name.value === "fields"
)?.value;
if (selectionValueNode?.kind === Kind.STRING) {
selections.push(selectionValueNode.value);
}
break;
}
case "inaccessible":
return null;
}
}
if (selections.length === 0) {
return node;
}
const typeMergingTypeConfig = typeMergingConfig[typeName] = typeMergingConfig[typeName] || {};
if (node.kind === Kind.OBJECT_TYPE_DEFINITION && !node.directives?.some((d) => d.name.value === "extends")) {
typeMergingTypeConfig.canonical = true;
}
entityTypes.push(typeName);
const selectionsStr = selections.join(" ");
typeMergingTypeConfig.selectionSet = `{ ${selectionsStr} }`;
typeMergingTypeConfig.dataLoaderOptions = {
cacheKeyFn: getCacheKeyFnFromKey(selectionsStr)
};
typeMergingTypeConfig.argsFromKeys = getArgsFromKeysForFederation;
typeMergingTypeConfig.fieldName = `_entities`;
typeMergingTypeConfig.key = getKeyForFederation;
const fields = [];
if (node.fields) {
for (const fieldNode of node.fields) {
let removed = false;
if (fieldNode.directives) {
const fieldName = fieldNode.name.value;
for (const directive of fieldNode.directives) {
const directiveArgs = directive.arguments || [];
switch (directive.name.value) {
case "requires": {
const typeMergingFieldsConfig = typeMergingTypeConfig.fields = typeMergingTypeConfig.fields || {};
typeMergingFieldsConfig[fieldName] = typeMergingFieldsConfig[fieldName] || {};
if (directiveArgs.some(
(arg) => arg.name.value === "resolvable" && arg.value.kind === Kind.BOOLEAN && arg.value.value === false
)) {
continue;
}
const selectionValueNode = directiveArgs.find(
(arg) => arg.name.value === "fields"
)?.value;
if (selectionValueNode?.kind === Kind.STRING) {
typeMergingFieldsConfig[fieldName].selectionSet = `{ ${selectionValueNode.value} }`;
typeMergingFieldsConfig[fieldName].computed = true;
}
break;
}
case "external":
case "inaccessible": {
removed = !typeMergingTypeConfig.selectionSet?.includes(
` ${fieldName} `
);
break;
}
case "override": {
const typeMergingFieldsConfig = typeMergingTypeConfig.fields = typeMergingTypeConfig.fields || {};
typeMergingFieldsConfig[fieldName] = typeMergingFieldsConfig[fieldName] || {};
typeMergingFieldsConfig[fieldName].canonical = true;
break;
}
}
}
}
if (!removed) {
fields.push(fieldNode);
}
}
node.fields = fields;
}
}
return {
...node,
kind: Kind.OBJECT_TYPE_DEFINITION
};
};
const parsedSDL = visit(typeDefs, {
ObjectTypeExtension: visitor,
ObjectTypeDefinition: visitor
});
let extraSdl = SubgraphBaseSDL;
if (entityTypes.length > 0) {
extraSdl += `
union _Entity = ${entityTypes.join(" | ")}`;
extraSdl += `
extend type Query { _entities(representations: [_Any!]!): [_Entity]! }`;
}
subschemaConfig.schema = buildASTSchema(
mergeTypeDefs([extraSdl, parsedSDL]),
{
assumeValidSDL: true,
assumeValid: true
}
);
return subschemaConfig;
}
async function getSubschemaForFederationWithExecutor(executor) {
const sdlQueryResult = await executor({
document: parse(SubgraphSDLQuery)
});
if (sdlQueryResult.errors?.length && sdlQueryResult.errors[0]) {
const error = sdlQueryResult.errors[0];
throw createGraphQLError(error.message, error);
}
if (!sdlQueryResult.data?._service?.sdl) {
throw new Error(`Unexpected result: ${inspect(sdlQueryResult)}`);
}
const typeDefs = parse(sdlQueryResult.data._service.sdl);
const subschemaConfig = getSubschemaForFederationWithTypeDefs(typeDefs);
return {
...subschemaConfig,
executor
};
}
async function getSubschemaForFederationWithSchema(schema) {
const executor = createDefaultExecutor(schema);
return getSubschemaForFederationWithExecutor(executor);
}
async function getStitchedSchemaWithUrls(configs) {
const subschemas = await Promise.all(
configs.map((config) => getSubschemaForFederationWithURL(config))
);
const schema = stitchSchemas({
subschemas
});
return filterInternalFieldsAndTypes(schema);
}
const federationSubschemaTransformer = function federationSubschemaTransformer2(subschemaConfig) {
const typeDefs = getDocumentNodeFromSchema(subschemaConfig.schema);
const newSubschema = getSubschemaForFederationWithTypeDefs(typeDefs);
return {
...subschemaConfig,
...newSubschema,
merge: {
...newSubschema.merge,
...subschemaConfig.merge
}
};
};
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 aliasedFieldNameKey = fieldNameKey.includes("(") ? `_${fieldNameKey.split("(")[0]}: ${fieldNameKey}` : fieldNameKey;
extraKeys.add(aliasedFieldNameKey);
fieldsConfig[fieldName] = {
selectionSet: `{ ${aliasedFieldNameKey} }`,
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 = async function debugExecutor(execReq) {
console.log(`Executing ${subgraphName} with args:`, {
document: print(execReq.document),
variables: JSON.stringify(execReq.variables)
});
const res = await origExecutor(execReq);
console.log(`Response from ${subgraphName}:`, JSON.stringify(res));
return res;
};
}
const typeNameProvidedMap = subgraphTypeNameProvidedMap.get(subgraphName);
const externalFieldMap = subgraphExternalFieldMap.get(subgra