@graphql-tools/federation
Version:
Useful tools to create and manipulate GraphQL schemas.
1,292 lines (1,287 loc) • 98.1 kB
JavaScript
'use strict';
var delegate = require('@graphql-tools/delegate');
var utils = require('@graphql-tools/utils');
var disposablestack = require('@whatwg-node/disposablestack');
var events = require('@whatwg-node/events');
var fetch = require('@whatwg-node/fetch');
var executor = require('@graphql-tools/executor');
var executorHttp = require('@graphql-tools/executor-http');
var stitch = require('@graphql-tools/stitch');
var promiseHelpers = require('@whatwg-node/promise-helpers');
var graphql = require('graphql');
function getEnvStr(key, opts = {}) {
const globalThat = opts.globalThis ?? globalThis;
let variable = globalThat.process?.env?.[key] || // @ts-expect-error can exist in wrangler and maybe other runtimes
globalThat.env?.[key] || // @ts-expect-error can exist in deno
globalThat.Deno?.env?.get(key) || // @ts-expect-error could be
globalThat[key];
if (variable != null) {
variable += "";
} else {
variable = void 0;
}
return variable?.trim();
}
// Regexps involved with splitting words in various case formats.
const SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
const SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
// Used to iterate over the initial split result and separate numbers.
const SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u;
// Regexp involved with stripping non-word characters from the result.
const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
// The replacement value for splits.
const SPLIT_REPLACE_VALUE = "$1\0$2";
// The default characters to keep after transforming case.
const DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
/**
* Split any cased input strings into an array of words.
*/
function split(value) {
let result = value.trim();
result = result
.replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE)
.replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
let start = 0;
let end = result.length;
// Trim the delimiter from around the output string.
while (result.charAt(start) === "\0")
start++;
if (start === end)
return [];
while (result.charAt(end - 1) === "\0")
end--;
return result.slice(start, end).split(/\0/g);
}
/**
* Split the input string into an array of words, separating numbers.
*/
function splitSeparateNumbers(value) {
const words = split(value);
for (let i = 0; i < words.length; i++) {
const word = words[i];
const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
if (match) {
const offset = match.index + (match[1] ?? match[2]).length;
words.splice(i, 1, word.slice(0, offset), word.slice(offset));
}
}
return words;
}
/**
* Convert a string to constant case (`FOO_BAR`).
*/
function constantCase(input, options) {
const [prefix, words, suffix] = splitPrefixSuffix(input, options);
return (prefix +
words.map(upperFactory(options?.locale)).join("_") +
suffix);
}
function upperFactory(locale) {
return locale === false
? (input) => input.toUpperCase()
: (input) => input.toLocaleUpperCase(locale);
}
function splitPrefixSuffix(input, options = {}) {
const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
let prefixIndex = 0;
let suffixIndex = input.length;
while (prefixIndex < input.length) {
const char = input.charAt(prefixIndex);
if (!prefixCharacters.includes(char))
break;
prefixIndex++;
}
while (suffixIndex > prefixIndex) {
const index = suffixIndex - 1;
const char = input.charAt(index);
if (!suffixCharacters.includes(char))
break;
suffixIndex = index;
}
return [
input.slice(0, prefixIndex),
splitFn(input.slice(prefixIndex, suffixIndex)),
input.slice(suffixIndex),
];
}
const getArgsFromKeysForFederation = utils.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 === graphql.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] = utils.mergeDeep(
[projectedData[fieldName], projectedKeyData],
void 0,
true,
true
);
}
} else {
projectedData[fieldName] = projectedKeyData;
}
}
} else if (selection.kind === graphql.Kind.INLINE_FRAGMENT) {
if (selection.typeCondition && projectedData["__typename"] != null && projectedData["__typename"] !== selection.typeCondition.name.value) {
continue;
}
Object.assign(
projectedData,
utils.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 = utils.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.trim().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 utils.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 = utils.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) {
let cacheKeyStr = "";
for (const key2 of keys) {
const keyVal = root[key2];
if (keyVal == null) {
continue;
} else if (typeof keyVal === "object") {
if (cacheKeyStr) {
cacheKeyStr += " ";
}
cacheKeyStr += JSON.stringify(keyVal);
} else {
if (cacheKeyStr) {
cacheKeyStr += " ";
}
cacheKeyStr += keyVal;
}
}
return cacheKeyStr;
};
}
return utils.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 utils.getDirectiveExtensions(obj)?.inaccessible?.length;
}
function filterInternalFieldsAndTypes(finalSchema) {
const internalTypeNameRegexp = /^(?:_Entity|_Any|_FieldSet|_Service|link|inaccessible|(?:link__|join__|core__)[\w]*)$/;
return utils.mapSchema(finalSchema, {
[utils.MapperKind.DIRECTIVE]: (directive) => {
if (internalTypeNameRegexp.test(directive.name)) {
return null;
}
return directive;
},
[utils.MapperKind.TYPE]: (type) => {
if (internalTypeNameRegexp.test(type.name) || hasInaccessible(type)) {
return null;
}
return type;
},
[utils.MapperKind.FIELD]: (fieldConfig) => {
if (hasInaccessible(fieldConfig)) {
return null;
}
return fieldConfig;
},
[utils.MapperKind.QUERY_ROOT_FIELD]: (fieldConfig, fieldName) => {
if (fieldName === "_entities" || hasInaccessible(fieldConfig)) {
return null;
}
return fieldConfig;
},
[utils.MapperKind.ENUM_VALUE]: (valueConfig) => {
if (hasInaccessible(valueConfig)) {
return null;
}
return valueConfig;
},
[utils.MapperKind.ARGUMENT]: (argConfig) => {
if (hasInaccessible(argConfig)) {
return null;
}
return argConfig;
}
});
}
function getNamedTypeNode(typeNode) {
if (typeNode.kind !== graphql.Kind.NAMED_TYPE) {
return getNamedTypeNode(typeNode.type);
}
return typeNode;
}
function getRngFromEnv() {
const rngEnv = globalThis.process?.env?.["PROGRESSIVE_OVERRIDE_RNG"];
if (rngEnv) {
const rngSeed = parseFloat(rngEnv);
if (!isNaN(rngSeed) && rngSeed >= 0 && rngSeed < 1) {
return rngSeed;
}
}
return void 0;
}
const progressiveOverridePossibilityHandler = (possibility, getRng) => {
const rng = getRngFromEnv() || (getRng ? getRng() : Math.random());
return rng < possibility;
};
function extractPercentageFromLabel(label) {
if (label.startsWith("percent(") && label.endsWith(")")) {
const regexp = /^percent\((\d+(?:\.\d+)?)\)$/;
const match = regexp.exec(label);
const percentageStr = match?.[1];
if (!percentageStr) {
throw new Error(`Expected a number in percent(x), got: ${label}`);
}
const parsedFloat = parseFloat(percentageStr);
if (isNaN(parsedFloat)) {
throw new Error(`Could not parse percentage value from label: ${label}`);
}
if (parsedFloat < 0 || parsedFloat > 100) {
throw new Error(
`Expected a percentage value between 0 and 100, got ${parsedFloat}`
);
}
return parsedFloat;
}
return void 0;
}
function parseJoinDirective(directiveNode) {
if (directiveNode.name.value !== "join__directive") {
return null;
}
if (directiveNode.arguments == null) {
return null;
}
let directiveName;
let directiveArgsAst;
const graphNames = [];
for (const argumentNode of directiveNode.arguments) {
if (argumentNode.name.value === "name" && argumentNode.value.kind === graphql.Kind.STRING) {
directiveName = argumentNode.value.value;
} else if (argumentNode.name.value === "args" && argumentNode.value.kind === graphql.Kind.OBJECT) {
directiveArgsAst = argumentNode.value;
} else if (argumentNode.name.value === "graphs" && argumentNode.value.kind === graphql.Kind.LIST) {
for (const graphNameNode of argumentNode.value.values) {
if (graphNameNode.kind === graphql.Kind.ENUM) {
graphNames.push(graphNameNode.value);
}
}
}
}
if (!directiveName || graphNames.length === 0) {
return null;
}
const args = directiveArgsAst ? directiveArgsAst.fields.map((field) => ({
kind: graphql.Kind.ARGUMENT,
name: field.name,
value: field.value
})) : void 0;
return {
graphNames,
directive: {
kind: graphql.Kind.DIRECTIVE,
name: { kind: graphql.Kind.NAME, value: directiveName },
arguments: args
}
};
}
function filterDirectivesByGraph(graphName, directives, filterDirectives) {
if (!directives) {
return directives;
}
const subgraphSpecificDirectives = [];
const filteredDirectives = directives.filter((directiveNode) => {
if (filterDirectives.includes(directiveNode.name.value)) {
return false;
}
if (directiveNode.name.value === "join__directive") {
const parsed = parseJoinDirective(directiveNode);
if (parsed?.graphNames.includes(graphName)) {
subgraphSpecificDirectives.push(parsed.directive);
}
return false;
}
return true;
});
const allDirectives = [
...filteredDirectives,
...subgraphSpecificDirectives
];
if (allDirectives.length === 0) {
return void 0;
}
return allDirectives;
}
function keySelectionIncludesAllFields(keyFieldSet, fieldDefinitionNodesOfSubgraph) {
let selectionSet;
try {
selectionSet = utils.parseSelectionSet(`{ ${keyFieldSet} }`);
} catch {
return false;
}
const topLevelFieldNames = /* @__PURE__ */ new Set();
for (const selection of selectionSet.selections) {
if (selection.kind === graphql.Kind.FIELD) {
topLevelFieldNames.add(selection.name.value);
}
}
return fieldDefinitionNodesOfSubgraph.every(
(fieldDefNode) => topLevelFieldNames.has(fieldDefNode.name.value)
);
}
function ensureSupergraphSDLAst(supergraphSdl) {
return typeof supergraphSdl === "string" ? graphql.parse(supergraphSdl, { noLocation: true }) : supergraphSdl;
}
const rootTypeMap = /* @__PURE__ */ new Map([
["Query", "query"],
["Mutation", "mutation"],
["Subscription", "subscription"]
]);
const memoizedASTPrint = utils.memoize1(graphql.print);
const memoizedTypePrint = utils.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();
const progressiveOverrideInfos = /* @__PURE__ */ new Map();
const progressiveOverrideInfosOpposite = /* @__PURE__ */ new Map();
const overrideLabels = /* @__PURE__ */ new Set();
const subgraphSchemaDefinitionDirectives = /* @__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 directiveImports = /* @__PURE__ */ new Map();
const directiveDefinitions = /* @__PURE__ */ new Map();
const inputDefinitions = /* @__PURE__ */ new Map();
const directiveExtraDefinitions = /* @__PURE__ */ new Map();
const subgraphNames = [];
graphql.visit(supergraphAst, {
EnumTypeDefinition(node) {
if (node.name.value === "join__Graph") {
node.values?.forEach((valueNode) => {
subgraphNames.push(valueNode.name.value);
});
}
inputDefinitions.set(node.name.value, node);
},
DirectiveDefinition(node) {
directiveDefinitions.set(node.name.value, node);
if (node.arguments?.length) {
const extraDefinitions2 = /* @__PURE__ */ new Set();
for (const arg of node.arguments) {
const argTypeNode = getNamedTypeNode(arg.type);
if (!specifiedTypeNames.includes(argTypeNode.name.value)) {
extraDefinitions2.add(argTypeNode.name.value);
}
}
if (extraDefinitions2.size > 0) {
directiveExtraDefinitions.set(node.name.value, extraDefinitions2);
}
}
},
ScalarTypeDefinition(node) {
inputDefinitions.set(node.name.value, node);
},
InputObjectTypeDefinition(node) {
inputDefinitions.set(node.name.value, node);
},
Directive(node) {
if (node.name.value === "link") {
const importedDirectives = node.arguments?.find(
(arg) => arg.name.value === "import"
);
const urlArg = node.arguments?.find((arg) => arg.name.value === "url");
const urlStr = urlArg?.value?.kind === graphql.Kind.STRING ? urlArg.value.value : void 0;
if (urlStr && importedDirectives?.value?.kind === graphql.Kind.LIST) {
for (const importedDirective of importedDirectives.value.values) {
if (importedDirective.kind === graphql.Kind.STRING) {
directiveImports.set(importedDirective.value, { url: urlStr });
} else if (importedDirective.kind === graphql.Kind.OBJECT) {
const nameField = importedDirective.fields.find(
(field) => field.name.value === "name"
);
const asField = importedDirective.fields.find(
(field) => field.name.value === "as"
);
if (nameField?.value?.kind === graphql.Kind.STRING && urlStr) {
const originalName = nameField.value.value;
const localName = asField?.value?.kind === graphql.Kind.STRING ? asField.value.value : originalName;
directiveImports.set(localName, {
url: urlStr,
originalName: localName !== originalName ? originalName : void 0
});
}
}
}
}
}
}
});
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: graphql.Kind.DIRECTIVE,
name: {
kind: graphql.Kind.NAME,
value: "join__type"
},
arguments: [
{
kind: graphql.Kind.ARGUMENT,
name: {
kind: graphql.Kind.NAME,
value: "graph"
},
value: {
kind: graphql.Kind.ENUM,
value: subgraphName
}
}
]
}))
];
}
let isOrphan = true;
const fieldDefinitionNodesByGraphName = /* @__PURE__ */ new Map();
typeNode.directives?.forEach((directiveNode) => {
if (typeNode.kind === graphql.Kind.OBJECT_TYPE_DEFINITION) {
if (directiveNode.name.value === "join__owner") {
directiveNode.arguments?.forEach((argumentNode) => {
if (argumentNode.name.value === "graph" && argumentNode.value?.kind === graphql.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 === graphql.Kind.ENUM) {
const graphName = joinTypeGraphArgNode.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 === graphql.Kind.ENUM && joinFieldGraphArgNode.value.value === graphName) {
notInSubgraph = false;
const isExternal = joinFieldDirectiveNode.arguments?.some(
(argumentNode) => argumentNode.name.value === "external" && argumentNode.value?.kind === graphql.Kind.BOOLEAN && argumentNode.value.value === true
);
const isOverridden = joinFieldDirectiveNode.arguments?.some(
(argumentNode) => argumentNode.name.value === "usedOverridden" && argumentNode.value?.kind === graphql.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 === graphql.Kind.STRING ? graphql.parseType(typeArg.value.value) : fieldNode.type;
fieldDefinitionNodesOfSubgraph.push({
...fieldNode,
type: typeNode2,
directives: filterDirectivesByGraph(
graphName,
fieldNode.directives,
["join__field"]
)
});
}
const overrideFromArg = joinFieldDirectiveNode.arguments?.find(
(argumentNode) => argumentNode.name.value === "override"
);
let overrideFromSubgraph = void 0;
if (overrideFromArg?.value?.kind === graphql.Kind.STRING) {
overrideFromSubgraph = overrideFromArg.value.value;
}
const overrideLabelArg = joinFieldDirectiveNode.arguments?.find(
(argumentNode) => argumentNode.name.value === "overrideLabel"
);
let overrideLabel = void 0;
if (overrideLabelArg?.value?.kind === graphql.Kind.STRING) {
overrideLabel = overrideLabelArg.value.value;
}
if (overrideFromSubgraph && overrideLabel != null) {
let subgraphPossibilities = progressiveOverrideInfos.get(graphName);
if (!subgraphPossibilities) {
subgraphPossibilities = /* @__PURE__ */ new Map();
progressiveOverrideInfos.set(
graphName,
subgraphPossibilities
);
}
let existingInfos = subgraphPossibilities.get(
typeNode.name.value
);
if (!existingInfos) {
existingInfos = [];
subgraphPossibilities.set(
typeNode.name.value,
existingInfos
);
}
existingInfos.push({
field: fieldNode.name.value,
from: overrideFromSubgraph,
label: overrideLabel
});
if (!overrideLabel.startsWith("percent(")) {
overrideLabels.add(overrideLabel);
}
const oppositeKey = constantCase(overrideFromSubgraph);
let oppositeInfos = progressiveOverrideInfosOpposite.get(oppositeKey);
if (!oppositeInfos) {
oppositeInfos = /* @__PURE__ */ new Map();
progressiveOverrideInfosOpposite.set(
oppositeKey,
oppositeInfos
);
}
let existingOppositeInfos = oppositeInfos.get(
typeNode.name.value
);
if (!existingOppositeInfos) {
existingOppositeInfos = [];
oppositeInfos.set(
typeNode.name.value,
existingOppositeInfos
);
}
existingOppositeInfos.push({
field: fieldNode.name.value,
to: graphName,
label: overrideLabel
});
}
const providedExtraField = joinFieldDirectiveNode.arguments?.find(
(argumentNode) => argumentNode.name.value === "provides"
);
if (providedExtraField?.value?.kind === graphql.Kind.STRING) {
let handleSelection2 = function(fieldNodeTypeName2, selection) {
switch (selection.kind) {
case graphql.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 graphql.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 = utils.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 === graphql.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
);
}
}
});
const keys = typeNameKeysBySubgraphMap.get(graphName)?.get(typeNode.name.value);
const keysArr = keys && Array.from(keys);
if (!joinFieldDirectives?.length) {
fieldDefinitionNodesOfSubgraph.push({
...fieldNode,
directives: filterDirectivesByGraph(
graphName,
fieldNode.directives,
["join__field"]
)
});
} else if (notInSubgraph && keysArr?.some(
(key) => key.split(" ").includes(fieldNode.name.value)
)) {
fieldDefinitionNodesOfSubgraph.push({
...fieldNode,
directives: filterDirectivesByGraph(
graphName,
fieldNode.directives,
["join__field"]
)
});
}
});
fieldDefinitionNodesByGraphName.set(
graphName,
fieldDefinitionNodesOfSubgraph
);
if (typeNode.kind === graphql.Kind.OBJECT_TYPE_DEFINITION || typeNode.kind === graphql.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 === graphql.Kind.BOOLEAN && argumentNode.value.value === false
);
if (keyArgumentNode?.value?.kind === graphql.Kind.STRING) {
const keyArgVal = keyArgumentNode.value.value;
if (isResolvable) {
if (typeNode.kind !== graphql.Kind.OBJECT_TYPE_DEFINITION || !keySelectionIncludesAllFields(
keyArgVal,
fieldDefinitionNodesOfSubgraph
)) {
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 = /* @__PURE__ */ new Set();
typeNameKeysMap.set(typeNode.name.value, keys);
}
keys.add(keyArgVal);
}
}
}
}
}
}
});
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 === graphql.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 === graphql.Kind.ENUM && argumentNode.value.value === graphName;
})) {
interfaces.push(interfaceNode);
}
});
if (typeNode.name.value === "Query") {
fieldDefinitionNodesOfSubgraph.push(entitiesFieldDefinitionNode);
}
const objectTypedDefNodeForSubgraph = {
...typeNode,
interfaces,
fields: fieldDefinitionNodesOfSubgraph,
directives: filterDirectivesByGraph(graphName, typeNode.directives, [
"join__type",
"join__owner",
"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);
}
}
graphql.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 === graphql.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: filterDirectivesByGraph(
graphName,
node.directives,
["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 === graphql.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: filterDirectivesByGraph(
graphName,
node.directives,
["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 === graphql.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 === graphql.Kind.ENUM && graphArgumentNode.value.value === graphName && memberArgumentNode?.value?.kind === graphql.Kind.STRING) {
unionMembers.push({
kind: graphql.Kind.NAMED_TYPE,
name: {
kind: graphql.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: filterDirectivesByGraph(
graphName,
node.directives,
["join__type", "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 === graphql.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 === graphql.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 === graphql.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: filterDirectivesByGraph(
graphName,
node.directives,
["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
});
for (const definition of supergraphAst.definitions) {
if (definition.kind === graphql.Kind.SCHEMA_DEFINITION) {
if (definition.directives) {
for (const directiveNode of definition.directives) {
const parsed = parseJoinDirective(directiveNode);
if (parsed) {
for (const graphName of parsed.graphNames) {
let subgraphSchemaLevelDefs = subgraphSchemaDefinitionDirectives.get(graphName);
if (!subgraphSchemaLevelDefs) {
subgraphSchemaLevelDefs = [];
subgraphSchemaDefinitionDirectives.set(
graphName,
subgraphSchemaLevelDefs
);
}
subgraphSchemaLevelDefs.push(parsed.directive);
}
}
}
}
} else {
continue;
}
}
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 === graphql.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 graphql.visit(node, {
[graphql.Kind.OBJECT_TYPE_DEFINITION]: visitObjectAndInterfaceDefs,
[graphql.Kind.OBJECT_TYPE_EXTENSION]: visitObjectAndInterfaceDefs,
[graphql.Kind.INTERFACE_TYPE_DEFINITION]: visitObjectAndInterfaceDefs,
[graphql.Kind.INTERFACE_TYPE_EXTENSION]: visitObjectAndInterfaceDefs,
[graphql.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
};
},
[graphql.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
};
},
[graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION](node2) {
const fields = visitFieldDefs(node2.fields);
return {
...node2,
fields
};
},
[graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION](node2) {
const fields = visitFieldDefs(node2.fields);
return {
...node2,
fields
};
}
});
}, collectTransitiveDeps2 = function(typeNames, into) {
const queue = [...typeNames];
while (queue.length > 0) {
const typeName = queue.shift();
const def = inputDefinitions.get(typeName);
if (!def || into.has(def)) continue;
into.add(def);
if (def.kind === graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION && def.fields) {
for (const field of def.fields) {
const fieldTypeName = getNamedTypeNode(field.type).name.value;
if (!specifiedTypeNames.includes(fieldTypeName)) {
queue.push(fieldTypeName);
}
}
}
}
};
const mergeConfig = {};
const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName);
const unionTypeNodes = [];
const pendingConflictSubschemaConfigs = [];
if (typeNameKeyMap) {
const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName);
for (const [typeName, keys] of typeNameKeyMap) {
let getMergedTypeConfigFromKey2 = function(key, keysExtraKeys = extraKeys) {
return {
selectionSet: `{ ${key} }`,
argsFromKeys: getArgsFromKeysForFederation,
key: getKeyFnForFederation(typeName, [key, ...keysExtraKeys]),
fieldName: `_entities`,
dataLoaderOptions: {
cacheKeyFn: getCacheKeyFnFromKey(key),
...opts.batchDelegateOptions || {}
}
};
};
const mergedTypeConfig = mergeConfig[typeName] = {};
const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName);