UNPKG

@graphql-tools/federation

Version:

Useful tools to create and manipulate GraphQL schemas.

2,044 lines • 73.9 kB
'use strict';

var delegate = require('@graphql-tools/delegate');
var executorHttp = require('@graphql-tools/executor-http');
var merge = require('@graphql-tools/merge');
var stitch = require('@graphql-tools/stitch');
var utils = require('@graphql-tools/utils');
var graphql = require('graphql');
var schema = require('@graphql-tools/schema');
var fetch = require('@whatwg-node/fetch');

const SubgraphBaseSDL = (
  /* GraphQL */
  `
  scalar _Any
  scalar _FieldSet
  scalar link__Import

  enum link__Purpose {
    SECURITY
    EXECUTION
  }

  type _Service {
    sdl: String!
  }

  type Query {
    _service: _Service!
  }

  directive @external on FIELD_DEFINITION | OBJECT
  directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
  directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
  directive @key(
    fields: _FieldSet!
    resolvable: Boolean = true
  ) repeatable on OBJECT | INTERFACE
  directive @link(
    url: String!
    as: String
    for: link__Purpose
    import: [link__Import]
  ) repeatable on SCHEMA
  directive @shareable repeatable on OBJECT | FIELD_DEFINITION
  directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
  directive @tag(
    name: String!
  ) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
  directive @override(from: String!) on FIELD_DEFINITION
  directive @composeDirective(name: String!) repeatable on SCHEMA

  directive @extends on OBJECT | INTERFACE
`
);
function buildSubgraphSchema(optsOrModules) {
  const opts = Array.isArray(optsOrModules) ? {
    typeDefs: optsOrModules.map((opt) => opt.typeDefs),
    resolvers: optsOrModules.map((opt) => opt.resolvers).flat()
  } : optsOrModules;
  const entityTypeNames = [];
  function handleEntity(node) {
    if (node.directives?.some((directive) => directive.name.value === "key")) {
      entityTypeNames.push(node.name.value);
    }
  }
  const typeDefs = graphql.visit(merge.mergeTypeDefs([SubgraphBaseSDL, opts.typeDefs]), {
    ObjectTypeDefinition: (node) => {
      handleEntity(node);
    },
    ObjectTypeExtension: (node) => {
      handleEntity(node);
      return {
        ...node,
        kind: graphql.Kind.OBJECT_TYPE_DEFINITION,
        directives: [
          ...node.directives || [],
          {
            kind: "Directive",
            name: {
              kind: "Name",
              value: "extends"
            }
          }
        ]
      };
    }
  });
  const givenResolvers = merge.mergeResolvers(opts.resolvers);
  const allTypeDefs = [typeDefs];
  const allResolvers = [sdlResolvers, givenResolvers];
  if (entityTypeNames.length > 0) {
    allTypeDefs.push(`union _Entity = ${entityTypeNames.join(" | ")}`);
    allTypeDefs.push(`extend type Query {
      _entities(representations: [_Any!]!): [_Entity]!
    }`);
    allResolvers.push({
      _Entity: {
        __resolveType: entityTypeResolver
      },
      Query: {
        _entities: (_root, args, context, info) => args.representations.map(
          (representation) => utils.mapMaybePromise(
            givenResolvers[representation.__typename]?.__resolveReference?.(
              representation,
              context,
              info
            ),
            (resolvedEntity) => {
              if (!resolvedEntity) {
                return representation;
              }
              if (!resolvedEntity.__typename) {
                resolvedEntity.__typename = representation.__typename;
              }
              return resolvedEntity;
            }
          )
        )
      }
    });
  }
  return schema.makeExecutableSchema({
    assumeValid: true,
    assumeValidSDL: true,
    ...opts,
    typeDefs: allTypeDefs,
    resolvers: allResolvers
  });
}
function entityTypeResolver(obj) {
  return obj.__typename;
}
const sdlResolvers = {
  Query: {
    _service: () => ({})
  },
  _Service: {
    sdl: (_root, _args, _context, info) => utils.printSchemaWithDirectives(info.schema)
  }
};

