UNPKG

@graphql-tools/federation

Version:

Useful tools to create and manipulate GraphQL schemas.

1,278 lines (1,274 loc) • 79.1 kB
import { getTypeInfo, handleOverrideByDelegation, extractUnavailableFieldsFromSelectionSet, subtractSelectionSets, delegateToSchema, OBJECT_SUBSCHEMA_SYMBOL, FIELD_SUBSCHEMA_MAP_SYMBOL, UNPATHED_ERRORS_SYMBOL, isExternalObject, createDefaultExecutor } from '@graphql-tools/delegate'; import { mapSchema, MapperKind, memoize1, parseSelectionSet, mergeDeep, getDirectiveExtensions, createGraphQLError, fakePromise } from '@graphql-tools/utils'; import { DisposableSymbols } from '@whatwg-node/disposablestack'; import { CustomEvent } from '@whatwg-node/events'; import { fetch } from '@whatwg-node/fetch'; import { CRITICAL_ERROR } from '@graphql-tools/executor'; import { buildHTTPExecutor } from '@graphql-tools/executor-http'; import { stitchSchemas, getDefaultFieldConfigMerger, ValidationLevel, calculateSelectionScore } from '@graphql-tools/stitch'; import { isPromise, handleMaybePromise } from '@whatwg-node/promise-helpers'; import { Kind, parse, visit, print, buildASTSchema, isObjectType, isInterfaceType, isInputObjectType, parseType, visitWithTypeInfo } from '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 = memoize1( function getArgsFromKeysForFederation2(representations) { return { representations }; } ); function projectDataSelectionSet(data, selectionSet) { if (data == null || selectionSet == null || !selectionSet?.selections?.length) { return data; } if (data instanceof Error) { return null; } if (Array.isArray(data)) { return data.map((entry) => projectDataSelectionSet(entry, selectionSet)); } const projectedData = { __typename: data.__typename }; for (const selection of selectionSet.selections) { if (selection.kind === Kind.FIELD) { const fieldName = selection.name.value; const responseKey = selection.alias?.value || selection.name.value; if (Object.prototype.hasOwnProperty.call(data, responseKey)) { const projectedKeyData = projectDataSelectionSet( data[responseKey], selection.selectionSet ); if (projectedData[fieldName]) { if (projectedKeyData != null && !(projectedKeyData instanceof Error)) { projectedData[fieldName] = mergeDeep( [projectedData[fieldName], projectedKeyData], void 0, true, true ); } } else { projectedData[fieldName] = projectedKeyData; } } } else if (selection.kind === Kind.INLINE_FRAGMENT) { if (selection.typeCondition && projectedData["__typename"] != null && projectedData["__typename"] !== selection.typeCondition.name.value) { continue; } Object.assign( projectedData, mergeDeep( [ projectedData, projectDataSelectionSet(data, selection.selectionSet) ], void 0, true, true ) ); } } return projectedData; } function getKeyFnForFederation(typeName, keys) { if (keys.some((key) => key.includes("{") || key.includes("("))) { const parsedSelectionSet = parseSelectionSet(`{${keys.join(" ")}}`, { noLocation: true }); return function keyFn(root) { if (root == null) { return root; } return projectDataSelectionSet( { __typename: typeName, ...root }, parsedSelectionSet ); }; } const allKeyProps = keys.flatMap((key) => key.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 memoize1(function keyFn(root) { if (root == null) { return null; } const keyPropVal = root[keyProp]; if (keyPropVal == null) { return null; } return { __typename: typeName, [keyProp]: keyPropVal }; }); } function getCacheKeyFnFromKey(key) { if (key.includes("{") || key.includes("(")) { const parsedSelectionSet = parseSelectionSet(`{${key}}`, { noLocation: true }); return function cacheKeyFn(root) { return JSON.stringify(projectDataSelectionSet(root, parsedSelectionSet)); }; } const keyTrimmed = key.trim(); const keys = keyTrimmed.split(" ").map((key2) => key2.trim()); if (keys.length > 1) { return function cacheKeyFn(root) { 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 memoize1(function cacheKeyFn(root) { const keyVal = root[keyTrimmed]; if (keyVal == null) { return ""; } if (typeof keyVal === "object") { return JSON.stringify(keyVal); } return keyVal; }); } function hasInaccessible(obj) { return getDirectiveExtensions(obj)?.inaccessible?.length; } function filterInternalFieldsAndTypes(finalSchema) { const internalTypeNameRegexp = /^(?:_Entity|_Any|_FieldSet|_Service|link|inaccessible|(?:link__|join__|core__)[\w]*)$/; return mapSchema(finalSchema, { [MapperKind.DIRECTIVE]: (directive) => { if (internalTypeNameRegexp.test(directive.name)) { return null; } return directive; }, [MapperKind.TYPE]: (type) => { if (internalTypeNameRegexp.test(type.name) || hasInaccessible(type)) { return null; } return type; }, [MapperKind.FIELD]: (fieldConfig) => { if (hasInaccessible(fieldConfig)) { return null; } return fieldConfig; }, [MapperKind.QUERY_ROOT_FIELD]: (fieldConfig, fieldName) => { if (fieldName === "_entities" || hasInaccessible(fieldConfig)) { return null; } return fieldConfig; }, [MapperKind.ENUM_VALUE]: (valueConfig) => { if (hasInaccessible(valueConfig)) { return null; } return valueConfig; }, [MapperKind.ARGUMENT]: (argConfig) => { if (hasInaccessible(argConfig)) { return null; } return argConfig; } }); } function getNamedTypeNode(typeNode) { if (typeNode.kind !== Kind.NAMED_TYPE) { return getNamedTypeNode(typeNode.type); } return typeNode; } function 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 ensureSupergraphSDLAst(supergraphSdl) { return typeof supergraphSdl === "string" ? parse(supergraphSdl, { noLocation: true }) : supergraphSdl; } const rootTypeMap = /* @__PURE__ */ new Map([ ["Query", "query"], ["Mutation", "mutation"], ["Subscription", "subscription"] ]); const memoizedASTPrint = memoize1(print); const memoizedTypePrint = memoize1( (type) => type.toString() ); function getStitchingOptionsFromSupergraphSdl(opts) { const supergraphAst = ensureSupergraphSDLAst(opts.supergraphSdl); const subgraphEndpointMap = /* @__PURE__ */ new Map(); const subgraphTypesMap = /* @__PURE__ */ new Map(); const typeNameKeysBySubgraphMap = /* @__PURE__ */ new Map(); const typeNameFieldsKeyBySubgraphMap = /* @__PURE__ */ new Map(); const typeNameCanonicalMap = /* @__PURE__ */ new Map(); const subgraphTypeNameProvidedMap = /* @__PURE__ */ new Map(); const subgraphTypeNameFieldProvidedSelectionMap = /* @__PURE__ */ new Map(); const orphanTypeMap = /* @__PURE__ */ new Map(); const typeFieldASTMap = /* @__PURE__ */ new Map(); const progressiveOverrideInfos = /* @__PURE__ */ new Map(); const progressiveOverrideInfosOpposite = /* @__PURE__ */ new Map(); const overrideLabels = /* @__PURE__ */ new Set(); for (const definition of supergraphAst.definitions) { if ("fields" in definition) { const fieldMap = /* @__PURE__ */ new Map(); typeFieldASTMap.set(definition.name.value, fieldMap); for (const field of definition.fields || []) { fieldMap.set(field.name.value, field); } } } const subgraphExternalFieldMap = /* @__PURE__ */ new Map(); const subgraphNames = []; visit(supergraphAst, { EnumTypeDefinition(node) { if (node.name.value === "join__Graph") { node.values?.forEach((valueNode) => { subgraphNames.push(valueNode.name.value); }); } } }); function TypeWithFieldsVisitor(typeNode) { if (typeNode.name.value === "Query" || typeNode.name.value === "Mutation" && !typeNode.directives?.some( (directiveNode) => directiveNode.name.value === "join__type" )) { typeNode.directives = [ ...typeNode.directives || [], ...subgraphNames.map((subgraphName) => ({ kind: Kind.DIRECTIVE, name: { kind: Kind.NAME, value: "join__type" }, arguments: [ { kind: Kind.ARGUMENT, name: { kind: Kind.NAME, value: "graph" }, value: { kind: Kind.ENUM, value: subgraphName } } ] })) ]; } let isOrphan = true; const fieldDefinitionNodesByGraphName = /* @__PURE__ */ new Map(); typeNode.directives?.forEach((directiveNode) => { if (typeNode.kind === Kind.OBJECT_TYPE_DEFINITION) { if (directiveNode.name.value === "join__owner") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode.value?.kind === Kind.ENUM) { typeNameCanonicalMap.set( typeNode.name.value, argumentNode.value.value ); } }); } } if (directiveNode.name.value === "join__type") { isOrphan = false; const joinTypeGraphArgNode = directiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "graph" ); if (joinTypeGraphArgNode?.value?.kind === Kind.ENUM) { const graphName = joinTypeGraphArgNode.value.value; if (typeNode.kind === Kind.OBJECT_TYPE_DEFINITION || typeNode.kind === Kind.INTERFACE_TYPE_DEFINITION) { const keyArgumentNode = directiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "key" ); const isResolvable = !directiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "resolvable" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === false ); if (isResolvable && keyArgumentNode?.value?.kind === Kind.STRING) { let typeNameKeysMap = typeNameKeysBySubgraphMap.get(graphName); if (!typeNameKeysMap) { typeNameKeysMap = /* @__PURE__ */ new Map(); typeNameKeysBySubgraphMap.set(graphName, typeNameKeysMap); } let keys = typeNameKeysMap.get(typeNode.name.value); if (!keys) { keys = []; typeNameKeysMap.set(typeNode.name.value, keys); } keys.push(keyArgumentNode.value.value); } } const fieldDefinitionNodesOfSubgraph = []; typeNode.fields?.forEach((fieldNode) => { const joinFieldDirectives = fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value === "join__field" ); let notInSubgraph = true; joinFieldDirectives?.forEach((joinFieldDirectiveNode) => { const joinFieldGraphArgNode = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "graph" ); if (joinFieldGraphArgNode?.value?.kind === Kind.ENUM && joinFieldGraphArgNode.value.value === graphName) { notInSubgraph = false; const isExternal = joinFieldDirectiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "external" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === true ); const isOverridden = joinFieldDirectiveNode.arguments?.some( (argumentNode) => argumentNode.name.value === "usedOverridden" && argumentNode.value?.kind === Kind.BOOLEAN && argumentNode.value.value === true ); if (isExternal) { let externalFieldsByType = subgraphExternalFieldMap.get(graphName); if (!externalFieldsByType) { externalFieldsByType = /* @__PURE__ */ new Map(); subgraphExternalFieldMap.set( graphName, externalFieldsByType ); } let externalFields = externalFieldsByType.get( typeNode.name.value ); if (!externalFields) { externalFields = /* @__PURE__ */ new Set(); externalFieldsByType.set( typeNode.name.value, externalFields ); } externalFields.add(fieldNode.name.value); } if (!isExternal && !isOverridden) { const typeArg = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "type" ); const typeNode2 = typeArg?.value.kind === Kind.STRING ? parseType(typeArg.value.value) : fieldNode.type; fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, type: typeNode2, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } const overrideFromArg = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "override" ); let overrideFromSubgraph = void 0; if (overrideFromArg?.value?.kind === Kind.STRING) { overrideFromSubgraph = overrideFromArg.value.value; } const overrideLabelArg = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "overrideLabel" ); let overrideLabel = void 0; if (overrideLabelArg?.value?.kind === 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 === Kind.STRING) { let handleSelection2 = function(fieldNodeTypeName2, selection) { switch (selection.kind) { case Kind.FIELD: { const extraFieldTypeNode = supergraphAst.definitions.find( (def) => "name" in def && def.name?.value === fieldNodeTypeName2 ); const extraFieldNodeInType = extraFieldTypeNode.fields?.find( (fieldNode2) => fieldNode2.name.value === selection.name.value ); if (extraFieldNodeInType) { let typeNameProvidedMap = subgraphTypeNameProvidedMap.get(graphName); if (!typeNameProvidedMap) { typeNameProvidedMap = /* @__PURE__ */ new Map(); subgraphTypeNameProvidedMap.set( graphName, typeNameProvidedMap ); } let providedFields = typeNameProvidedMap.get(fieldNodeTypeName2); if (!providedFields) { providedFields = /* @__PURE__ */ new Set(); typeNameProvidedMap.set( fieldNodeTypeName2, providedFields ); } providedFields.add(selection.name.value); if (selection.selectionSet) { const extraFieldNodeNamedType = getNamedTypeNode( extraFieldNodeInType.type ); const extraFieldNodeTypeName = extraFieldNodeNamedType.name.value; for (const subSelection of selection.selectionSet.selections) { handleSelection2( extraFieldNodeTypeName, subSelection ); } } } } break; case Kind.INLINE_FRAGMENT: { const fragmentType = selection.typeCondition?.name?.value || fieldNodeType.name.value; if (selection.selectionSet) { for (const subSelection of selection.selectionSet.selections) { handleSelection2(fragmentType, subSelection); } } } break; } }; const providesSelectionSet = parseSelectionSet( /* GraphQL */ `{ ${providedExtraField.value.value} }` ); let typeNameFieldProvidedSelectionMap = subgraphTypeNameFieldProvidedSelectionMap.get(graphName); if (!typeNameFieldProvidedSelectionMap) { typeNameFieldProvidedSelectionMap = /* @__PURE__ */ new Map(); subgraphTypeNameFieldProvidedSelectionMap.set( graphName, typeNameFieldProvidedSelectionMap ); } let fieldProvidedSelectionMap = typeNameFieldProvidedSelectionMap.get(typeNode.name.value); if (!fieldProvidedSelectionMap) { fieldProvidedSelectionMap = /* @__PURE__ */ new Map(); typeNameFieldProvidedSelectionMap.set( typeNode.name.value, fieldProvidedSelectionMap ); } fieldProvidedSelectionMap.set( fieldNode.name.value, providesSelectionSet ); const fieldNodeType = getNamedTypeNode(fieldNode.type); const fieldNodeTypeName = fieldNodeType.name.value; for (const selection of providesSelectionSet.selections) { handleSelection2(fieldNodeTypeName, selection); } } const requiresArgumentNode = joinFieldDirectiveNode.arguments?.find( (argumentNode) => argumentNode.name.value === "requires" ); if (requiresArgumentNode?.value?.kind === Kind.STRING) { let typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(graphName); if (!typeNameFieldsKeyMap) { typeNameFieldsKeyMap = /* @__PURE__ */ new Map(); typeNameFieldsKeyBySubgraphMap.set( graphName, typeNameFieldsKeyMap ); } let fieldsKeyMap = typeNameFieldsKeyMap.get( typeNode.name.value ); if (!fieldsKeyMap) { fieldsKeyMap = /* @__PURE__ */ new Map(); typeNameFieldsKeyMap.set(typeNode.name.value, fieldsKeyMap); } fieldsKeyMap.set( fieldNode.name.value, requiresArgumentNode.value.value ); } } }); if (!joinFieldDirectives?.length) { fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } else if (notInSubgraph && typeNameKeysBySubgraphMap.get(graphName)?.get(typeNode.name.value)?.some((key) => key.split(" ").includes(fieldNode.name.value))) { fieldDefinitionNodesOfSubgraph.push({ ...fieldNode, directives: fieldNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__field" ) }); } }); fieldDefinitionNodesByGraphName.set( graphName, fieldDefinitionNodesOfSubgraph ); } } }); const joinImplementsDirectives = typeNode.directives?.filter( (directiveNode) => directiveNode.name.value === "join__implements" ); fieldDefinitionNodesByGraphName.forEach( (fieldDefinitionNodesOfSubgraph, graphName) => { const interfaces = []; typeNode.interfaces?.forEach((interfaceNode) => { const implementedSubgraphs = joinImplementsDirectives?.filter( (directiveNode) => { const argumentNode = directiveNode.arguments?.find( (argumentNode2) => argumentNode2.name.value === "interface" ); return argumentNode?.value?.kind === Kind.STRING && argumentNode.value.value === interfaceNode.name.value; } ); if (!implementedSubgraphs?.length || implementedSubgraphs.some((directiveNode) => { const argumentNode = directiveNode.arguments?.find( (argumentNode2) => argumentNode2.name.value === "graph" ); return argumentNode?.value?.kind === Kind.ENUM && argumentNode.value.value === graphName; })) { interfaces.push(interfaceNode); } }); if (typeNode.name.value === "Query") { fieldDefinitionNodesOfSubgraph.push(entitiesFieldDefinitionNode); } const objectTypedDefNodeForSubgraph = { ...typeNode, interfaces, fields: fieldDefinitionNodesOfSubgraph, directives: typeNode.directives?.filter( (directiveNode) => directiveNode.name.value !== "join__type" && directiveNode.name.value !== "join__owner" && directiveNode.name.value !== "join__implements" ) }; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push(objectTypedDefNodeForSubgraph); } ); if (isOrphan) { orphanTypeMap.set(typeNode.name.value, typeNode); } } visit(supergraphAst, { ScalarTypeDefinition(node) { let isOrphan = !node.name.value.startsWith("link__") && !node.name.value.startsWith("join__"); node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ) }); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, InputObjectTypeDefinition(node) { let isOrphan = true; node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ) }); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, InterfaceTypeDefinition: TypeWithFieldsVisitor, UnionTypeDefinition(node) { let isOrphan = true; node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode?.value?.kind === Kind.ENUM) { isOrphan = false; const graphName = argumentNode.value.value; const unionMembers = []; node.directives?.forEach((directiveNode2) => { if (directiveNode2.name.value === "join__unionMember") { const graphArgumentNode = directiveNode2.arguments?.find( (argumentNode2) => argumentNode2.name.value === "graph" ); const memberArgumentNode = directiveNode2.arguments?.find( (argumentNode2) => argumentNode2.name.value === "member" ); if (graphArgumentNode?.value?.kind === Kind.ENUM && graphArgumentNode.value.value === graphName && memberArgumentNode?.value?.kind === Kind.STRING) { unionMembers.push({ kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: memberArgumentNode.value.value } }); } } }); if (unionMembers.length > 0) { let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push({ ...node, types: unionMembers, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" && directiveNode2.name.value !== "join__unionMember" ) }); } } }); } }); if (isOrphan && node.name.value !== "_Entity") { orphanTypeMap.set(node.name.value, node); } }, EnumTypeDefinition(node) { let isOrphan = true; if (node.name.value === "join__Graph") { node.values?.forEach((valueNode) => { isOrphan = false; valueNode.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__graph") { directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "url" && argumentNode.value?.kind === Kind.STRING) { subgraphEndpointMap.set( valueNode.name.value, argumentNode.value.value ); } }); } }); }); } node.directives?.forEach((directiveNode) => { if (directiveNode.name.value === "join__type") { isOrphan = false; directiveNode.arguments?.forEach((argumentNode) => { if (argumentNode.name.value === "graph" && argumentNode.value?.kind === Kind.ENUM) { const graphName = argumentNode.value.value; const enumValueNodes = []; node.values?.forEach((valueNode) => { const joinEnumValueDirectives = valueNode.directives?.filter( (directiveNode2) => directiveNode2.name.value === "join__enumValue" ); if (joinEnumValueDirectives?.length) { joinEnumValueDirectives.forEach( (joinEnumValueDirectiveNode) => { joinEnumValueDirectiveNode.arguments?.forEach( (argumentNode2) => { if (argumentNode2.name.value === "graph" && argumentNode2.value?.kind === Kind.ENUM && argumentNode2.value.value === graphName) { enumValueNodes.push({ ...valueNode, directives: valueNode.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__enumValue" ) }); } } ); } ); } else { enumValueNodes.push(valueNode); } }); const enumTypedDefNodeForSubgraph = { ...node, directives: node.directives?.filter( (directiveNode2) => directiveNode2.name.value !== "join__type" ), values: enumValueNodes }; let subgraphTypes = subgraphTypesMap.get(graphName); if (!subgraphTypes) { subgraphTypes = []; subgraphTypesMap.set(graphName, subgraphTypes); } subgraphTypes.push(enumTypedDefNodeForSubgraph); } }); } }); if (isOrphan) { orphanTypeMap.set(node.name.value, node); } }, ObjectTypeDefinition: TypeWithFieldsVisitor }); const subschemas = []; for (const [subgraphName, endpoint] of subgraphEndpointMap) { let visitTypeDefinitionsForOrphanTypes2 = function(node) { function visitNamedTypeNode(namedTypeNode) { const typeName = namedTypeNode.name.value; if (specifiedTypeNames.includes(typeName)) { return node; } const orphanType = orphanTypeMap.get(typeName); if (orphanType) { if (!extraOrphanTypesForSubgraph.has(typeName)) { extraOrphanTypesForSubgraph.set(typeName, {}); const extraOrphanType = visitTypeDefinitionsForOrphanTypes2(orphanType); extraOrphanTypesForSubgraph.set(typeName, extraOrphanType); } } else if (!subgraphTypes.some((typeNode) => typeNode.name.value === typeName)) { return null; } return node; } function visitFieldDefs(nodeFields) { const fields = []; for (const field of nodeFields || []) { const isTypeNodeOk = visitNamedTypeNode( getNamedTypeNode(field.type) ); if (!isTypeNodeOk) { continue; } if (field.kind === Kind.FIELD_DEFINITION) { const args = visitFieldDefs( field.arguments ); fields.push({ ...field, arguments: args }); } else { fields.push(field); } } return fields; } function visitObjectAndInterfaceDefs(node2) { const fields = visitFieldDefs(node2.fields); const interfaces = []; for (const iface of node2.interfaces || []) { const isTypeNodeOk = visitNamedTypeNode(iface); if (!isTypeNodeOk) { continue; } interfaces.push(iface); } return { ...node2, fields, interfaces }; } return visit(node, { [Kind.OBJECT_TYPE_DEFINITION]: visitObjectAndInterfaceDefs, [Kind.OBJECT_TYPE_EXTENSION]: visitObjectAndInterfaceDefs, [Kind.INTERFACE_TYPE_DEFINITION]: visitObjectAndInterfaceDefs, [Kind.INTERFACE_TYPE_EXTENSION]: visitObjectAndInterfaceDefs, [Kind.UNION_TYPE_DEFINITION](node2) { const types = []; for (const type of node2.types || []) { const isTypeNodeOk = visitNamedTypeNode(type); if (!isTypeNodeOk) { continue; } types.push(type); } return { ...node2, types }; }, [Kind.UNION_TYPE_EXTENSION](node2) { const types = []; for (const type of node2.types || []) { const isTypeNodeOk = visitNamedTypeNode(type); if (!isTypeNodeOk) { continue; } types.push(type); } return { ...node2, types }; }, [Kind.INPUT_OBJECT_TYPE_DEFINITION](node2) { const fields = visitFieldDefs(node2.fields); return { ...node2, fields }; }, [Kind.INPUT_OBJECT_TYPE_EXTENSION](node2) { const fields = visitFieldDefs(node2.fields); return { ...node2, fields }; } }); }; const mergeConfig = {}; const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName); const unionTypeNodes = []; if (typeNameKeyMap) { const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName); for (const [typeName, keys] of typeNameKeyMap) { let getMergedTypeConfigFromKey2 = function(key) { return { selectionSet: `{ ${key} }`, argsFromKeys: getArgsFromKeysForFederation, key: getKeyFnForFederation(typeName, [key, ...extraKeys]), fieldName: `_entities`, dataLoaderOptions: { cacheKeyFn: getCacheKeyFnFromKey(key), ...opts.batchDelegateOptions || {} } }; }; const mergedTypeConfig = mergeConfig[typeName] = {}; const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName); const extraKeys = /* @__PURE__ */ new Set(); if (fieldsKeyMap) { const fieldsConfig = mergedTypeConfig.fields = {}; for (const [fieldName, fieldNameKey] of fieldsKeyMap) { const selectionSetNode = parseSelectionSet(`{${fieldNameKey}}`); (function aliasFieldsWithArgs(selectionSetNode2) { for (const selection of selectionSetNode2.selections) { if (selection.kind === Kind.FIELD && selection.arguments?.length) { const argsHash = selection.arguments.map( (arg) => arg.name.value + // TODO: slow? faster hash? memoizedASTPrint(arg.value).replace( /[^a-zA-Z0-9]/g, "" ) ).join(""); selection.alias = { kind: Kind.NAME, value: "_" + selection.name.value + "_" + argsHash }; } if ("selectionSet" in selection && selection.selectionSet) { aliasFieldsWithArgs(selection.selectionSet); } } })(selectionSetNode); const selectionSet = print(selectionSetNode).replaceAll(/\n/g, " ").replaceAll(/\s+/g, " "); extraKeys.add( // remove first and last characters (curly braces) selectionSet.slice(1, -1) ); fieldsConfig[fieldName] = { selectionSet, computed: true }; } } if (typeNameCanonicalMap.get(typeName) === subgraphName) { mergedTypeConfig.canonical = true; } if (keys.length === 1 && keys[0]) { Object.assign(mergedTypeConfig, getMergedTypeConfigFromKey2(keys[0])); } if (keys.length > 1) { const entryPoints = keys.map( (key) => getMergedTypeConfigFromKey2(key) ); mergedTypeConfig.entryPoints = entryPoints; } unionTypeNodes.push({ kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: typeName } }); opts.onMergedTypeConfig?.(typeName, mergedTypeConfig); } } const typeNameProvidedSelectionMap = subgraphTypeNameFieldProvidedSelectionMap.get(subgraphName); if (typeNameProvidedSelectionMap) { for (const [ typeName, fieldSelectionMap ] of typeNameProvidedSelectionMap) { const mergedTypeConfig = mergeConfig[typeName] ||= {}; const fieldsConfig = mergedTypeConfig.fields ||= {}; for (const [fieldName, selectionSet] of fieldSelectionMap) { fieldsConfig[fieldName] = { provides: selectionSet }; } } } const entitiesUnionTypeDefinitionNode = { name: { kind: Kind.NAME, value: "_Entity" }, kind: Kind.UNION_TYPE_DEFINITION, types: unionTypeNodes }; const extraOrphanTypesForSubgraph = /* @__PURE__ */ new Map(); const subgraphTypes = subgraphTypesMap.get(subgraphName) || []; subgraphTypes.forEach((typeNode) => { visitTypeDefinitionsForOrphanTypes2(typeNode); }); const extendedSubgraphTypes = [ ...subgraphTypes, ...extraOrphanTypesForSubgraph.values() ]; for (const interfaceInSubgraph of extendedSubgraphTypes) { if (interfaceInSubgraph.kind === Kind.INTERFACE_TYPE_DEFINITION) { let isOrphan = true; for (const definitionNode of supergraphAst.definitions) { if (definitionNode.kind === Kind.OBJECT_TYPE_DEFINITION && definitionNode.interfaces?.some( (interfaceNode) => interfaceNode.name.value === interfaceInSubgraph.name.value )) { isOrphan = false; } } if (isOrphan) { interfaceInSubgraph.kind = Kind.OBJECT_TYPE_DEFINITION; } } } let schema; let schemaAst = { kind: Kind.DOCUMENT, definitions: [ ...extendedSubgraphTypes, entitiesUnionTypeDefinitionNode, anyTypeDefinitionNode ] }; if (opts.onSubgraphAST) { schemaAst = opts.onSubgraphAST(subgraphName, schemaAst); } try { schema = buildASTSchema(schemaAst, { assumeValidSDL: true, assumeValid: true }); } catch (e) { throw new Error( `Error building schema for subgraph ${subgraphName}: ${e?.stack || e?.message || e.toString()}` ); } let httpExecutorOpts; if (typeof opts.httpExecutorOpts === "function") { httpExecutorOpts = opts.httpExecutorOpts({ name: subgraphName, endpoint }); } else { httpExecutorOpts = opts.httpExecutorOpts || {}; } let executor = buildHTTPExecutor({ endpoint, ...httpExecutorOpts }); if (globalThis.process?.env?.["DEBUG"]) { const origExecutor = executor; executor = function debugExecutor(execReq) { const prefix = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName}`; console.debug(`${prefix} - subgraph-execute-start`, { document: memoizedASTPrint(execReq.document), variables: JSON.stringify(execReq.variables) }); return handleMaybePromise( () => origExecutor(execReq), (res) => { console.debug( `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName} - subgraph-execute-done`, JSON.stringify(res) ); return res; }, (err) => { console.error( `[${(/* @__PURE__ */ new Date()).toISOString()}] ${subgraphName} - subgraph-execute-error`, err ); return err; } ); }; } const typeNameProvidedMap = subgraphTypeNameProvidedMap.get(subgraphName); const externalFieldMap = subgraphExternalFieldMap.get(subgraphName); const transforms = []; if (externalFieldMap?.size && extendedSubgraphTypes.some( (t) => t.kind === Kind.INTERFACE_TYPE_DEFINITION )) { let createUnresolvableError2 = function(fieldName, fieldNode) { return createGraphQLError( `Was not able to find any options for ${fieldName}: This shouldn't have happened.`, { extensions: { [CRITICAL_ERROR]: true }, nodes: [fieldNode] } ); }, createIfaceFieldChecker2 = function(parentType) { const providedInterfaceFields = typeNameProvidedMap?.get( parentType.name ); const implementations = schema.getPossibleTypes(parentType); const ifaceFieldCheckResult = /* @__PURE__ */ new Map(); return function ifaceFieldChecker(fieldName) { let result = ifaceFieldCheckResult.get(fieldName); if (result == null) { result = true; for (const implementation of implementations) { const externalFields = externalFieldMap?.get(implementation.name); const providedFields = typeNameProvidedMap?.get( implementation.name ); if (!providedInterfaceFields?.has(fieldName) && !p