@graphql-tools/federation
Version:
Useful tools to create and manipulate GraphQL schemas.
2,712 lines • 99 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 registerProvidedSelectionForField2 = function(parentTypeName, fieldName, selectionSet) {
let fieldMap = typeNameFieldProvidedSelectionMap.get(parentTypeName);
if (!fieldMap) {
fieldMap = /* @__PURE__ */ new Map();
typeNameFieldProvidedSelectionMap.set(
parentTypeName,
fieldMap
);
}
const existing = fieldMap.get(fieldName);
if (existing) {
fieldMap.set(fieldName, {
kind: graphql.Kind.SELECTION_SET,
selections: [
...existing.selections,
...selectionSet.selections
]
});
} else {
fieldMap.set(fieldName, selectionSet);
}
}, 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) {
registerProvidedSelectionForField2(
fieldNodeTypeName2,
selection.name.value,
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
);
}
registerProvidedSelectionForField2(
typeNode.name.value,
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);
const extraKeys = /* @__PURE__ */ new Set();
if (fieldsKeyMap) {
let hashStringToAlphanumeric2 = function(str) {
let hash = 5381;
for (let index = 0; index < str.length; index++) {
hash = Math.imul(hash, 33) ^ str.charCodeAt(index) | 0;
}
return (hash >>> 0).toString(36);
}, aliasFieldsWithArgs2 = function(selectionSetNode) {
for (const selection of selectionSetNode.selections) {
if (selection.kind === graphql.Kind.FIELD && selection.arguments?.length) {
const normalizedArgs = [...selection.arguments].sort((a, b) => a.name.value.localeCompare(b.name.value)).map(
(arg) => `${arg.name.value}:${memoizedASTPrint(arg.value)}`
).join(",");
const argsHash = hashStringToAlphanumeric2(normalizedArgs);
selection.alias = {
kind: graphql.Kind.NAME,
value: "_" + selection.name.value + "_" + argsHash
};
}
if ("selectionSet" in selection && selection.selectionSet) {
aliasFieldsWithArgs2(selection.selectionSet);
}
}
};
const mainFieldsConfig = mergedTypeConfig.fields = {};
const groups = [
{
extraKeys,
fieldsConfig: mainFieldsConfig,
aliasedFields: /* @__PURE__ */ new Map()
}
];
for (const [fieldName, fieldNameKey] of fieldsKeyMap) {
const selectionSetNode = utils.parseSelectionSet(`{${fieldNameKey}}`);
aliasFieldsWithArgs2(selectionSetNode);
const selectionSet = graphql.print(selectionSetNode).replaceAll(/\n/g, " ").replaceAll(/\s+/g, " ");
const extraKey = (
// remove first and last characters (curly braces)
selectionSet.slice(1, -1)
);
const fieldAliasedNames = /* @__PURE__ */ new Map();
for (const sel of selectionSetNode.selections) {
if (sel.kind === graphql.Kind.FIELD && sel.alias) {
fieldAliasedNames.set(sel.name.value, sel.alias.value);
}
}
let targetGroup;
for (const group of groups) {
let hasConflict = false;
for (const [origName, aliasName] of fieldAliasedNames) {
const existingAlias = group.aliasedFields.get(origName);
if (existingAlias !== void 0 && existingAlias !== aliasName) {
hasConflict = true;
break;
}
}
if (!hasConflict) {
targetGroup = group;
break;
}
}
if (!targetGroup) {
const newExtraKeys = /* @__PURE__ */ new Set();
const newFieldsConfig = {};
targetGroup = {
extraKeys: newExtraKeys,
fieldsConfig: newFieldsConfig,
aliasedFields: /* @__PURE__ */ new Map()
};
groups.push(targetGroup);
}
targetGroup.extraKeys.add(extraKey);
targetGroup.fieldsConfig[fieldName] = {
selectionSet,
computed: true
};
for (const [origName, aliasName] of fieldAliasedNames) {
targetGroup.aliasedFields.set(origName, aliasName);
}
}
for (const group of groups.slice(1)) {
const additionalMergeTypeConfig = {
fields: group.fieldsConfig
};
if (typeNameCanonicalMap.get(typeName) === subgraphName) {
additionalMergeTypeConfig.canonical = true;
}
pendingConflictSubschemaConfigs.push({
typeName,
// Store closure-captured values for later SubschemaConfig creation
// by borrowing getMergedTypeConfigFromKey with the group's extraKeys.
mergeTypeConfig: additionalMergeTypeConfig,
groupExtraKeys: group.extraKeys
});
}
}
if (typeNameCanonicalMap.get(typeName) === subgraphName) {
mergedTypeConfig.canonical = true;
}
const keysArr = Array.from(keys);
if (keysArr.length === 1 && keysArr[0]) {
Object.assign(
mergedTypeConfig,
getMergedTypeConfigFromKey2(keysArr[0])
);
}
if (keysArr.length > 1) {
const entryPoints = keysArr.map(
(key) => getMergedTypeConfigFromKey2(key)
);
mergedTypeConfig.entryPoints = entryPoints;
}
for (const pending of pendingConflictSubschemaConfigs) {
if (pending.typeName === typeName && !("selectionSet" in pending.mergeTypeConfig) && !("entryPoints" in pending.mergeTypeConfig)) {
const groupExtraKeys = pending.groupExtraKeys;
if (keysArr.length === 1 && keysArr[0]) {
Object.assign(
pending.mergeTypeConfig,
getMergedTypeConfigFromKey2(keysArr[0], groupExtraKeys)
);
} else if (keysArr.length > 1) {
pending.mergeTypeConfig.entryPoints = keysArr.map(
(key) => getMergedTypeConfigFromKey2(key, groupExtraKeys)
);
}
opts.onMergedTypeConfig?.(
pending.typeName,
pending.mergeTypeConfig
);
}
}
unionTypeNodes.push({
kind: graphql.Kind.NAMED_TYPE,
name: {
kind: graphql.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: graphql.Kind.NAME,
value: "_Entity"
},
kind: graphql.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 === graphql.Kind.INTERFACE_TYPE_DEFINITION) {
let isOrphan = true;
for (const definitionNode of supergraphAst.definitions) {
if (definitionNode.kind === graphql.Kind.OBJECT_TYPE_DEFINITION && definitionNode.interfaces?.some(
(interfaceNode) => interfaceNode.name.value === interfaceInSubgraph.name.value
)) {
isOrphan = false;
}
}
if (isOrphan) {
interfaceInSubgraph.kind = graphql.Kind.OBJECT_TYPE_DEFINITION;
}
}
}
let schema;
let schemaAst = {
kind: graphql.Kind.DOCUMENT,
definitions: [
...extendedSubgraphTypes,
entitiesUnionTypeDefinitionNode,
anyTypeDefinitionNode
]
};
const linkImports = /* @__PURE__ */ new Map();
const extraDefinitions2 = /* @__PURE__ */ new Set();
graphql.visit(schemaAst, {
Directive(node) {
const directiveName = node.name.value;
const directiveDefinition = directiveDefinitions.get(directiveName);
if (directiveDefinition && !extraDefinitions2.has(directiveDefinition)) {
extraDefinitions2.add(directiveDefinition);
const extraDefinitionsForDirective = directiveExtraDefinitions.get(directiveName);
if (extraDefinitionsForDirective) {
collectTransitiveDeps2(
extraDefinitionsForDirective,
extraDefinitions2
);
}
const directiveNameInImport = `@${directiveName}`;
const directiveImport = directiveImports.get(directiveNameInImport);
if (directiveImport) {
let urlImports = linkImports.get(directiveImport.url);
if (!urlImports) {
urlImports = [];
linkImports.set(directiveImport.url, urlImports);
}
urlImports.push({
name: directiveImport.originalName || directiveNameInImport,
as: directiveNameInImport
});
}
}
}
});
const linkDirectiveNodes = [];
for (const [url, linkImportsForUrl] of linkImports) {
const urlArg = {
kind: graphql.Kind.ARGUMENT,
name: {
kind: graphql.Kind.NAME,
value: "url"
},
value: {
kind: graphql.Kind.STRING,
value: url
}
};
const importArg = {
kind: graphql.Kind.ARGUMENT,
name: {
kind: graphql.Kind.NAME,
value: "import"
},
value: {
kind: graphql.Kind.LIST,
values: linkImportsForUrl.map((linkImport) => {
if (linkImport.name === linkImport.as) {
return {
kind: graphql.Kind.STRING,
value: linkImport.name
};
} else {
return {
kind: graphql.Kind.OBJECT,
fields: [
{
kind: graphql.Kind.OBJECT_FIELD,
name: {
kind: graphql.Kind.NAME,
value: "name"
},
value: {
kind: graphql.Kind.STRING,
value: linkImport.name
}
},
{
kind: graphql.Kind.OBJECT_FIELD,
name: {
kind: graphql.Kind.NAME,
value: "as"
},
value: {
kind: graphql.Kind.STRING,
value: linkImport.as
}
}
]
};
}
})
}
};
linkDirectiveNodes.push({
kind: graphql.Kind.DIRECTIVE,
name: {
kind: graphql.Kind.NAME,
value: "link"
},
arguments: [urlArg, importArg]
});
}
if (linkDirectiveNodes.length > 0) {
const linkDirectiveDef = directiveDefinitions.get("link");
if (linkDirectiveDef) {
extraDefinitions2.add(linkDirectiveDef);
}
const linkDirectiveExtraDefs = directiveExtraDefinitions.get("link");
if (linkDirectiveExtraDefs) {
collectTransitiveDeps2(linkDirectiveExtraDefs, extraDefinitions2);
}
extraDefinitions2.add({
kind: graphql.Kind.SCHEMA_EXTENSION,
directives: [...linkDirectiveNodes]
});
}
const schemaDefDirectives = subgraphSchemaDefinitionDirectives.get(subgraphName);
if (schemaDefDirectives) {
extraDefinitions2.add({
kind: graphql.Kind.SCHEMA_EXTENSION,
directives: schemaDefDirectives
});
}
schemaAst = {
...schemaAst,
definitions: [...extraDefinitions2, ...schemaAst.definitions]
};
if (opts.onSubgraphAST) {
schemaAst = opts.onSubgraphAST(subgraphName, schemaAst);
}
try {
schema = graphql.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$1 = executorHttp.buildHTTPExecutor({
endpoint,
...httpExecutorOpts
});
if (globalThis.process?.env?.["DEBUG"]) {
const origExecutor = executor$1;
executor$1 = 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 promiseHelpers.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 === graphql.Kind.INTERFACE_TYPE_DEFINITION
)) {
let createUnresolvableError2 = function(fieldName, fieldNode) {
return utils.createGraphQLError(
`Was not able to find any options for ${fieldName}: This shouldn't have happened.`,
{
extensions: {
[executor.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 = delegate.getTypeInfo(schema);
const visitorKeys = {
Document: ["definitions"],
OperationDefinition: ["selectionSet"],
SelectionSet: ["selections"],
Field: ["selectionSet"],
InlineFragment: ["selectionSet"],
FragmentDefinition: ["selectionSet"]
};
const unresolvableIfaceFieldCheckerMap = /* @__PURE__ */ new Map();
transforms.push({
transformRequest(request) {
return {
...request,
document: graphql.visit(
request.document,
graphql.visitWithTypeInfo(typeInfo, {
// To avoid resolving unresolvable interface fields
[graphql.Kind.FIELD](fieldNode) {
const fieldName = fieldNode.name.value;
if (fieldName !== "__typename") {
const parentType = typeInfo.getParentType();
if (graphql.isInterfaceType(parentType)) {
let unresolvableIfaceFieldChecker = unresolvableIfaceFieldCheckerMap.get(parentType.name);
if (unresolvableIfaceFieldChecker == null) {
unresolvableIfaceFieldChecker = createIfaceFieldChecker2(parentType);
unresolvableIfaceFieldCheckerMap.set(
parentType.name,
unresolvableIfaceFieldChecker
);
}
if (!unresolvableIfaceFieldChecker(fieldName)) {
throw createUnresolvableError2(fieldName, fieldNode);
}
}
}
}
}),
visitorKeys
)
};
}
});
}
const progressiveOverrideInfosForSubgraph = progressiveOverrideInfos.get(subgraphName);
if (progressiveOverrideInfosForSubgraph != null) {
for (const [
typeName,
fieldInfos
] of progressiveOverrideInfosForSubgraph) {
let mergedConfig = mergeConfig[typeName];
if (!mergedConfig) {
mergedConfig = mergeConfig[typeName] = {};
}
for (const fieldInfo of fieldInfos) {
let fieldsConfig = mergedConfig.fields;
if (!fieldsConfig) {
fieldsConfig = mergedConfig.fields = {};
}
let fieldConfig = fieldsConfig[fieldInfo.field];
if (!fieldConfig) {
fieldConfig = fieldsConfig[fieldInfo.field] = {};
}
const label = fieldInfo.label;
const percent = extractPercentageFromLabel(label);
if (percent != null) {
const possibility = percent / 100;
fieldConfig.override = () => progressiveOverridePossibilityHandler(possibility, opts.getRng);
} else if (opts.handleProgressiveOverride) {
const progressiveOverrideHandler = opts.handleProgressiveOverride;
fieldConfig.override = (context, info) => progressiveOverrideHandler(label, context, info);
}
}
}
}
const progressiveOverrideOppositeInfosForSubgraph = progressiveOverrideInfosOpposite.get(constantCase(subgraphName));
if (progressiveOverrideOppositeInfosForSubgraph != null) {
for (const [
typeName,
fieldInfos
] of progressiveOverrideOppositeInfosForSubgraph) {
let mergedConfig = mergeConfig[typeName];
if (!mergedConfig) {
mergedConfig = mergeConfig[typeName] = {};
}
for (const fieldInfo of fieldInfos) {
let fieldsConfig = mergedConfig.fields;
if (!fieldsConfig) {
fieldsConfig = mergedConfig.fields = {};
}
let fieldConfig = fieldsConfig[fieldInfo.field];
if (!fieldConfig) {
fieldConfig = fieldsConfig[fieldInfo.field] = {};
}
const label = fieldInfo.label;
const percent = extractPercentageFromLabel(label);
if (percent != null) {
const possibility = percent / 100;
fieldConfig.override = () => !progressiveOverridePossibilityHandler(possibility, opts.getRng);
} else if (opts.handleProgressiveOverride) {
const progressiveOverrideHandler = opts.handleProgressiveOverride;
fieldConfig.override = (context, info) => !progressiveOverrideHandler(label, context, info);
}
}
}
}
let mainSchema = schema;
const additionalGroupFieldsByType = /* @__PURE__ */ new Map();
for (const {
typeName,
mergeTypeConfig
} of pendingConflictSubschemaConfigs) {
for (const [fieldName, fieldConfig] of Object.entries(
mergeTypeConfig.fields ?? {}
)) {
if (!fieldConfig?.computed) continue;
let set = additionalGroupFieldsByType.get(typeName);
if (!set) {
set = /* @__PURE__ */ new Set();
additionalGroupFieldsByType.set(typeName, set);
}
set.add(fieldName);
}
}
if (additionalGroupFieldsByType.size > 0) {
mainSchema = utils.mapSchema(schema, {
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
if (additionalGroupFieldsByType.get(typeName)?.has(fieldName)) {
return null;
}
return fieldConfig;
}
});
}
const allComputedFieldsByType = new Map(
Array.from(additionalGroupFieldsByType, ([t, s]) => [t, new Set(s)])
);
for (const [typeName, typeConfig] of Object.entries(mergeConfig)) {
if (!typeConfig?.fields) continue;
for (const [fieldName, fieldConfig] of Object.entries(
typeConfig.fields
)) {
if (!fieldConfig?.computed) continue;
let set = allComputedFieldsByType.get(typeName);
if (!set) {
set = /* @__PURE__ */ new Set();
allComputedFieldsByType.set(typeName, set);
}
set.add(fieldName);
}
}
const subschemaConfig = {
name: subgraphName,
endpoint,
schema: mainSchema,
executor: executor$1,
merge: mergeConfig,
transforms,
batch: opts.batch,
batchingOptions: opts.batchingOptions
};
subschemas.push(subschemaConfig);
for (const {
typeName,
mergeTypeConfig
} of pendingConflictSubschemaConfigs) {
const groupOwnFields = new Set(
Object.entries(mergeTypeConfig.fields ?? {}).filter(([, fc]) => fc?.computed).map(([fn]) => fn)
);
const allComputedForType = allComputedFieldsByType.get(typeName) ?? /* @__PURE__ */ new Set();
const fieldsToRemove = new Set(
[...allComputedForType].filter((f) => !groupOwnFields.has(f))
);
const groupSchema = fieldsToRemove.size > 0 || allComputedFieldsByType.size > 0 ? utils.mapSchema(schema, {
[utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, type) => {
if (type === typeName && fieldsToRemove.has(fieldName)) {
return null;
}
if (type !== typeName && allComputedFieldsByType.get(type)?.has(fieldName)) {
return null;
}
return fieldConfig;
}
}) : schema;
subschemas.push({
name: subgraphName,
endpoint,
schema: groupSchema,
executor: executor$1,
merge: { [typeName]: mergeTypeConfig },
transforms,
batch: opts.batch,
batchingOptions: opts.batchingOptions
});
}
}
const defaultMerger = stitch.getDefaultFieldConfigMerger(true);
const fieldConfigMerger = function(candidates) {
if (candidates.length === 1 || candidates.some((candidate) => candidate.fieldName === "_entities")) {
if (candidates[0]) {
return candidates[0].fieldConfig;
}
}
let operationType;
for (const candidate of candidates) {
const candidateOperationType = rootTypeMap.get(candidate.type.name);
if (candidateOperationType) {
operationType = candidateOperationType;
}
}
if (operationType) {
const defaultMergedField = defaultMerger(candidates);
const mergedResolver = function mergedResolver2(_root, args, context, info) {
const filteredCandidates2 = candidates.filter((candidate) => {
const subschemaConfig = candidate.subschema;
const overrideHandler = subschemaConfig?.merge?.[candidate.type.name]?.fields?.[candidate.fieldName]?.override;
if (overrideHandler) {
return delegate.handleOverrideByDelegation(info, context, overrideHandler);
}
return true;
});
const originalSelectionSet = {
kind: graphql.Kind.SELECTION_SET,
selections: info.fieldNodes
};
const candidatesReversed = filteredCandidates2.toReversed ? filteredCandidates2.toReversed() : [...filteredCandidates2].reverse();
let currentSubschema;
let currentScore = Infinity;
let currentUnavailableSelectionSet;
let currentFriendSubschemas;
let currentAvailableSelectionSet;
for (const candidate of candidatesReversed) {
if (candidate.transformedSubschema) {
const unavailableFields = delegate.extractUnavailableFieldsFromSelectionSet(
candidate.transformedSubschema.transformedSchema,
candidate.type,
originalSelectionSet,
() => true,
info.fragments
);
const score = stitch.calculateSelectionScore(
unavailableFields,
info.fragments
);
if (score < currentScore) {
currentScore = score;
currentSubschema = candidate.transformedSubschema;
currentFriendSubschemas = /* @__PURE__ */ new Map();
currentUnavailableSelectionSet = {
kind: graphql.Kind.SELECTION_SET,
selections: unavailableFields
};
currentAvailableSelectionSet = delegate.subtractSelectionSets(
originalSelectionSet,
currentUnavailableSelectionSet
);
for (const friendCandidate of filteredCandidates2) {
if (friendCandidate === candidate || !friendCandidate.transformedSubschema || !currentUnavailableSelectionSet.selections.length) {
continue;
}
const unavailableFieldsInFriend = delegate.extractUnavailableFieldsFromSelectionSet(
friendCandidate.transformedSubschema.transformedSchema,
friendCandidate.type,
currentUnavailableSelectionSet,
() => true,
info.fragments
);
const friendScore = stitch.calculateSelectionScore(
unavailableFieldsInFriend,
info.fragments
);
if (friendScore < score) {
const unavailableInFriendSelectionSet = {
kind: graphql.Kind.SELECTION_SET,
selections: unavailableFieldsInFriend
};
const subschemaSelectionSet = delegate.subtractSelectionSets(
currentUnavailableSelectionSet,
unavailableInFriendSelectionSet
);
if (subschemaSelectionSet.selections.length) {
currentFriendSubschemas.set(
friendCandidate.transformedSubschema,
subschemaSelectionSet
);
currentUnavailableSelectionSet = unavailableInFriendSelectionSet;
}
}
}
}
}
}
if (!currentSubschema) {
throw new Error("Could not determine subschema");
}
const jobs = [];
let hasPromise = false;
const mainJob = delegate.delegateToSchema({
schema: currentSubschema,
operation: rootTypeMap.get(info.parentType.name) || "query",
context,
args,
info: currentFriendSubschemas?.size ? {
...info,
fieldNodes: [
...currentAvailableSelectionSet?.selections || [],
...currentUnavailableSelectionSet?.selections || []
]
} : info
});
if (operationType !== "query") {
return mainJob;
}
if (promiseHelpers.isPromise(mainJob)) {
hasPromise = true;
}
jobs.push(mainJob);
if (currentFriendSubschemas?.size) {
for (const [
friendSubschema,
friendSelectionSet
] of currentFriendSubschemas) {
const friendJob = delegate.delegateToSchema({
schema: friendSubschema,
operation: rootTypeMap.get(info.parentType.name) || "query",
context,
args,
info: {
...info,
fieldNodes: friendSelectionSet.selections
},
skipTypeMerging: true
});
if (promiseHelpers.isPromise(friendJob)) {
hasPromise = true;
}
jobs.push(friendJob);
}
}
if (jobs.length === 1) {
return jobs[0];
}
function getFieldNames() {
const fieldNames = /* @__PURE__ */ new Set();
originalSelectionSet?.selections.forEach((selection) => {
if (selection.kind === graphql.Kind.FIELD && selection.name.value === info.fieldName) {
selection.selectionSet?.selections.forEach((selection2) => {
if (selection2.kind === graphql.Kind.FIELD) {
fieldNames.add(selection2.name.value);
}
});
}
});
return fieldNames;
}
if (hasPromise) {
return Promise.all(jobs).then(
(results) => mergeResults(results, getFieldNames)
);
}
return mergeResults(jobs, getFieldNames);
};
if (operationType === "subscription") {
return {
...defaultMergedField,
subscribe: mergedResolver,
resolve: function identityFn(payload) {
return payload;
}
};
}
return {
...defaultMergedField,
resolve: mergedResolver
};
}
const filteredCandidates = candidates.filter((candidate) => {
const fieldASTMap = typeFieldASTMap.get(candidate.type.name);
if (fieldASTMap) {
const fieldAST = fieldASTMap.get(candidate.fieldName);
if (fieldAST) {
const typeNodeInAST = memoizedASTPrint(fieldAST.type);
const typeNodeInCandidate = memoizedTypePrint(
candidate.fieldConfig.type
);
return typeNodeInAST === typeNodeInCandidate;
}
}
return false;
});
return defaultMerger(
filteredCandidates.length ? filteredCandidates : candidates
);
};
function doesFieldExistInSupergraph(typeName, fieldName) {
for (const subschemaConfig of subschemas) {
const type = subschemaConfig.schema.getType(typeName);
if (type && "getFields" in type) {
if (type.getFields()[fieldName]) {
return true;
}
}
}
return false;
}
function getTypeInSupergraph(typeName) {
for (const subschemaConfig of subschemas) {
const type = subschemaConfig.schema.getType(typeName);
if (type) {
return type;
}
}
return void 0;
}
const extraDefinitions = [];
for (const definition of supergraphAst.definitions) {
if ("name" in definition && definition.name && definition.kind !== graphql.Kind.DIRECTIVE_DEFINITION) {
const typeName = definition.name.value;
const typeInSchema = getTypeInSupergraph(typeName);
if (!typeInSchema) {
extraDefinitions.push(definition);
} else if ("fields" in definition && definition.fields) {
const extraFields = [];
for (const field of definition.fields) {
if (!doesFieldExistInSupergraph(typeName, field.name.value)) {
extraFields.push(field);
}
}
if (extraFields.length) {
let definitionKind;
if (graphql.isObjectType(typeInSchema)) {
definitionKind = graphql.Kind.OBJECT_TYPE_DEFINITION;
} else if (graphql.isInterfaceType(typeInSchema)) {
definitionKind = graphql.Kind.INTERFACE_TYPE_DEFINITION;
} else if (graphql.isInputObjectType(typeInSchema)) {
definitionKind = graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION;
}
if (definitionKind) {
extraDefinitions.push({
kind: definitionKind,
name: definition.name,
fields: extraFields
// `fields` are different in input object types and regular types
});
}
}
}
} else if (definition.kind === graphql.Kind.DIRECTIVE_DEFINITION && !definition.name.value.startsWith("join__") && !definition.name.value.startsWith("core")) {
extraDefinitions.push(definition);
}
}
const additionalTypeDefs = {
kind: graphql.Kind.DOCUMENT,
definitions: extraDefinitions
};
if (opts.onSubschemaConfig) {
for (const subschema of subschemas) {
opts.onSubschemaConfig(subschema);
}
}
let schemaExtensions;
if (overrideLabels.size) {
schemaExtensions = {
schemaExtensions: {
overrideLabels
},
types: {}
};
}
return {
subschemas,
typeDefs: additionalTypeDefs,
assumeValid: true,
assumeValidSDL: true,
typeMergingOptions: {
useNonNullableFieldOnConflict: true,
validationSettings: {
validationLevel: stitch.ValidationLevel.Off
},
fieldConfigMerger
},
schemaExtensions
};
}
function getStitchedSchemaFromSupergraphSdl(opts) {
const stitchSchemasOpts = getStitchingOptionsFromSupergraphSdl(opts);
opts.onStitchingOptions?.(stitchSchemasOpts);
let supergraphSchema = stitch.stitchSchemas(stitchSchemasOpts);
supergraphSchema = filterInternalFieldsAndTypes(supergraphSchema);
if (opts.onStitchedSchema) {
supergraphSchema = opts.onStitchedSchema(supergraphSchema) || supergraphSchema;
}
return supergraphSchema;
}
const anyTypeDefinitionNode = {
name: {
kind: graphql.Kind.NAME,
value: "_Any"
},
kind: graphql.Kind.SCALAR_TYPE_DEFINITION
};
const entitiesFieldDefinitionNode = {
kind: graphql.Kind.FIELD_DEFINITION,
name: {
kind: graphql.Kind.NAME,
value: "_entities"
},
type: {
kind: graphql.Kind.NON_NULL_TYPE,
type: {
kind: graphql.Kind.LIST_TYPE,
type: {
kind: graphql.Kind.NAMED_TYPE,
name: {
kind: graphql.Kind.NAME,
value: "_Entity"
}
}
}
},
arguments: [
{
kind: graphql.Kind.INPUT_VALUE_DEFINITION,
name: {
kind: graphql.Kind.NAME,
value: "representations"
},
type: {
kind: graphql.Kind.NON_NULL_TYPE,
type: {
kind: graphql.Kind.LIST_TYPE,
type: {
kind: graphql.Kind.NON_NULL_TYPE,
type: {
kind: graphql.Kind.NAMED_TYPE,
name: {
kind: graphql.Kind.NAME,
value: "_Any"
}
}
}
}
}
}
]
};
const specifiedTypeNames = [
"ID",
"String",
"Float",
"Int",
"Boolean",
"_Any",
"_Entity"
];
function makeExternalObject(data, errors, getFieldNames) {
if (!delegate.isExternalObject(data) && typeof data === "object" && data != null) {
data[delegate.UNPATHED_ERRORS_SYMBOL] = errors;
}
if (errors.length) {
const errorsToPop = [...errors];
const fieldNamesToPop = [];
for (const fieldName of getFieldNames()) {
if (data?.[fieldName] == null) {
fieldNamesToPop.push(fieldName);
}
}
while (fieldNamesToPop.length && errorsToPop.length) {
const fieldName = fieldNamesToPop.pop();
if (!fieldName) {
break;
}
const error = errorsToPop.pop();
if (!error) {
break;
}
const errorToSet = fieldNamesToPop.length || !errorsToPop.length ? error : new AggregateError(
[error, ...errorsToPop],
[error, ...errorsToPop].map((e) => e.message).join(", \n")
);
data ||= {};
data[fieldName] = errorToSet;
}
}
return data;
}
function mergeResults(results, getFieldNames) {
const errors = [];
const datas = [];
for (const result of results) {
if (result instanceof AggregateError) {
errors.push(...result.errors);
} else if (result instanceof Error) {
errors.push(result);
} else if (result != null) {
datas.push(result);
}
}
if (datas.length) {
if (datas.length === 1) {
return makeExternalObject(datas[0], errors, getFieldNames);
}
const mergedData = utils.mergeDeep(datas, void 0, true, true);
const symbols = [
delegate.OBJECT_SUBSCHEMA_SYMBOL,
delegate.FIELD_SUBSCHEMA_MAP_SYMBOL,
delegate.UNPATHED_ERRORS_SYMBOL
];
for (const symbol of symbols) {
if (mergedData?.[symbol] == null) {
for (const data of datas) {
const symbolValue = data?.[symbol];
if (symbolValue != null) {
mergedData[symbol] = symbolValue;
break;
}
}
}
}
return makeExternalObject(mergedData, errors, getFieldNames);
}
if (errors.length) {
if (errors.length === 1) {
throw errors[0];
}
return new AggregateError(
errors,
errors.map((error) => error.message).join(", \n")
);
}
return null;
}
const DEFAULT_UPLINKS = [
"https://uplink.api.apollographql.com/",
"https://aws.uplink.api.apollographql.com/"
];
async function fetchSupergraphSdlFromManagedFederation(options = {}) {
const userDefinedUplinks = getEnvStr("APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT")?.split(",") ?? [];
const {
upLink = userDefinedUplinks[0] || DEFAULT_UPLINKS[0],
loggerByMessageLevel = DEFAULT_MESSAGE_LOGGER,
fetch: fetch$1 = fetch.fetch,
...variables
} = options;
if (!variables.graphRef) {
variables.graphRef = getEnvStr("APOLLO_GRAPH_REF");
}
if (!variables.apiKey) {
variables.apiKey = getEnvStr("APOLLO_KEY");
}
if (!upLink) {
throw new Error("No up link provided");
}
const response = await fetch$1(upLink, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
query: (
/* GraphQL */
`
query ($apiKey: String!, $graphRef: String!, $lastSeenId: ID) {
routerConfig(
ref: $graphRef
apiKey: $apiKey
ifAfterId: $lastSeenId
) {
__typename
... on FetchError {
code
message
minDelaySeconds
}
... on Unchanged {
id
minDelaySeconds
}
... on RouterConfigResult {
id
supergraphSdl: supergraphSDL
minDelaySeconds
messages {
level
body
}
}
}
}
`
),
variables
}),
signal: AbortSignal.timeout(3e4)
});
const responseBody = await response.text();
if (!response.ok) {
throw new Error(
`Failed to fetch supergraph SDL from '${upLink}' due to an HTTP error (${response.status}) ${responseBody}`
);
}
let result;
try {
result = JSON.parse(responseBody);
} catch (err) {
throw new Error(
`Failed to parse response from '${upLink}': ${err.message}
${responseBody}`
);
}
if (result.errors) {
throw new AggregateError(
result.errors,
`Failed to fetch supergraph SDL from '${upLink}'`
);
}
if (!result.data?.routerConfig) {
throw new Error(
`Failed to fetch supergraph SDL from '${upLink}': ${responseBody}`
);
}
const { routerConfig } = result.data;
if (routerConfig.__typename === "FetchError") {
return {
error: { code: routerConfig.code, message: routerConfig.message },
minDelaySeconds: routerConfig.minDelaySeconds
};
}
if (routerConfig.__typename === "Unchanged") {
return {
id: routerConfig.id,
minDelaySeconds: routerConfig.minDelaySeconds
};
}
for (const message of routerConfig.messages) {
loggerByMessageLevel[message.level](message.body);
}
return {
supergraphSdl: routerConfig.supergraphSdl,
id: routerConfig.id,
minDelaySeconds: routerConfig.minDelaySeconds
};
}
async function getStitchedSchemaFromManagedFederation(options) {
const result = await fetchSupergraphSdlFromManagedFederation({
graphRef: options.graphRef,
apiKey: options.apiKey,
upLink: options.upLink,
lastSeenId: options.lastSeenId,
fetch: options.fetch,
loggerByMessageLevel: options.loggerByMessageLevel
});
if ("supergraphSdl" in result) {
return {
...result,
schema: getStitchedSchemaFromSupergraphSdl({
supergraphSdl: result.supergraphSdl,
onStitchingOptions: options.onStitchingOptions,
onStitchedSchema: options.onStitchedSchema,
httpExecutorOpts: options.httpExecutorOpts,
onSubschemaConfig: options.onSubschemaConfig,
batch: options.batch
})
};
}
return result;
}
const DEFAULT_MESSAGE_LOGGER = {
ERROR: (message) => console.error("[Managed Federation] Uplink message: [ERROR]", message),
WARN: (message) => console.warn("[Managed Federation] Uplink message: [WARN]", message),
INFO: (message) => console.info("[Managed Federation] Uplink message: [INFO]", message)
};
const TypedEventTargetCtor = EventTarget;
class SupergraphSchemaManager extends TypedEventTargetCtor {
constructor(options = {}) {
super();
this.options = options;
}
schema = void 0;
#lastSeenId;
#retries = 1;
#timeout;
load = async (options) => {
this.options.apiKey ||= options.apollo.key;
this.options.graphRef ||= options.apollo.graphRef;
await this.#fetchSchema();
return {
executor(requestContext) {
return delegate.createDefaultExecutor(requestContext.schema)({
document: requestContext.document,
variables: requestContext.request.variables,
operationType: requestContext.operation.operation,
operationName: requestContext.operationName || void 0,
context: requestContext.context
});
}
};
};
onSchemaLoadOrUpdate = (callback) => {
const onSchemaChange = (event) => {
callback({
apiSchema: event.detail.schema,
coreSupergraphSdl: event.detail.supergraphSdl
});
};
this.addEventListener("schema", onSchemaChange);
return () => {
this.removeEventListener("schema", onSchemaChange);
};
};
start = (delayInSeconds = 0) => {
if (this.#timeout) {
this.stop();
}
this.#timeout = setTimeout(() => {
this.#log("info", "Polling started");
this.#retries = 1;
this.#fetchSchema();
}, delayInSeconds * 1e3);
};
forcePull = () => {
if (this.#timeout) {
clearTimeout(this.#timeout);
this.#timeout = void 0;
}
this.#retries = 1;
this.#fetchSchema();
};
stop = () => {
this.#log("info", "Polling stopped");
if (this.#timeout) {
clearTimeout(this.#timeout);
this.#timeout = void 0;
}
return utils.fakePromise();
};
#fetchSchema = async () => {
const { retryDelaySeconds = 0, minDelaySeconds = 0 } = this.options;
try {
this.#log("info", "Fetch schema from managed federation");
const result = await getStitchedSchemaFromManagedFederation({
...this.options,
loggerByMessageLevel: {
ERROR: (message) => {
const logEvent = new events.CustomEvent(
"log",
{
detail: { source: "uplink", level: "error", message }
}
);
this.dispatchEvent(logEvent);
},
WARN: (message) => {
const logEvent = new events.CustomEvent(
"log",
{
detail: { source: "uplink", level: "warn", message }
}
);
this.dispatchEvent(logEvent);
},
INFO: (message) => {
const logEvent = new events.CustomEvent(
"log",
{
detail: { source: "uplink", level: "info", message }
}
);
this.dispatchEvent(logEvent);
}
},
lastSeenId: this.#lastSeenId
});
if ("error" in result) {
this.#lastSeenId = void 0;
const errorEvent = new events.CustomEvent(
"error",
{
detail: {
error: result.error,
minDelaySeconds: result.minDelaySeconds
}
}
);
this.dispatchEvent(errorEvent);
this.#retryOnError(
result.error,
Math.max(result.minDelaySeconds, minDelaySeconds)
);
return;
}
if ("schema" in result) {
this.#lastSeenId = result.id;
this.schema = result.schema;
const schemaEvent = new events.CustomEvent(
"schema",
{
detail: {
schema: result.schema,
supergraphSdl: result.supergraphSdl
}
}
);
this.dispatchEvent(schemaEvent);
this.#log("info", "Supergraph successfully updated");
} else {
this.#log("info", "Supergraph is up to date");
}
this.#retries = 1;
const delay = Math.max(result.minDelaySeconds, minDelaySeconds);
this.#timeout = setTimeout(this.#fetchSchema, delay * 1e3);
this.#log("info", `Next pull in ${delay.toFixed(1)} seconds`);
} catch (e) {
this.#retryOnError(e, retryDelaySeconds ?? 0);
const errorEvent = new events.CustomEvent(
"error",
{
detail: e
}
);
this.dispatchEvent(errorEvent);
}
};
#retryOnError = (error, delayInSeconds) => {
const { maxRetries = 3 } = this.options;
const message = error?.message;
this.#log(
"error",
`Failed to pull schema from managed federation: ${message}`
);
if (this.#retries >= maxRetries) {
this.#timeout = void 0;
this.#log("error", "Max retries reached, giving up");
const failureEvent = new events.CustomEvent(
"failure",
{
detail: { error, delayInSeconds }
}
);
this.dispatchEvent(failureEvent);
return;
}
this.#retries++;
this.#log(
"info",
`Retrying (${this.#retries}/${maxRetries})${delayInSeconds ? ` in ${delayInSeconds.toFixed(1)} seconds` : ""}`
);
this.#timeout = setTimeout(this.#fetchSchema, delayInSeconds * 1e3);
};
#log = (level, message) => {
const logEvent = new events.CustomEvent("log", {
detail: {
source: "manager",
level,
message
}
});
this.dispatchEvent(logEvent);
};
[disposablestack.DisposableSymbols.dispose]() {
return this.stop();
}
}
exports.DEFAULT_UPLINKS = DEFAULT_UPLINKS;
exports.SupergraphSchemaManager = SupergraphSchemaManager;
exports.ensureSupergraphSDLAst = ensureSupergraphSDLAst;
exports.extractPercentageFromLabel = extractPercentageFromLabel;
exports.fetchSupergraphSdlFromManagedFederation = fetchSupergraphSdlFromManagedFederation;
exports.filterDirectivesByGraph = filterDirectivesByGraph;
exports.filterInternalFieldsAndTypes = filterInternalFieldsAndTypes;
exports.getArgsFromKeysForFederation = getArgsFromKeysForFederation;
exports.getCacheKeyFnFromKey = getCacheKeyFnFromKey;
exports.getKeyFnForFederation = getKeyFnForFederation;
exports.getNamedTypeNode = getNamedTypeNode;
exports.getRngFromEnv = getRngFromEnv;
exports.getStitchedSchemaFromManagedFederation = getStitchedSchemaFromManagedFederation;
exports.getStitchedSchemaFromSupergraphSdl = getStitchedSchemaFromSupergraphSdl;
exports.getStitchingOptionsFromSupergraphSdl = getStitchingOptionsFromSupergraphSdl;
exports.parseJoinDirective = parseJoinDirective;
exports.progressiveOverridePossibilityHandler = progressiveOverridePossibilityHandler;
exports.projectDataSelectionSet = projectDataSelectionSet;