function getArgsFromKeysForFederation(representations) {
  return { representations };
}
function getKeyForFederation(root) {
  return root;
}
function projectDataSelectionSet(data, selectionSet) {
  if (data == null || selectionSet == null || !selectionSet?.selections?.length) {
    return data;
  }
  if (data instanceof Error) {
    return null;
  }
  if (Array.isArray(data)) {
    return data.map((entry) => projectDataSelectionSet(entry, selectionSet));
  }
  const projectedData = {
    __typename: data.__typename
  };
  for (const selection of selectionSet.selections) {
    if (selection.kind === 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.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) {
      return keys.map((key2) => {
        const keyVal = root[key2];
        if (keyVal == null) {
          return "";
        }
        if (typeof keyVal === "object") {
          return JSON.stringify(keyVal);
        }
        return keyVal;
      }).join(" ");
    };
  }
  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;
}
class EventEmitter {
  #listeners = /* @__PURE__ */ new Map();
  on(eventName, listener) {
    const listeners = this.#listeners.get(eventName);
    if (!listeners) {
      this.#listeners.set(eventName, [listener]);
    } else {
      listeners.push(listener);
    }
    return this;
  }
  once(eventName, listener) {
    const selfRemovingListener = (...args) => {
      this.off(eventName, selfRemovingListener);
      listener(...args);
    };
    this.on(eventName, selfRemovingListener);
    return this;
  }
  off(eventName, listener) {
    const listeners = this.#listeners.get(eventName);
    if (listeners) {
      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    }
    return this;
  }
  emit(eventName, ...args) {
    const listeners = this.#listeners.get(eventName);
    if (!listeners) {
      return false;
    }
    for (let i = listeners.length - 1; i >= 0; i--) {
      listeners[i]?.(...args);
    }
    return true;
  }
}

const SubgraphSDLQuery = (
  /* GraphQL */
  `
  query SubgraphSDL {
    _service {
      sdl
    }
  }
`
);
async function getSubschemaForFederationWithURL(config) {
  const executor = executorHttp.buildHTTPExecutor(config);
  const subschemaConfig = await getSubschemaForFederationWithExecutor(executor);
  return {
    batch: true,
    ...subschemaConfig
  };
}
function getSubschemaForFederationWithTypeDefs(typeDefs) {
  const subschemaConfig = {};
  const typeMergingConfig = subschemaConfig.merge = subschemaConfig.merge || {};
  const entityTypes = [];
  const visitor = (node) => {
    if (node.directives) {
      const typeName = node.name.value;
      const selections = [];
      for (const directive of node.directives) {
        const directiveArgs = directive.arguments || [];
        switch (directive.name.value) {
          case "key": {
            if (directiveArgs.some(
              (arg) => arg.name.value === "resolvable" && arg.value.kind === graphql.Kind.BOOLEAN && arg.value.value === false
            )) {
              continue;
            }
            const selectionValueNode = directiveArgs.find(
              (arg) => arg.name.value === "fields"
            )?.value;
            if (selectionValueNode?.kind === graphql.Kind.STRING) {
              selections.push(selectionValueNode.value);
            }
            break;
          }
          case "inaccessible":
            return null;
        }
      }
      if (selections.length === 0) {
        return node;
      }
      const typeMergingTypeConfig = typeMergingConfig[typeName] = typeMergingConfig[typeName] || {};
      if (node.kind === graphql.Kind.OBJECT_TYPE_DEFINITION && !node.directives?.some((d) => d.name.value === "extends")) {
        typeMergingTypeConfig.canonical = true;
      }
      entityTypes.push(typeName);
      const selectionsStr = selections.join(" ");
      typeMergingTypeConfig.selectionSet = `{ ${selectionsStr} }`;
      typeMergingTypeConfig.dataLoaderOptions = {
        cacheKeyFn: getCacheKeyFnFromKey(selectionsStr)
      };
      typeMergingTypeConfig.argsFromKeys = getArgsFromKeysForFederation;
      typeMergingTypeConfig.fieldName = `_entities`;
      typeMergingTypeConfig.key = getKeyForFederation;
      const fields = [];
      if (node.fields) {
        for (const fieldNode of node.fields) {
          let removed = false;
          if (fieldNode.directives) {
            const fieldName = fieldNode.name.value;
            for (const directive of fieldNode.directives) {
              const directiveArgs = directive.arguments || [];
              switch (directive.name.value) {
                case "requires": {
                  const typeMergingFieldsConfig = typeMergingTypeConfig.fields = typeMergingTypeConfig.fields || {};
                  typeMergingFieldsConfig[fieldName] = typeMergingFieldsConfig[fieldName] || {};
                  if (directiveArgs.some(
                    (arg) => arg.name.value === "resolvable" && arg.value.kind === graphql.Kind.BOOLEAN && arg.value.value === false
                  )) {
                    continue;
                  }
                  const selectionValueNode = directiveArgs.find(
                    (arg) => arg.name.value === "fields"
                  )?.value;
                  if (selectionValueNode?.kind === graphql.Kind.STRING) {
                    typeMergingFieldsConfig[fieldName].selectionSet = `{ ${selectionValueNode.value} }`;
                    typeMergingFieldsConfig[fieldName].computed = true;
                  }
                  break;
                }
                case "external":
                case "inaccessible": {
                  removed = !typeMergingTypeConfig.selectionSet?.includes(
                    ` ${fieldName} `
                  );
                  break;
                }
                case "override": {
                  const typeMergingFieldsConfig = typeMergingTypeConfig.fields = typeMergingTypeConfig.fields || {};
                  typeMergingFieldsConfig[fieldName] = typeMergingFieldsConfig[fieldName] || {};
                  typeMergingFieldsConfig[fieldName].canonical = true;
                  break;
                }
              }
            }
          }
          if (!removed) {
            fields.push(fieldNode);
          }
        }
        node.fields = fields;
      }
    }
    return {
      ...node,
      kind: graphql.Kind.OBJECT_TYPE_DEFINITION
    };
  };
  const parsedSDL = graphql.visit(typeDefs, {
    ObjectTypeExtension: visitor,
    ObjectTypeDefinition: visitor
  });
  let extraSdl = SubgraphBaseSDL;
  if (entityTypes.length > 0) {
    extraSdl += `
union _Entity = ${entityTypes.join(" | ")}`;
    extraSdl += `
extend type Query { _entities(representations: [_Any!]!): [_Entity]! }`;
  }
  subschemaConfig.schema = graphql.buildASTSchema(
    merge.mergeTypeDefs([extraSdl, parsedSDL]),
    {
      assumeValidSDL: true,
      assumeValid: true
    }
  );
  return subschemaConfig;
}
async function getSubschemaForFederationWithExecutor(executor) {
  const sdlQueryResult = await executor({
    document: graphql.parse(SubgraphSDLQuery)
  });
  if (sdlQueryResult.errors?.length && sdlQueryResult.errors[0]) {
    const error = sdlQueryResult.errors[0];
    throw utils.createGraphQLError(error.message, error);
  }
  if (!sdlQueryResult.data?._service?.sdl) {
    throw new Error(`Unexpected result: ${utils.inspect(sdlQueryResult)}`);
  }
  const typeDefs = graphql.parse(sdlQueryResult.data._service.sdl);
  const subschemaConfig = getSubschemaForFederationWithTypeDefs(typeDefs);
  return {
    ...subschemaConfig,
    executor
  };
}
async function getSubschemaForFederationWithSchema(schema) {
  const executor = delegate.createDefaultExecutor(schema);
  return getSubschemaForFederationWithExecutor(executor);
}
async function getStitchedSchemaWithUrls(configs) {
  const subschemas = await Promise.all(
    configs.map((config) => getSubschemaForFederationWithURL(config))
  );
  const schema = stitch.stitchSchemas({
    subschemas
  });
  return filterInternalFieldsAndTypes(schema);
}
const federationSubschemaTransformer = function federationSubschemaTransformer2(subschemaConfig) {
  const typeDefs = utils.getDocumentNodeFromSchema(subschemaConfig.schema);
  const newSubschema = getSubschemaForFederationWithTypeDefs(typeDefs);
  return {
    ...subschemaConfig,
    ...newSubschema,
    merge: {
      ...newSubschema.merge,
      ...subschemaConfig.merge
    }
  };
};

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();
  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 = [];
  graphql.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: 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;
          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 (isResolvable && keyArgumentNode?.value?.kind === graphql.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 === 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: fieldNode.directives?.filter(
                      (directiveNode2) => directiveNode2.name.value !== "join__field"
                    )
                  });
                }
                const providedExtraField = joinFieldDirectiveNode.arguments?.find(
                  (argumentNode) => argumentNode.name.value === "provides"
                );
                if (providedExtraField?.value?.kind === graphql.Kind.STRING) {
                  let handleSelection2 = function(fieldNodeTypeName2, selection) {
                    switch (selection.kind) {
                      case graphql.Kind.FIELD:
                        {
                          const extraFieldTypeNode = supergraphAst.definitions.find(
                            (def) => "name" in def && def.name?.value === fieldNodeTypeName2
                          );
                          const extraFieldNodeInType = extraFieldTypeNode.fields?.find(
                            (fieldNode2) => fieldNode2.name.value === selection.name.value
                          );
                          if (extraFieldNodeInType) {
                            let typeNameProvidedMap = subgraphTypeNameProvidedMap.get(graphName);
                            if (!typeNameProvidedMap) {
                              typeNameProvidedMap = /* @__PURE__ */ new Map();
                              subgraphTypeNameProvidedMap.set(
                                graphName,
                                typeNameProvidedMap
                              );
                            }
                            let providedFields = typeNameProvidedMap.get(fieldNodeTypeName2);
                            if (!providedFields) {
                              providedFields = /* @__PURE__ */ new Set();
                              typeNameProvidedMap.set(
                                fieldNodeTypeName2,
                                providedFields
                              );
                            }
                            providedFields.add(selection.name.value);
                            if (selection.selectionSet) {
                              const extraFieldNodeNamedType = getNamedTypeNode(
                                extraFieldNodeInType.type
                              );
                              const extraFieldNodeTypeName = extraFieldNodeNamedType.name.value;
                              for (const subSelection of selection.selectionSet.selections) {
                                handleSelection2(
                                  extraFieldNodeTypeName,
                                  subSelection
                                );
                              }
                            }
                          }
                        }
                        break;
                      case graphql.Kind.INLINE_FRAGMENT:
                        {
                          const fragmentType = selection.typeCondition?.name?.value || fieldNodeType.name.value;
                          if (selection.selectionSet) {
                            for (const subSelection of selection.selectionSet.selections) {
                              handleSelection2(fragmentType, subSelection);
                            }
                          }
                        }
                        break;
                    }
                  };
                  const providesSelectionSet = utils.parseSelectionSet(
                    /* GraphQL */
                    `{ ${providedExtraField.value.value} }`
                  );
                  let typeNameFieldProvidedSelectionMap = subgraphTypeNameFieldProvidedSelectionMap.get(graphName);
                  if (!typeNameFieldProvidedSelectionMap) {
                    typeNameFieldProvidedSelectionMap = /* @__PURE__ */ new Map();
                    subgraphTypeNameFieldProvidedSelectionMap.set(
                      graphName,
                      typeNameFieldProvidedSelectionMap
                    );
                  }
                  let fieldProvidedSelectionMap = typeNameFieldProvidedSelectionMap.get(typeNode.name.value);
                  if (!fieldProvidedSelectionMap) {
                    fieldProvidedSelectionMap = /* @__PURE__ */ new Map();
                    typeNameFieldProvidedSelectionMap.set(
                      typeNode.name.value,
                      fieldProvidedSelectionMap
                    );
                  }
                  fieldProvidedSelectionMap.set(
                    fieldNode.name.value,
                    providesSelectionSet
                  );
                  const fieldNodeType = getNamedTypeNode(fieldNode.type);
                  const fieldNodeTypeName = fieldNodeType.name.value;
                  for (const selection of providesSelectionSet.selections) {
                    handleSelection2(fieldNodeTypeName, selection);
                  }
                }
                const requiresArgumentNode = joinFieldDirectiveNode.arguments?.find(
                  (argumentNode) => argumentNode.name.value === "requires"
                );
                if (requiresArgumentNode?.value?.kind === graphql.Kind.STRING) {
                  let typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(graphName);
                  if (!typeNameFieldsKeyMap) {
                    typeNameFieldsKeyMap = /* @__PURE__ */ new Map();
                    typeNameFieldsKeyBySubgraphMap.set(
                      graphName,
                      typeNameFieldsKeyMap
                    );
                  }
                  let fieldsKeyMap = typeNameFieldsKeyMap.get(
                    typeNode.name.value
                  );
                  if (!fieldsKeyMap) {
                    fieldsKeyMap = /* @__PURE__ */ new Map();
                    typeNameFieldsKeyMap.set(typeNode.name.value, fieldsKeyMap);
                  }
                  fieldsKeyMap.set(
                    fieldNode.name.value,
                    requiresArgumentNode.value.value
                  );
                }
              }
            });
            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 === 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: 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);
    }
  }
  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: 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 === 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: 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 === 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: 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 === 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: 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 === 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
          };
        }
      });
    };
    const mergeConfig = {};
    const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName);
    const unionTypeNodes = [];
    if (typeNameKeyMap) {
      const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName);
      for (const [typeName, keys] of typeNameKeyMap) {
        let getMergedTypeConfigFromKey2 = function(key) {
          return {
            selectionSet: `{ ${key} }`,
            argsFromKeys: getArgsFromKeysForFederation,
            key: getKeyFnForFederation(typeName, [key, ...extraKeys]),
            fieldName: `_entities`,
            dataLoaderOptions: {
              cacheKeyFn: getCacheKeyFnFromKey(key),
              ...opts.batchDelegateOptions || {}
            }
          };
        };
        const mergedTypeConfig = mergeConfig[typeName] = {};
        const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName);
        const extraKeys = /* @__PURE__ */ new Set();
        if (fieldsKeyMap) {
          const fieldsConfig = mergedTypeConfig.fields = {};
          for (const [fieldName, fieldNameKey] of fieldsKeyMap) {
            const aliasedFieldNameKey = fieldNameKey.includes("(") ? `_${fieldNameKey.split("(")[0]}: ${fieldNameKey}` : fieldNameKey;
            extraKeys.add(aliasedFieldNameKey);
            fieldsConfig[fieldName] = {
              selectionSet: `{ ${aliasedFieldNameKey} }`,
              computed: true
            };
          }
        }
        if (typeNameCanonicalMap.get(typeName) === subgraphName) {
          mergedTypeConfig.canonical = true;
        }
        if (keys.length === 1 && keys[0]) {
          Object.assign(mergedTypeConfig, getMergedTypeConfigFromKey2(keys[0]));
        }
        if (keys.length > 1) {
          const entryPoints = keys.map(
            (key) => getMergedTypeConfigFromKey2(key)
          );
          mergedTypeConfig.entryPoints = entryPoints;
        }
        unionTypeNodes.push({
          kind: 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
      ]
    };
    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 = executorHttp.buildHTTPExecutor({
      endpoint,
      ...httpExecutorOpts
    });
    if (globalThis.process?.env?.["DEBUG"]) {
      const origExecutor = executor;
      executor = async function debugExecutor(execReq) {
        console.log(`Executing ${subgraphName} with args:`, {
          document: graphql.print(execReq.document),
          variables: JSON.stringify(execReq.variables)
        });
        const res = await origExecutor(execReq);
        console.log(`Response from ${subgraphName}:`, JSON.stringify(res));
        return res;
      };
    }
    const typeNameProvidedMap = subgraphTypeNameProvidedMap.get(subgraphName);
    const externalFieldMap = subgraphExternalFieldMap.get(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: {
              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 subschemaConfig = {
      name: subgraphName,
      endpoint,
      schema,
      executor,
      merge: mergeConfig,
      transforms,
      batch: opts.batch,
      batchingOptions: opts.batchingOptions
    };
    subschemas.push(subschemaConfig);
  }
  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;
      }
    }
    if (candidates.some((candidate) => rootTypeMap.has(candidate.type.name))) {
      const defaultMergedField = defaultMerger(candidates);
      return {
        ...defaultMergedField,
        resolve(_root, _args, context, info) {
          const originalSelectionSet = {
            kind: graphql.Kind.SELECTION_SET,
            selections: info.fieldNodes
          };
          const candidatesReversed = candidates.toReversed ? candidates.toReversed() : [...candidates].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);
              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 candidates) {
                  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
                  );
                  if (friendScore < score) {
                    const unavailableInFriendSelectionSet = {
                      kind: graphql.Kind.SELECTION_SET,
                      selections: unavailableFieldsInFriend
                    };
                    const subschemaSelectionSet = delegate.subtractSelectionSets(
                      currentUnavailableSelectionSet,
                      unavailableInFriendSelectionSet
                    );
                    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,
            info: currentFriendSubschemas?.size ? {
              ...info,
              fieldNodes: [
                ...currentAvailableSelectionSet?.selections || [],
                ...currentUnavailableSelectionSet?.selections || []
              ]
            } : info
          });
          if (utils.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,
                info: {
                  ...info,
                  fieldNodes: friendSelectionSet.selections
                },
                skipTypeMerging: true
              });
              if (utils.isPromise(friendJob)) {
                hasPromise = true;
              }
              jobs.push(friendJob);
            }
          }
          if (jobs.length === 1) {
            return jobs[0];
          }
          if (hasPromise) {
            return Promise.all(jobs).then((results) => mergeResults(results));
          }
          return mergeResults(jobs);
        }
      };
    }
    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
            });
          }
        }
      }
    }
  }
  const additionalTypeDefs = {
    kind: graphql.Kind.DOCUMENT,
    definitions: extraDefinitions
  };
  if (opts.onSubschemaConfig) {
    for (const subschema of subschemas) {
      opts.onSubschemaConfig(subschema);
    }
  }
  return {
    subschemas,
    typeDefs: additionalTypeDefs,
    assumeValid: true,
    assumeValidSDL: true,
    typeMergingOptions: {
      useNonNullableFieldOnConflict: true,
      validationSettings: {
        validationLevel: stitch.ValidationLevel.Off
      },
      fieldConfigMerger
    }
  };
}
function getStitchedSchemaFromSupergraphSdl(opts) {
  const stitchSchemasOpts = getStitchingOptionsFromSupergraphSdl(opts);
  opts.onStitchingOptions?.(stitchSchemasOpts);
  const supergraphSchema = stitch.stitchSchemas(stitchSchemasOpts);
  return filterInternalFieldsAndTypes(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) {
  if (!delegate.isExternalObject(data) && typeof data === "object" && data != null) {
    data[delegate.UNPATHED_ERRORS_SYMBOL] = errors;
  }
  return data;
}
function mergeResults(results) {
  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);
    }
    return makeExternalObject(utils.mergeDeep(datas, void 0, true, true), errors);
  }
  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 = process.env["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 = process.env["APOLLO_GRAPH_REF"];
  }
  if (!variables.apiKey) {
    variables.apiKey = process.env["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
    })
  });
  const responseBody = await response.text();
  if (!response.ok) {
    throw new Error(
      `Failed to fetch supergraph SDL from managed federation up link '${upLink}': [${response.status} ${response.statusText}] ${responseBody}`
    );
  }
  let result;
  try {
    result = JSON.parse(responseBody);
  } catch (err) {
    throw new Error(
      `Failed to parse response from managed federation up link '${upLink}': ${err.message}

${responseBody}`
    );
  }
  if (result.errors) {
    const errors = result.errors.map(({ message }) => "\n" + message).join("");
    throw new Error(
      `Failed to fetch supergraph SDL from managed federation up link '${upLink}': ${errors}`
    );
  }
  if (!result.data?.routerConfig) {
    throw new Error(
      `Failed to fetch supergraph SDL from managed federation up link '${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,
        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)
};
class SupergraphSchemaManager extends EventEmitter {
  constructor(options) {
    super();
    this.options = options;
    registerCleanup(() => {
      this.stop();
    });
  }
  schema = void 0;
  #lastSeenId;
  #retries = 1;
  #timeout;
  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;
    }
  };
  #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) => this.emit("log", { source: "uplink", level: "error", message }),
          WARN: (message) => this.emit("log", { source: "uplink", level: "warn", message }),
          INFO: (message) => this.emit("log", { source: "uplink", level: "info", message })
        },
        lastSeenId: this.#lastSeenId
      });
      if ("error" in result) {
        this.#lastSeenId = void 0;
        this.emit("error", result.error);
        this.#retryOnError(
          result.error,
          Math.max(result.minDelaySeconds, minDelaySeconds)
        );
        return;
      }
      if ("schema" in result) {
        this.#lastSeenId = result.id;
        this.schema = result.schema;
        this.emit("schema", result.schema, result.supergraphSdl);
        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);
      this.emit("error", e);
    }
  };
  #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");
      this.emit("failure", error, delayInSeconds);
      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) => {
    this.emit("log", { source: "manager", level, message });
  };
}
function registerCleanup(cleanupFn) {
  if (typeof global.process === "object") {
    for (const signal of ["SIGINT", "SIGTERM", "SIGQUIT"])
      process.on(signal, () => {
        cleanupFn();
      });
  }
}

exports.DEFAULT_UPLINKS = DEFAULT_UPLINKS;
exports.EventEmitter = EventEmitter;
exports.SubgraphBaseSDL = SubgraphBaseSDL;
exports.SubgraphSDLQuery = SubgraphSDLQuery;
exports.SupergraphSchemaManager = SupergraphSchemaManager;
exports.buildSubgraphSchema = buildSubgraphSchema;
exports.ensureSupergraphSDLAst = ensureSupergraphSDLAst;
exports.federationSubschemaTransformer = federationSubschemaTransformer;
exports.fetchSupergraphSdlFromManagedFederation = fetchSupergraphSdlFromManagedFederation;
exports.filterInternalFieldsAndTypes = filterInternalFieldsAndTypes;
exports.getArgsFromKeysForFederation = getArgsFromKeysForFederation;
exports.getCacheKeyFnFromKey = getCacheKeyFnFromKey;
exports.getKeyFnForFederation = getKeyFnForFederation;
exports.getKeyForFederation = getKeyForFederation;
exports.getNamedTypeNode = getNamedTypeNode;
exports.getStitchedSchemaFromManagedFederation = getStitchedSchemaFromManagedFederation;
exports.getStitchedSchemaFromSupergraphSdl = getStitchedSchemaFromSupergraphSdl;
exports.getStitchedSchemaWithUrls = getStitchedSchemaWithUrls;
exports.getStitchingOptionsFromSupergraphSdl = getStitchingOptionsFromSupergraphSdl;
exports.getSubschemaForFederationWithExecutor = getSubschemaForFederationWithExecutor;
exports.getSubschemaForFederationWithSchema = getSubschemaForFederationWithSchema;
exports.getSubschemaForFederationWithTypeDefs = getSubschemaForFederationWithTypeDefs;
exports.getSubschemaForFederationWithURL = getSubschemaForFederationWithURL;
exports.projectDataSelectionSet = projectDataSelectionSet;