UNPKG

@graphql-tools/stitch

Version:

A set of utils for faster development of GraphQL tools

3,156 lines • 112 kB
import { subtractSelectionSets, extractUnavailableFields, leftOverByDelegationPlan, extractUnavailableFieldsFromSelectionSet, delegateToSchema, cloneSubschemaConfig, isSubschemaConfig, defaultMergedResolver, Subschema } from '@graphql-tools/delegate';
import { mergeType, mergeInputType, mergeInterface, mergeUnion, mergeEnum, mergeScalar, mergeTypeDefs, mergeResolvers, mergeExtensions, applyExtensions } from '@graphql-tools/merge';
import { extendResolversFromInterfaces, addResolversToSchema, assertResolversPresent, makeExecutableSchema } from '@graphql-tools/schema';
import { collectSubFields, memoize2, memoize1, memoize3, parseSelectionSet, collectFields, isSome, getImplementingTypes, getRootTypeNames, filterSchema, mergeDeep, getDescription, createNamedStub, createStub, getRootTypeMap, getRootTypes, inspect, rewireTypes, getOperationASTFromRequest, getDefinedRootType } from '@graphql-tools/utils';
import { isAbstractType, Kind, TypeNameMetaFieldDef, getNamedType, GraphQLList, isLeafType, isInputObjectType, isUnionType, print, isObjectType, isInterfaceType, visit, isScalarType, isCompositeType, isNonNullType, isEnumType, isListType, getNullableType, isNullableType, GraphQLObjectType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLScalarType, GraphQLDirective, valueFromASTUntyped, getDirectiveValues, GraphQLDeprecatedDirective, DirectiveLocation, isNamedType, isIntrospectionType, isDirective, isSpecifiedScalarType, specifiedDirectives, GraphQLSchema, extendSchema } from 'graphql';
import { handleMaybePromise } from '@whatwg-node/promise-helpers';
import { batchDelegateToSchema } from '@graphql-tools/batch-delegate';
import { FilterTypes, TransformCompositeFields, wrapSchema } from '@graphql-tools/wrap';
import { getFragmentsFromDocument } from '@graphql-tools/executor';

function getFieldsNotInSubschema(schema, stitchingInfo, gatewayType, subschemaType, fieldNodes, fragments, variableValues, subschema, providedSelectionNode) {
  const sourceSchema = subschema.transformedSchema;
  let { fields: subFieldNodesByResponseKey, patches } = collectSubFields(
    schema,
    fragments,
    variableValues,
    gatewayType,
    fieldNodes
  );
  let mapChanged = false;
  if (patches.length) {
    subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
    for (const patch of patches) {
      for (const [responseKey, fields2] of patch.fields) {
        if (!mapChanged) {
          subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
          mapChanged = true;
        }
        const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
        if (existingSubFieldNodes) {
          existingSubFieldNodes.push(...fields2);
        } else {
          subFieldNodesByResponseKey.set(responseKey, fields2);
        }
      }
    }
  }
  const fieldsNotInSchema = /* @__PURE__ */ new Set();
  if (isAbstractType(gatewayType)) {
    fieldsNotInSchema.add({
      kind: Kind.FIELD,
      name: {
        kind: Kind.NAME,
        value: "__typename"
      }
    });
    for (const possibleType of schema.getPossibleTypes(gatewayType)) {
      const { fields: subFieldNodesOfPossibleType, patches: patches2 } = collectSubFields(
        schema,
        fragments,
        variableValues,
        possibleType,
        fieldNodes
      );
      for (const patch of patches2) {
        for (const [responseKey, fields2] of patch.fields) {
          if (!mapChanged) {
            subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
            mapChanged = true;
          }
          const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
          if (existingSubFieldNodes) {
            existingSubFieldNodes.push(...fields2);
          } else {
            subFieldNodesByResponseKey.set(responseKey, fields2);
          }
        }
      }
      for (const [responseKey, subFieldNodes] of subFieldNodesOfPossibleType) {
        if (!mapChanged) {
          subFieldNodesByResponseKey = new Map(subFieldNodesByResponseKey);
          mapChanged = true;
        }
        const existingSubFieldNodes = subFieldNodesByResponseKey.get(responseKey);
        if (existingSubFieldNodes) {
          existingSubFieldNodes.push(...subFieldNodes);
        } else {
          subFieldNodesByResponseKey.set(responseKey, subFieldNodes);
        }
      }
    }
  }
  const fieldNodesByField = stitchingInfo?.fieldNodesByField;
  const fields = subschemaType.getFields();
  const fieldNodesByFieldForType = fieldNodesByField?.[gatewayType.name];
  for (const [, subFieldNodes] of subFieldNodesByResponseKey) {
    let fieldNotInSchema = false;
    const fieldName = subFieldNodes[0]?.name.value;
    const field = fieldName === "__typename" ? TypeNameMetaFieldDef : fields[fieldName];
    if (!field) {
      if (providedSelectionNode) {
        const subFieldSelection = {
          kind: Kind.SELECTION_SET,
          selections: subFieldNodes
        };
        const subtracted = subtractSelectionSets(
          subFieldSelection,
          providedSelectionNode
        );
        if (subtracted?.selections?.length) {
          fieldNotInSchema = true;
          for (const subFieldNode of subtracted.selections) {
            fieldsNotInSchema.add(subFieldNode);
          }
        }
      } else {
        fieldNotInSchema = true;
        for (const subFieldNode of subFieldNodes) {
          fieldsNotInSchema.add(subFieldNode);
        }
      }
    } else {
      for (const subFieldNode of subFieldNodes) {
        const unavailableFields = extractUnavailableFields(
          sourceSchema,
          field,
          subFieldNode,
          (fieldType) => {
            if (stitchingInfo.mergedTypes[fieldType.name]?.resolvers.get(
              subschema
            )) {
              return false;
            }
            return true;
          },
          fragments
        );
        if (unavailableFields.length) {
          fieldNotInSchema = true;
          fieldsNotInSchema.add({
            ...subFieldNode,
            selectionSet: {
              kind: Kind.SELECTION_SET,
              selections: unavailableFields
            }
          });
        }
      }
    }
    const isComputedField = subschema.merge?.[gatewayType.name]?.fields?.[fieldName]?.computed;
    let addedSubFieldNodes = false;
    if ((isComputedField || fieldNotInSchema) && fieldNodesByFieldForType) {
      const visitedFieldNames = /* @__PURE__ */ new Set();
      addMissingRequiredFields({
        fieldName,
        fields,
        fieldsNotInSchema,
        visitedFieldNames,
        onAdd: () => {
          if (!addedSubFieldNodes) {
            for (const subFieldNode of subFieldNodes) {
              fieldsNotInSchema.add(subFieldNode);
            }
            addedSubFieldNodes = true;
          }
        },
        fieldNodesByField: fieldNodesByFieldForType
      });
    }
  }
  return Array.from(fieldsNotInSchema);
}
function addMissingRequiredFields({
  fieldName,
  fields,
  fieldsNotInSchema,
  onAdd,
  fieldNodesByField,
  visitedFieldNames
}) {
  if (visitedFieldNames.has(fieldName)) {
    return;
  }
  visitedFieldNames.add(fieldName);
  const fieldNodesForField = fieldNodesByField?.[fieldName];
  if (fieldNodesForField) {
    for (const fieldNode of fieldNodesForField) {
      if (fieldNode.name.value !== "__typename" && !fields[fieldNode.name.value]) {
        onAdd();
        fieldsNotInSchema.add(fieldNode);
        addMissingRequiredFields({
          fieldName: fieldNode.name.value,
          fields,
          fieldsNotInSchema,
          onAdd,
          fieldNodesByField,
          visitedFieldNames
        });
      }
    }
  }
}

function memoize5of7(fn) {
  const memoize5Cache = /* @__PURE__ */ new WeakMap();
  return function memoized(a1, a2, a3, a4, a5, a6, a7) {
    let cache2 = memoize5Cache.get(a1);
    if (!cache2) {
      cache2 = /* @__PURE__ */ new WeakMap();
      memoize5Cache.set(a1, cache2);
      const cache32 = /* @__PURE__ */ new WeakMap();
      cache2.set(a2, cache32);
      const cache42 = /* @__PURE__ */ new WeakMap();
      cache32.set(a3, cache42);
      const cache52 = /* @__PURE__ */ new WeakMap();
      cache42.set(a4, cache52);
      const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
      cache52.set(a5, newValue);
      return newValue;
    }
    let cache3 = cache2.get(a2);
    if (!cache3) {
      cache3 = /* @__PURE__ */ new WeakMap();
      cache2.set(a2, cache3);
      const cache42 = /* @__PURE__ */ new WeakMap();
      cache3.set(a3, cache42);
      const cache52 = /* @__PURE__ */ new WeakMap();
      cache42.set(a4, cache52);
      const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
      cache52.set(a5, newValue);
      return newValue;
    }
    let cache4 = cache3.get(a3);
    if (!cache4) {
      cache4 = /* @__PURE__ */ new WeakMap();
      cache3.set(a3, cache4);
      const cache52 = /* @__PURE__ */ new WeakMap();
      cache4.set(a4, cache52);
      const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
      cache52.set(a5, newValue);
      return newValue;
    }
    let cache5 = cache4.get(a4);
    if (!cache5) {
      cache5 = /* @__PURE__ */ new WeakMap();
      cache4.set(a4, cache5);
      const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
      cache5.set(a5, newValue);
      return newValue;
    }
    const cachedValue = cache5.get(a5);
    if (cachedValue === void 0) {
      const newValue = fn(a1, a2, a3, a4, a5, a6, a7);
      cache5.set(a5, newValue);
      return newValue;
    }
    return cachedValue;
  };
}

function calculateDelegationStage(mergedTypeInfo, sourceSubschemas, targetSubschemas, fieldNodes, fragments) {
  const { selectionSets, fieldSelectionSets, uniqueFields, nonUniqueFields } = mergedTypeInfo;
  const proxiableSubschemas = [];
  const nonProxiableSubschemas = [];
  for (const t of targetSubschemas) {
    const selectionSet = selectionSets.get(t);
    const fieldSelectionSetsMap = fieldSelectionSets.get(t);
    if (selectionSet != null && !subschemaTypesContainSelectionSet(
      mergedTypeInfo,
      sourceSubschemas,
      selectionSet
    )) {
      nonProxiableSubschemas.push(t);
    } else {
      if (fieldSelectionSetsMap == null || fieldNodes.every((fieldNode) => {
        const fieldName = fieldNode.name.value;
        const fieldSelectionSet = fieldSelectionSetsMap[fieldName];
        return fieldSelectionSet == null || subschemaTypesContainSelectionSet(
          mergedTypeInfo,
          sourceSubschemas,
          fieldSelectionSet
        );
      })) {
        proxiableSubschemas.push(t);
      } else {
        nonProxiableSubschemas.push(t);
      }
    }
  }
  const unproxiableFieldNodes = [];
  const delegationMap = /* @__PURE__ */ new Map();
  for (const fieldNode of fieldNodes) {
    const fieldName = fieldNode.name.value;
    if (fieldName === "__typename") {
      continue;
    }
    const sourcesWithUnsatisfiedDependencies = sourceSubschemas.filter(
      (s) => fieldSelectionSets.get(s) != null && fieldSelectionSets.get(s)[fieldName] != null && !subschemaTypesContainSelectionSet(
        mergedTypeInfo,
        sourceSubschemas,
        fieldSelectionSets.get(s)[fieldName]
      )
    );
    if (sourcesWithUnsatisfiedDependencies.length === sourceSubschemas.length) {
      unproxiableFieldNodes.push(fieldNode);
      for (const source of sourcesWithUnsatisfiedDependencies) {
        if (!nonProxiableSubschemas.includes(source)) {
          nonProxiableSubschemas.push(source);
        }
      }
      continue;
    }
    const uniqueSubschema = uniqueFields[fieldName];
    if (uniqueSubschema != null) {
      if (!proxiableSubschemas.includes(uniqueSubschema)) {
        unproxiableFieldNodes.push(fieldNode);
        continue;
      }
      const existingSubschema2 = delegationMap.get(uniqueSubschema)?.selections;
      if (existingSubschema2 != null) {
        existingSubschema2.push(fieldNode);
      } else {
        delegationMap.set(uniqueSubschema, {
          kind: Kind.SELECTION_SET,
          selections: [fieldNode]
        });
      }
      continue;
    }
    let nonUniqueSubschemas = nonUniqueFields[fieldNode.name.value];
    if (nonUniqueSubschemas == null) {
      unproxiableFieldNodes.push(fieldNode);
      continue;
    }
    nonUniqueSubschemas = nonUniqueSubschemas.filter(
      (s) => proxiableSubschemas.includes(s)
    );
    if (!nonUniqueSubschemas.length) {
      unproxiableFieldNodes.push(fieldNode);
      continue;
    }
    const existingSubschema = nonUniqueSubschemas.find(
      (s) => delegationMap.has(s)
    );
    if (existingSubschema != null) {
      delegationMap.get(existingSubschema).selections.push(fieldNode);
    } else {
      let bestUniqueSubschema = nonUniqueSubschemas[0];
      let bestScore = Infinity;
      for (const nonUniqueSubschema of nonUniqueSubschemas) {
        const typeInSubschema = nonUniqueSubschema.transformedSchema.getType(
          mergedTypeInfo.typeName
        );
        const fields = typeInSubschema.getFields();
        const field = fields[fieldNode.name.value];
        if (field != null) {
          const unavailableFields = extractUnavailableFields(
            nonUniqueSubschema.transformedSchema,
            field,
            fieldNode,
            (fieldType) => {
              if (!nonUniqueSubschema.merge?.[fieldType.name]) {
                let nonUniqueSubschemaSelections = (
                  // We have to cast it to `SelectionNode[]` because it is Readonly<SelectionNode[]> and it doesn't allow us to push new elements.
                  delegationMap.get(nonUniqueSubschema)?.selections
                );
                if (nonUniqueSubschemaSelections == null) {
                  nonUniqueSubschemaSelections = [];
                  delegationMap.set(nonUniqueSubschema, {
                    kind: Kind.SELECTION_SET,
                    selections: nonUniqueSubschemaSelections
                  });
                }
                nonUniqueSubschemaSelections.push(fieldNode);
                return false;
              }
              return true;
            }
          );
          const currentScore = calculateSelectionScore(
            unavailableFields,
            fragments
          );
          if (currentScore < bestScore) {
            bestScore = currentScore;
            bestUniqueSubschema = nonUniqueSubschema;
          }
        }
      }
      delegationMap.set(bestUniqueSubschema, {
        kind: Kind.SELECTION_SET,
        selections: [fieldNode]
      });
    }
  }
  if (delegationMap.size > 1) {
    optimizeDelegationMap(delegationMap, mergedTypeInfo.typeName, fragments);
  }
  return {
    delegationMap,
    proxiableSubschemas,
    nonProxiableSubschemas,
    unproxiableFieldNodes
  };
}
const calculateSelectionScore = memoize2(
  function calculateSelectionScore2(selections, fragments) {
    let score = 0;
    for (const selectionNode of selections) {
      switch (selectionNode.kind) {
        case Kind.FIELD:
          score++;
          if (selectionNode.selectionSet?.selections) {
            score += calculateSelectionScore2(
              selectionNode.selectionSet.selections,
              fragments
            );
          }
          break;
        case Kind.INLINE_FRAGMENT:
          score += calculateSelectionScore2(
            selectionNode.selectionSet.selections,
            fragments
          );
          break;
        case Kind.FRAGMENT_SPREAD:
          const fragment = fragments?.[selectionNode.name.value];
          if (fragment) {
            score += calculateSelectionScore2(
              fragment.selectionSet.selections,
              fragments
            );
          }
          break;
      }
    }
    return score;
  }
);
function getStitchingInfo(schema) {
  const stitchingInfo = schema.extensions?.["stitchingInfo"];
  if (!stitchingInfo) {
    throw new Error(`Schema is not a stitched schema.`);
  }
  return stitchingInfo;
}
function createDelegationPlanBuilder(mergedTypeInfo) {
  mergedTypeInfo.nonMemoizedDelegationPlanBuilder = function delegationPlanBuilder(schema, sourceSubschema, variableValues, fragments, fieldNodes, _context, info) {
    const stitchingInfo = getStitchingInfo(schema);
    const targetSubschemas = mergedTypeInfo?.targetSubschemas.get(sourceSubschema);
    if (!targetSubschemas || !targetSubschemas.length) {
      return [];
    }
    const typeName = mergedTypeInfo.typeName;
    const typeInSubschema = sourceSubschema.transformedSchema.getType(
      typeName
    );
    let providedSelectionNode;
    const parentFieldName = fieldNodes[0]?.name.value;
    if (info?.parentType && parentFieldName) {
      const providedSelectionsByField = stitchingInfo.mergedTypes[info.parentType.name]?.providedSelectionsByField?.get(sourceSubschema);
      providedSelectionNode = providedSelectionsByField?.[parentFieldName];
    }
    const fieldsNotInSubschema = getFieldsNotInSubschema(
      schema,
      stitchingInfo,
      schema.getType(typeName),
      mergedTypeInfo.typeMaps.get(sourceSubschema)?.[typeName],
      fieldNodes,
      fragments,
      variableValues,
      sourceSubschema,
      providedSelectionNode
    );
    if (!fieldsNotInSubschema.length) {
      return [];
    }
    const delegationMaps = [];
    let sourceSubschemas = createSubschemas(sourceSubschema);
    let delegationStage = calculateDelegationStage(
      mergedTypeInfo,
      sourceSubschemas,
      targetSubschemas,
      fieldsNotInSubschema,
      fragments
    );
    let { delegationMap } = delegationStage;
    while (delegationMap.size) {
      delegationMaps.push(delegationMap);
      const {
        proxiableSubschemas,
        nonProxiableSubschemas,
        unproxiableFieldNodes
      } = delegationStage;
      sourceSubschemas = combineSubschemas(
        sourceSubschemas,
        proxiableSubschemas
      );
      delegationStage = calculateDelegationStage(
        mergedTypeInfo,
        sourceSubschemas,
        nonProxiableSubschemas,
        unproxiableFieldNodes,
        fragments
      );
      delegationMap = delegationStage.delegationMap;
    }
    if (isAbstractType(typeInSubschema) && fieldsNotInSubschema.some(
      (fieldNode) => fieldNode.name.value === "__typename"
    )) {
      const inlineFragments = [];
      for (const fieldNode of fieldNodes) {
        if (fieldNode.selectionSet) {
          for (const selection of fieldNode.selectionSet.selections) {
            if (selection.kind === Kind.INLINE_FRAGMENT) {
              inlineFragments.push(selection);
            }
          }
        }
      }
      const implementedSubschemas = targetSubschemas.filter((subschema) => {
        const typeInTargetSubschema = mergedTypeInfo.typeMaps.get(subschema)?.[typeName];
        return isAbstractType(typeInTargetSubschema) && subschema.transformedSchema.getPossibleTypes(typeInTargetSubschema).length;
      });
      let added = false;
      for (const implementedSubgraphs of implementedSubschemas) {
        for (const delegationMap2 of delegationMaps) {
          const existingSelections = delegationMap2.get(implementedSubgraphs)?.selections;
          if (existingSelections) {
            existingSelections.push({
              kind: Kind.FIELD,
              name: {
                kind: Kind.NAME,
                value: "__typename"
              }
            });
            existingSelections.push(...inlineFragments);
            added = true;
            break;
          }
          if (added) {
            break;
          }
        }
      }
      if (!added) {
        const subschemaWithTypeName = implementedSubschemas[0];
        if (subschemaWithTypeName) {
          const delegationStageToFetchTypeName = /* @__PURE__ */ new Map();
          delegationStageToFetchTypeName.set(subschemaWithTypeName, {
            kind: Kind.SELECTION_SET,
            selections: [
              {
                kind: Kind.FIELD,
                name: {
                  kind: Kind.NAME,
                  value: "__typename"
                }
              },
              ...inlineFragments
            ]
          });
          delegationMaps.push(delegationStageToFetchTypeName);
        }
      }
    }
    if (delegationStage.unproxiableFieldNodes.length && delegationStage.nonProxiableSubschemas.length) {
      leftOverByDelegationPlan.set(delegationMaps, {
        unproxiableFieldNodes: delegationStage.unproxiableFieldNodes,
        nonProxiableSubschemas: delegationStage.nonProxiableSubschemas,
        missingFieldsParentMap: /* @__PURE__ */ new Map(),
        missingFieldsParentDeferredMap: /* @__PURE__ */ new Map()
      });
    }
    return delegationMaps;
  };
  return memoize5of7(function wrappedDelegationPlanBuilder(schema, sourceSubschema, variableValues, fragments, fieldNodes, context, info) {
    return mergedTypeInfo.nonMemoizedDelegationPlanBuilder(
      schema,
      sourceSubschema,
      variableValues,
      fragments,
      fieldNodes,
      context,
      info
    );
  });
}
function optimizeDelegationMap(delegationMap, typeName, fragments) {
  for (const [subschema, selectionSet] of delegationMap) {
    for (const [subschema2, selectionSet2] of delegationMap) {
      if (subschema === subschema2) {
        continue;
      }
      const unavailableFields = extractUnavailableFieldsFromSelectionSet(
        subschema2.transformedSchema,
        // Unfortunately, getType returns GraphQLNamedType, but we already know the type is a GraphQLObjectType, so we can cast it.
        subschema2.transformedSchema.getType(typeName),
        selectionSet,
        () => true,
        fragments
      );
      if (!unavailableFields.length) {
        delegationMap.set(subschema2, {
          kind: Kind.SELECTION_SET,
          selections: [...selectionSet2.selections, ...selectionSet.selections]
        });
        delegationMap.delete(subschema);
      }
    }
  }
  return delegationMap;
}
const createSubschemas = memoize1(function createSubschemas2(sourceSubschema) {
  return [sourceSubschema];
});
const combineSubschemas = memoize2(function combineSubschemas2(sourceSubschemas, additionalSubschemas) {
  return sourceSubschemas.concat(additionalSubschemas);
});
const subschemaTypesContainSelectionSet = memoize3(
  function subschemaTypesContainSelectionSet2(mergedTypeInfo, sourceSubchemas, selectionSet) {
    return typesContainSelectionSet(
      sourceSubchemas.map(
        (sourceSubschema) => sourceSubschema.transformedSchema.getType(
          mergedTypeInfo.typeName
        )
      ),
      selectionSet
    );
  }
);
function typesContainSelectionSet(types, selectionSet) {
  const fieldMaps = types.map((type) => type.getFields());
  for (const selection of selectionSet.selections) {
    if (selection.kind === Kind.FIELD) {
      const fields = fieldMaps.map((fieldMap) => fieldMap[selection.name.value]).filter((field) => field != null);
      if (!fields.length) {
        return false;
      }
      if (selection.selectionSet != null) {
        return typesContainSelectionSet(
          fields.map(
            (field) => getNamedType(field.type)
          ),
          selection.selectionSet
        );
      }
    } else if (selection.kind === Kind.INLINE_FRAGMENT && selection.typeCondition?.name.value === types[0]?.name) {
      return typesContainSelectionSet(types, selection.selectionSet);
    }
  }
  return true;
}

function createMergedTypeResolver(mergedTypeResolverOptions, mergedType) {
  const { fieldName, argsFromKeys, valuesFromResults, args } = mergedTypeResolverOptions;
  function getType(info) {
    if (!mergedType) {
      return getNamedType(info.returnType);
    }
    if (typeof mergedType === "string") {
      return info.schema.getType(mergedType);
    }
    return mergedType;
  }
  if (argsFromKeys != null) {
    return function mergedBatchedTypeResolver(_originalResult, context, info, subschema, selectionSet, key, type = getType(info)) {
      return batchDelegateToSchema({
        schema: subschema,
        operation: "query",
        fieldName,
        returnType: new GraphQLList(type),
        key,
        argsFromKeys,
        valuesFromResults,
        selectionSet,
        context,
        info,
        skipTypeMerging: true,
        dataLoaderOptions: mergedTypeResolverOptions.dataLoaderOptions
      });
    };
  }
  if (args != null) {
    return function mergedTypeResolver(originalResult, context, info, subschema, selectionSet, _key, type = getType(info)) {
      return delegateToSchema({
        schema: subschema,
        operation: "query",
        fieldName,
        returnType: type,
        args: args(originalResult),
        selectionSet,
        context,
        info,
        skipTypeMerging: true
      });
    };
  }
  return void 0;
}

function createStitchingInfo(subschemaMap, typeCandidates, mergeTypes) {
  const mergedTypes = createMergedTypes(typeCandidates, mergeTypes);
  return {
    subschemaMap,
    fieldNodesByType: /* @__PURE__ */ Object.create(null),
    fieldNodesByField: /* @__PURE__ */ Object.create(null),
    dynamicSelectionSetsByField: /* @__PURE__ */ Object.create(null),
    mergedTypes
  };
}
function createMergedTypes(typeCandidates, mergeTypes) {
  const mergedTypes = /* @__PURE__ */ Object.create(
    null
  );
  const typeInterfacesMap = /* @__PURE__ */ Object.create(null);
  for (const typeName in typeCandidates) {
    if (typeCandidates[typeName]) {
      for (const { type } of typeCandidates[typeName]) {
        if ("getInterfaces" in type) {
          const interfaces = type.getInterfaces();
          for (const iface of interfaces) {
            const interfaceName = iface.name;
            let implementingTypes = typeInterfacesMap[typeName];
            if (implementingTypes == null) {
              implementingTypes = /* @__PURE__ */ new Set();
              typeInterfacesMap[typeName] = implementingTypes;
            }
            implementingTypes.add(interfaceName);
          }
        }
      }
    }
  }
  for (const typeName in typeCandidates) {
    const typeCandidatesOfTypeName = typeCandidates[typeName];
    if (!typeCandidatesOfTypeName) {
      throw new Error(`Invalid type candidates for type name ${typeName}`);
    }
    const typeCandidate = typeCandidatesOfTypeName[0];
    if (isObjectType(typeCandidate?.type) || isInterfaceType(typeCandidate?.type)) {
      const typeCandidatesWithMergedTypeConfig = typeCandidatesOfTypeName.filter(
        (typeCandidate2) => typeCandidate2.transformedSubschema != null && typeCandidate2.transformedSubschema.merge != null && typeName in typeCandidate2.transformedSubschema.merge
      );
      if (mergeTypes === true || typeof mergeTypes === "function" && mergeTypes(typeCandidatesOfTypeName, typeName) || Array.isArray(mergeTypes) && mergeTypes.includes(typeName) || typeCandidatesWithMergedTypeConfig.length) {
        const targetSubschemas = [];
        const typeMaps = /* @__PURE__ */ new Map();
        const supportedBySubschemas = /* @__PURE__ */ Object.create({});
        const selectionSets = /* @__PURE__ */ new Map();
        const fieldSelectionSets = /* @__PURE__ */ new Map();
        const resolvers = /* @__PURE__ */ new Map();
        const providedSelectionsByField = /* @__PURE__ */ new Map();
        for (const typeCandidate2 of typeCandidatesOfTypeName) {
          const subschema = typeCandidate2.transformedSubschema;
          if (subschema == null) {
            continue;
          }
          typeMaps.set(subschema, subschema.transformedSchema.getTypeMap());
          let mergedTypeConfig2 = subschema?.merge?.[typeName];
          if (!mergedTypeConfig2) {
            for (const interfaceName of typeInterfacesMap[typeName] ?? []) {
              mergedTypeConfig2 = subschema?.merge?.[interfaceName];
              if (mergedTypeConfig2) {
                break;
              }
            }
          }
          if (mergedTypeConfig2 == null) {
            continue;
          }
          if (mergedTypeConfig2.selectionSet) {
            const selectionSet2 = parseSelectionSet(
              mergedTypeConfig2.selectionSet,
              {
                noLocation: true
              }
            );
            selectionSets.set(subschema, selectionSet2);
          }
          if (mergedTypeConfig2.fields) {
            const parsedFieldSelectionSets = /* @__PURE__ */ Object.create(null);
            for (const fieldName in mergedTypeConfig2.fields) {
              if (mergedTypeConfig2.fields[fieldName]?.selectionSet) {
                const rawFieldSelectionSet = mergedTypeConfig2.fields[fieldName].selectionSet;
                parsedFieldSelectionSets[fieldName] = rawFieldSelectionSet ? parseSelectionSet(rawFieldSelectionSet, {
                  noLocation: true
                }) : void 0;
              }
              let providedSelectionSet = mergedTypeConfig2.fields[fieldName]?.provides;
              if (providedSelectionSet) {
                providedSelectionSet = visit(providedSelectionSet, {
                  [Kind.SELECTION_SET](node) {
                    const typeNameField = node.selections.find(
                      (selection) => selection.kind === Kind.FIELD && selection.name.value === "__typename"
                    );
                    if (typeNameField) {
                      return node;
                    }
                    return {
                      ...node,
                      selections: [
                        ...node.selections,
                        {
                          kind: Kind.FIELD,
                          name: {
                            kind: Kind.NAME,
                            value: "__typename"
                          }
                        }
                      ]
                    };
                  }
                });
                let providedSelectionsForSubschema = providedSelectionsByField.get(subschema);
                if (providedSelectionsForSubschema == null) {
                  providedSelectionsForSubschema = /* @__PURE__ */ Object.create({});
                  providedSelectionsByField.set(
                    subschema,
                    providedSelectionsForSubschema
                  );
                }
                providedSelectionsForSubschema[fieldName] = providedSelectionSet;
              }
            }
            fieldSelectionSets.set(subschema, parsedFieldSelectionSets);
          }
          const type = subschema.transformedSchema.getType(typeName);
          const resolver = mergedTypeConfig2.resolve ?? createMergedTypeResolver(mergedTypeConfig2, type);
          if (resolver == null) {
            continue;
          }
          const keyFn = mergedTypeConfig2.key;
          resolvers.set(
            subschema,
            keyFn ? function batchMergedTypeResolverWrapper(originalResult, context, info, subschema2, selectionSet2, type2) {
              return handleMaybePromise(
                () => keyFn(originalResult),
                (key) => resolver(
                  originalResult,
                  context,
                  info,
                  subschema2,
                  selectionSet2,
                  key,
                  type2
                )
              );
            } : resolver
          );
          targetSubschemas.push(subschema);
          const fieldMap = type.getFields();
          const selectionSet = selectionSets.get(subschema);
          for (const fieldName in fieldMap) {
            const field = fieldMap[fieldName];
            const fieldType = getNamedType(field?.type);
            if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) {
              continue;
            }
            if (!supportedBySubschemas[fieldName]) {
              supportedBySubschemas[fieldName] = [];
            }
            supportedBySubschemas[fieldName]?.push(subschema);
          }
        }
        const sourceSubschemas = typeCandidates[typeName]?.map((typeCandidate2) => typeCandidate2?.transformedSubschema).filter(isSome);
        const targetSubschemasBySubschema = /* @__PURE__ */ new Map();
        if (sourceSubschemas) {
          for (const subschema of sourceSubschemas) {
            const filteredSubschemas = targetSubschemas.filter(
              (s) => s !== subschema
            );
            if (filteredSubschemas.length) {
              targetSubschemasBySubschema.set(subschema, filteredSubschemas);
            }
          }
        }
        const mergedTypeConfig = {
          typeName,
          targetSubschemas: targetSubschemasBySubschema,
          typeMaps,
          selectionSets,
          fieldSelectionSets,
          uniqueFields: /* @__PURE__ */ Object.create({}),
          nonUniqueFields: /* @__PURE__ */ Object.create({}),
          resolvers,
          providedSelectionsByField
        };
        mergedTypes[typeName] = mergedTypeConfig;
        mergedTypeConfig.delegationPlanBuilder = createDelegationPlanBuilder(
          mergedTypeConfig
        );
        for (const fieldName in supportedBySubschemas) {
          if (supportedBySubschemas[fieldName]?.length === 1 && supportedBySubschemas[fieldName][0]) {
            mergedTypeConfig.uniqueFields[fieldName] = supportedBySubschemas[fieldName][0];
          } else if (supportedBySubschemas[fieldName]) {
            mergedTypeConfig.nonUniqueFields[fieldName] = supportedBySubschemas[fieldName];
          }
        }
      }
    }
  }
  return mergedTypes;
}
function completeStitchingInfo(stitchingInfo, resolvers, schema) {
  const {
    fieldNodesByType,
    fieldNodesByField,
    dynamicSelectionSetsByField,
    mergedTypes
  } = stitchingInfo;
  const rootTypes = [schema.getQueryType(), schema.getMutationType()];
  for (const rootType of rootTypes) {
    if (rootType) {
      fieldNodesByType[rootType.name] = [
        parseSelectionSet("{ __typename }", { noLocation: true }).selections[0]
      ];
    }
  }
  const selectionSetsByField = /* @__PURE__ */ Object.create(null);
  for (const typeName in mergedTypes) {
    const mergedTypeInfo = mergedTypes[typeName];
    if (mergedTypeInfo?.selectionSets == null && mergedTypeInfo?.fieldSelectionSets == null) {
      continue;
    }
    for (const [
      subschemaConfig,
      selectionSet
    ] of mergedTypeInfo.selectionSets) {
      const schema2 = subschemaConfig.transformedSchema;
      const type = schema2.getType(typeName);
      const fields = type.getFields();
      for (const fieldName in fields) {
        const field = fields[fieldName];
        const fieldType = getNamedType(field?.type);
        if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) {
          continue;
        }
        updateSelectionSetMap(
          selectionSetsByField,
          typeName,
          fieldName,
          selectionSet,
          true
        );
      }
      if (isAbstractType(type)) {
        updateSelectionSetMap(
          selectionSetsByField,
          typeName,
          "__typename",
          selectionSet
        );
      }
    }
    for (const [, selectionSetFieldMap] of mergedTypeInfo.fieldSelectionSets) {
      for (const fieldName in selectionSetFieldMap) {
        const selectionSet = selectionSetFieldMap[fieldName];
        if (selectionSet) {
          updateSelectionSetMap(
            selectionSetsByField,
            typeName,
            fieldName,
            selectionSet,
            true
          );
        }
      }
    }
  }
  for (const typeName in resolvers) {
    const type = schema.getType(typeName);
    if (type === void 0 || isLeafType(type) || isInputObjectType(type) || isUnionType(type)) {
      continue;
    }
    const resolver = resolvers[typeName];
    for (const fieldName in resolver) {
      const field = resolver[fieldName];
      if (typeof field.selectionSet === "function") {
        if (!dynamicSelectionSetsByField[typeName]) {
          dynamicSelectionSetsByField[typeName] = /* @__PURE__ */ Object.create(null);
        }
        if (!dynamicSelectionSetsByField[typeName][fieldName]) {
          dynamicSelectionSetsByField[typeName][fieldName] = [];
        }
        dynamicSelectionSetsByField[typeName][fieldName].push(
          field.selectionSet
        );
      } else if (field.selectionSet) {
        const selectionSet = parseSelectionSet(field.selectionSet, {
          noLocation: true
        });
        updateSelectionSetMap(
          selectionSetsByField,
          typeName,
          fieldName,
          selectionSet
        );
      }
    }
  }
  const variableValues = /* @__PURE__ */ Object.create(null);
  const fragments = /* @__PURE__ */ Object.create(null);
  const fieldNodeMap = /* @__PURE__ */ Object.create(null);
  for (const typeName in selectionSetsByField) {
    const type = schema.getType(typeName);
    for (const fieldName in selectionSetsByField[typeName]) {
      for (const selectionSet of selectionSetsByField[typeName][fieldName]) {
        const { fields } = collectFields(
          schema,
          fragments,
          variableValues,
          type,
          selectionSet
        );
        for (const [, fieldNodes] of fields) {
          for (const fieldNode of fieldNodes) {
            const key = print(fieldNode);
            if (fieldNodeMap[key] == null) {
              fieldNodeMap[key] = fieldNode;
              updateArrayMap(fieldNodesByField, typeName, fieldName, fieldNode);
            } else {
              updateArrayMap(
                fieldNodesByField,
                typeName,
                fieldName,
                fieldNodeMap[key]
              );
            }
          }
        }
      }
    }
  }
  return stitchingInfo;
}
function updateSelectionSetMap(map, typeName, fieldName, selectionSet, includeTypename) {
  if (includeTypename) {
    const typenameSelectionSet = parseSelectionSet("{ __typename }", {
      noLocation: true
    });
    updateArrayMap(
      map,
      typeName,
      fieldName,
      selectionSet,
      typenameSelectionSet
    );
    return;
  }
  updateArrayMap(map, typeName, fieldName, selectionSet);
}
function updateArrayMap(map, typeName, fieldName, value, initialValue) {
  if (map[typeName] == null) {
    const initialItems = initialValue === void 0 ? [value] : [initialValue, value];
    map[typeName] = {
      [fieldName]: initialItems
    };
  } else if (map[typeName][fieldName] == null) {
    const initialItems = initialValue === void 0 ? [value] : [initialValue, value];
    map[typeName][fieldName] = initialItems;
  } else {
    map[typeName][fieldName].push(value);
  }
}
function addStitchingInfo(stitchedSchema, stitchingInfo) {
  stitchedSchema.extensions = {
    ...stitchedSchema.extensions,
    stitchingInfo
  };
}
function selectionSetContainsTopLevelField(selectionSet, fieldName) {
  return selectionSet.selections.some(
    (selection) => selection.kind === Kind.FIELD && selection.name.value === fieldName
  );
}

function isolateComputedFieldsTransformer(subschemaConfig) {
  if (subschemaConfig.merge == null) {
    return [subschemaConfig];
  }
  const baseSchemaTypes = /* @__PURE__ */ Object.create(null);
  const isolatedSchemaTypes = /* @__PURE__ */ Object.create(null);
  for (const typeName in subschemaConfig.merge) {
    const mergedTypeConfig = subschemaConfig.merge[typeName];
    const objectType = subschemaConfig.schema.getType(
      typeName
    );
    baseSchemaTypes[typeName] = mergedTypeConfig;
    if (mergedTypeConfig.fields) {
      const baseFields = /* @__PURE__ */ Object.create(null);
      const isolatedFields = /* @__PURE__ */ Object.create(null);
      for (const fieldName in mergedTypeConfig.fields) {
        const mergedFieldConfig = mergedTypeConfig.fields[fieldName];
        if (mergedFieldConfig?.computed && mergedFieldConfig?.selectionSet) {
          isolatedFields[fieldName] = mergedFieldConfig;
        } else if (mergedFieldConfig?.computed) {
          throw new Error(
            `A selectionSet is required for computed field "${typeName}.${fieldName}"`
          );
        } else {
          baseFields[fieldName] = mergedFieldConfig;
        }
      }
      const isolatedFieldCount = Object.keys(isolatedFields).length;
      if (isolatedFieldCount && isolatedFieldCount !== Object.keys(objectType.getFields()).length) {
        baseSchemaTypes[typeName] = {
          ...mergedTypeConfig,
          fields: baseFields
        };
        const keyFieldNames = isolatedSchemaTypes[typeName]?.keyFieldNames ?? [];
        if (keyFieldNames.length === 0) {
          if (mergedTypeConfig.selectionSet) {
            const parsedSelectionSet = parseSelectionSet(
              mergedTypeConfig.selectionSet,
              { noLocation: true }
            );
            const keyFields = collectFields(
              subschemaConfig.schema,
              {},
              {},
              objectType,
              parsedSelectionSet
            );
            keyFieldNames.push(...Array.from(keyFields.fields.keys()));
          }
          for (const entryPoint of mergedTypeConfig.entryPoints ?? []) {
            if (entryPoint.selectionSet) {
              const parsedSelectionSet = parseSelectionSet(
                entryPoint.selectionSet,
                { noLocation: true }
              );
              const keyFields = collectFields(
                subschemaConfig.schema,
                {},
                {},
                objectType,
                parsedSelectionSet
              );
              keyFieldNames.push(...Array.from(keyFields.fields.keys()));
            }
          }
        }
        isolatedSchemaTypes[typeName] = {
          ...mergedTypeConfig,
          // there might already be key fields
          keyFieldNames,
          fields: {
            ...isolatedSchemaTypes[typeName]?.fields ?? {},
            ...isolatedFields
          },
          canonical: void 0
        };
        for (const fieldName in isolatedFields) {
          const returnType = getNamedType(
            objectType.getFields()[fieldName]?.type
          );
          const returnTypes = [returnType];
          if (isInterfaceType(returnType)) {
            returnTypes.push(
              ...getImplementingTypes(
                returnType.name,
                subschemaConfig.schema
              ).map(
                (name) => subschemaConfig.schema.getType(
                  name
                )
              )
            );
          } else if (isUnionType(returnType)) {
            returnTypes.push(...returnType.getTypes());
          }
          for (const type of returnTypes) {
            const returnTypeMergeConfig = subschemaConfig.merge[type.name];
            if (Object.values(subschemaConfig.schema.getTypeMap()).filter(isObjectType).filter((t) => t !== type).filter((t) => !isolatedSchemaTypes[t.name]).find(
              (t) => Object.values(t.getFields()).find(
                (f) => getNamedType(f.type) === type
              )
            )) {
              continue;
            }
            if (isObjectType(type)) {
              const returnTypeSelectionSet = returnTypeMergeConfig?.selectionSet;
              if (returnTypeSelectionSet) {
                const keyFieldNames2 = [];
                const parsedSelectionSet = parseSelectionSet(
                  returnTypeSelectionSet,
                  { noLocation: true }
                );
                const keyFields = collectFields(
                  subschemaConfig.schema,
                  {},
                  {},
                  type,
                  parsedSelectionSet
                );
                keyFieldNames2.push(...Array.from(keyFields.fields.keys()));
                for (const entryPoint of returnTypeMergeConfig.entryPoints ?? []) {
                  if (entryPoint.selectionSet) {
                    const parsedSelectionSet2 = parseSelectionSet(
                      entryPoint.selectionSet,
                      { noLocation: true }
                    );
                    const keyFields2 = collectFields(
                      subschemaConfig.schema,
                      {},
                      {},
                      type,
                      parsedSelectionSet2
                    );
                    keyFieldNames2.push(...Array.from(keyFields2.fields.keys()));
                  }
                }
                isolatedSchemaTypes[type.name] = {
                  ...returnTypeMergeConfig,
                  keyFieldNames: keyFieldNames2,
                  fields: {
                    ...isolatedSchemaTypes[type.name]?.fields ?? {}
                  }
                };
              } else if (!returnTypeMergeConfig) {
                const fields = {
                  ...isolatedSchemaTypes[type.name]?.fields
                };
                if (isAbstractType(type)) {
                  for (const implementingType of getImplementingTypes(
                    type.name,
                    subschemaConfig.schema
                  )) {
                    const implementingTypeFields = isolatedSchemaTypes[implementingType]?.fields;
                    if (implementingTypeFields) {
                      for (const fieldName2 in implementingTypeFields) {
                        if (implementingTypeFields[fieldName2]) {
                          fields[fieldName2] = {
                            ...implementingTypeFields[fieldName2],
                            ...fields[fieldName2]
                          };
                        }
                      }
                    }
                  }
                }
                if (isInterfaceType(type) || isObjectType(type)) {
                  for (const fieldName2 in type.getFields()) {
                    fields[fieldName2] ||= {};
                  }
                }
                isolatedSchemaTypes[type.name] = {
                  keyFieldNames: [],
                  fields,
                  canonical: true
                };
              }
            }
          }
        }
      }
    }
  }
  if (Object.keys(isolatedSchemaTypes).length) {
    return [
      filterIsolatedSubschema(subschemaConfig, isolatedSchemaTypes),
      filterBaseSubschema(
        { ...subschemaConfig, merge: baseSchemaTypes },
        isolatedSchemaTypes
      )
    ];
  }
  return [subschemaConfig];
}
function _createCompositeFieldFilter(schema) {
  const filteredFields = {};
  for (const typeName in schema.getTypeMap()) {
    const type = schema.getType(typeName);
    if (isObjectType(type) || isInterfaceType(type)) {
      const filteredFieldsOfType = {
        __typename: true
      };
      let hasField = false;
      const fieldMap = type.getFields();
      for (const fieldName in fieldMap) {
        filteredFieldsOfType[fieldName] = true;
        hasField = true;
      }
      if (hasField) {
        filteredFields[typeName] = filteredFieldsOfType;
      }
    }
  }
  return new TransformCompositeFields(
    (typeName, fieldName) => filteredFields[typeName]?.[fieldName] ? void 0 : null,
    (typeName, fieldName) => filteredFields[typeName]?.[fieldName] ? void 0 : null
  );
}
function isIsolatedField(typeName, fieldName, isolatedSchemaTypes) {
  const fieldConfig = isolatedSchemaTypes[typeName]?.fields?.[fieldName];
  if (fieldConfig) {
    return true;
  }
  return false;
}
function filterBaseSubschema(subschemaConfig, isolatedSchemaTypes) {
  const schema = subschemaConfig.schema;
  const typesForInterface = {};
  const iFacesForTypes = {};
  const filteredSchema = filterSchema({
    schema,
    objectFieldFilter: (typeName, fieldName) => {
      const iFacesForType = iFacesForTypes[typeName] ||= [];
      if (!iFacesForType) {
        let addIface2 = function(iFace) {
          if (!iFacesForType.includes(iFace.name)) {
            iFacesForType.push(iFace.name);
            iFace.getInterfaces().forEach(addIface2);
          }
        };
        const type = schema.getType(typeName);
        let iFaces = type.getInterfaces();
        for (const iface of iFaces) {
          addIface2(iface);
        }
      }
      const allTypes = [typeName, ...iFacesForType];
      const isIsolatedFieldName = allTypes.every(
        (implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
      );
      const isKeyFieldName = allTypes.some(
        (implementingTypeName) => (isolatedSchemaTypes[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
      );
      return !isIsolatedFieldName || isKeyFieldName;
    },
    interfaceFieldFilter: (typeName, fieldName) => {
      if (!typesForInterface[typeName]) {
        typesForInterface[typeName] = getImplementingTypes(typeName, schema);
      }
      const iFacesForType = iFacesForTypes[typeName] ||= [];
      if (!iFacesForType) {
        let addIface2 = function(iFace) {
          if (!iFacesForType.includes(iFace.name)) {
            iFacesForType.push(iFace.name);
            iFace.getInterfaces().forEach(addIface2);
          }
        };
        const type = schema.getType(typeName);
        let iFaces = type.getInterfaces();
        for (const iface of iFaces) {
          addIface2(iface);
        }
      }
      const allTypes = [
        typeName,
        ...iFacesForType,
        ...typesForInterface[typeName]
      ];
      const isIsolatedFieldName = allTypes.every(
        (implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
      );
      const isKeyFieldName = allTypes.some(
        (implementingTypeName) => (isolatedSchemaTypes[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
      );
      return !isIsolatedFieldName || isKeyFieldName;
    }
  });
  const filteredSubschema = {
    ...subschemaConfig,
    merge: subschemaConfig.merge ? {
      ...subschemaConfig.merge
    } : void 0,
    transforms: (subschemaConfig.transforms ?? []).concat([
      _createCompositeFieldFilter(filteredSchema),
      new FilterTypes((type) => {
        const typeName = type.name;
        const typeInFiltered = filteredSchema.getType(typeName);
        if (!typeInFiltered) {
          return false;
        }
        if (isObjectType(type) || isInterfaceType(type)) {
          return Object.keys(type.getFields()).length > 0;
        }
        return true;
      })
    ])
  };
  const remainingTypes = filteredSchema.getTypeMap();
  const mergeConfig = filteredSubschema.merge;
  if (mergeConfig) {
    for (const mergeType in mergeConfig) {
      if (!remainingTypes[mergeType]) {
        delete mergeConfig[mergeType];
      }
    }
    if (!Object.keys(mergeConfig).length) {
      delete filteredSubschema.merge;
    }
  }
  return filteredSubschema;
}
function filterIsolatedSubschema(subschemaConfig, isolatedSchemaTypes) {
  const computedFieldTypes = {};
  const queryRootFields = {};
  function listReachableTypesToIsolate(subschemaConfig2, type, typeNames = /* @__PURE__ */ new Set()) {
    if (isScalarType(type)) {
      return typeNames;
    } else if ((isObjectType(type) || isInterfaceType(type)) && subschemaConfig2.merge?.[type.name]) {
      typeNames.add(type.name);
      return typeNames;
    } else if (isCompositeType(type)) {
      typeNames.add(type.name);
      const types = /* @__PURE__ */ new Set();
      if (isObjectType(type)) {
        types.add(type);
      } else if (isInterfaceType(type)) {
        getImplementingTypes(type.name, subschemaConfig2.schema).forEach(
          (name) => types.add(
            subschemaConfig2.schema.getType(name)
          )
        );
      } else if (isUnionType(type)) {
        type.getTypes().forEach((t) => types.add(t));
      }
      for (const type2 of types) {
        typeNames.add(type2.name);
        for (const f of Object.values(type2.getFields())) {
          const fieldType = getNamedType(f.type);
          if (!typeNames.has(fieldType.name) && isCompositeType(fieldType)) {
            listReachableTypesToIsolate(subschemaConfig2, fieldType, typeNames);
          }
        }
      }
      return typeNames;
    } else if (isUnionType(type)) {
      typeNames.add(type.name);
      type.getTypes().forEach(
        (t) => listReachableTypesToIsolate(subschemaConfig2, t, typeNames)
      );
      return typeNames;
    } else {
      return typeNames;
    }
  }
  const queryType = subschemaConfig.schema.getQueryType();
  for (const typeName in subschemaConfig.merge) {
    const mergedTypeConfig = subschemaConfig.merge[typeName];
    const entryPoints = mergedTypeConfig?.entryPoints ?? [mergedTypeConfig];
    const queryTypeFields = queryType?.getFields();
    for (const entryPoint of entryPoints) {
      if (entryPoint?.fieldName != null) {
        queryRootFields[entryPoint.fieldName] = true;
        const rootField = queryTypeFields?.[entryPoint.fieldName];
        if (rootField) {
          const rootFieldType = getNamedType(rootField.type);
          computedFieldTypes[rootFieldType.name] = true;
          if (isInterfaceType(rootFieldType)) {
            getImplementingTypes(
              rootFieldType.name,
              subschemaConfig.schema
            ).forEach((tn) => {
              computedFieldTypes[tn] = true;
            });
          }
        }
      }
    }
    const computedFields = [
      ...Object.entries(mergedTypeConfig?.fields || {}).map(([k, v]) => v.computed ? k : null).filter((fn) => fn !== null)
    ].filter((fn) => !queryRootFields[fn]);
    const type = subschemaConfig.schema.getType(typeName);
    for (const fieldName of computedFields) {
      const fieldType = getNamedType(type.getFields()[fieldName].type);
      computedFieldTypes[fieldType.name] = true;
      listReachableTypesToIsolate(subschemaConfig, fieldType).forEach((tn) => {
        computedFieldTypes[tn] = true;
      });
    }
  }
  const rootTypeNames = getRootTypeNames(subschemaConfig.schema);
  const typesForInterface = {};
  const iFaceForTypes = {};
  const filteredSchema = filterSchema({
    schema: subschemaConfig.schema,
    rootFieldFilter: (typeName, fieldName, config) => {
      if (rootTypeNames.has(typeName)) {
        if (queryType?.name === typeName) {
          if (queryRootFields[fieldName]) {
            return true;
          }
        } else {
          return true;
        }
      }
      const returnType = getNamedType(config.type);
      if (isAbstractType(returnType)) {
        const typesForInterface2 = [
          returnType.name,
          ...getImplementingTypes(returnType.name, subschemaConfig.schema)
        ];
        return typesForInterface2.some((t) => computedFieldTypes[t] != null);
      }
      return computedFieldTypes[returnType.name] != null;
    },
    objectFieldFilter: (typeName, fieldName, config) => {
      if (computedFieldTypes[typeName]) {
        return true;
      }
      if (!iFaceForTypes[typeName]) {
        iFaceForTypes[typeName] = subschemaConfig.schema.getType(typeName).getInterfaces().map((iFace) => iFace.name);
      }
      if (iFaceForTypes[typeName].some((iFace) => computedFieldTypes[iFace])) {
        return true;
      }
      const fieldType = getNamedType(config.type);
      if (computedFieldTypes[fieldType.name]) {
        return true;
      }
      return subschemaConfig.merge?.[typeName] == null || subschemaConfig.merge[typeName]?.fields?.[fieldName] != null || (isolatedSchemaTypes[typeName]?.keyFieldNames ?? []).includes(fieldName);
    },
    interfaceFieldFilter: (typeName, fieldName, config) => {
      if (computedFieldTypes[typeName]) {
        return true;
      }
      const fieldType = getNamedType(config.type);
      if (computedFieldTypes[fieldType.name]) {
        return true;
      }
      if (!typesForInterface[typeName]) {
        typesForInterface[typeName] = getImplementingTypes(
          typeName,
          subschemaConfig.schema
        );
      }
      if (typesForInterface[typeName].some((t) => computedFieldTypes[t])) {
        return true;
      }
      const isIsolatedFieldName = typesForInterface[typeName].some(
        (implementingTypeName) => isIsolatedField(implementingTypeName, fieldName, isolatedSchemaTypes)
      ) || subschemaConfig.merge?.[typeName]?.fields?.[fieldName] != null;
      const isComputedFieldType = typesForInterface[typeName].some(
        (implementingTypeName) => {
          if (computedFieldTypes[implementingTypeName]) {
            return true;
          }
          const type = subschemaConfig.schema.getType(
            implementingTypeName
          );
          const field = type.getFields()[fieldName];
          if (field == null) {
            return false;
          }
          const fieldType2 = getNamedType(field.type);
          return computedFieldTypes[fieldType2.name] != null;
        }
      );
      return isIsolatedFieldName || isComputedFieldType || typesForInterface[typeName].some(
        (implementingTypeName) => (isolatedSchemaTypes?.[implementingTypeName]?.keyFieldNames ?? []).includes(fieldName)
      ) || (isolatedSchemaTypes[typeName]?.keyFieldNames ?? []).includes(fieldName);
    }
  });
  const merge = Object.fromEntries(
    // get rid of keyFieldNames again
    Object.entries(isolatedSchemaTypes).map(
      ([typeName, { keyFieldNames, ...config }]) => [typeName, config]
    )
  );
  const filteredSubschema = {
    ...subschemaConfig,
    merge,
    transforms: (subschemaConfig.transforms ?? []).concat([
      _createCompositeFieldFilter(filteredSchema)
    ])
  };
  return filteredSubschema;
}

function splitMergedTypeEntryPointsTransformer(subschemaConfig) {
  if (!subschemaConfig.merge) return [subschemaConfig];
  const maxEntryPoints = Object.values(subschemaConfig.merge).reduce(
    (max, mergedTypeConfig) => {
      return Math.max(max, mergedTypeConfig?.entryPoints?.length ?? 0);
    },
    0
  );
  if (maxEntryPoints === 0) return [subschemaConfig];
  const subschemaPermutations = [];
  for (let i = 0; i < maxEntryPoints; i += 1) {
    const subschemaPermutation = cloneSubschemaConfig(subschemaConfig);
    const mergedTypesCopy = subschemaPermutation.merge ?? /* @__PURE__ */ Object.create(null);
    let currentMerge = mergedTypesCopy;
    if (i > 0) {
      subschemaPermutation.merge = currentMerge = /* @__PURE__ */ Object.create(null);
    }
    for (const typeName in mergedTypesCopy) {
      const mergedTypeConfig = mergedTypesCopy[typeName];
      const mergedTypeEntryPoint = mergedTypeConfig?.entryPoints?.[i];
      if (mergedTypeEntryPoint) {
        if (mergedTypeConfig.selectionSet ?? mergedTypeConfig.fieldName ?? mergedTypeConfig.resolve) {
          throw new Error(
            `Merged type ${typeName} may not define entryPoints in addition to selectionSet, fieldName, or resolve`
          );
        }
        Object.assign(mergedTypeConfig, mergedTypeEntryPoint);
        delete mergedTypeConfig.entryPoints;
        if (i > 0) {
          delete mergedTypeConfig.canonical;
          if (mergedTypeConfig.fields != null) {
            for (const mergedFieldName in mergedTypeConfig.fields) {
              const mergedFieldConfig = mergedTypeConfig.fields[mergedFieldName];
              delete mergedFieldConfig?.canonical;
            }
          }
        }
        currentMerge[typeName] = mergedTypeConfig;
      }
    }
    subschemaPermutations.push(subschemaPermutation);
  }
  return subschemaPermutations;
}

function extractDefinitions(ast) {
  const typeDefinitions = [];
  const directiveDefs = [];
  const schemaDefs = [];
  const schemaExtensions = [];
  const extensionDefs = [];
  for (const def of ast.definitions) {
    switch (def.kind) {
      case Kind.OBJECT_TYPE_DEFINITION:
      case Kind.INTERFACE_TYPE_DEFINITION:
      case Kind.INPUT_OBJECT_TYPE_DEFINITION:
      case Kind.UNION_TYPE_DEFINITION:
      case Kind.ENUM_TYPE_DEFINITION:
      case Kind.SCALAR_TYPE_DEFINITION:
        typeDefinitions.push(def);
        break;
      case Kind.DIRECTIVE_DEFINITION:
        directiveDefs.push(def);
        break;
      case Kind.SCHEMA_DEFINITION:
        schemaDefs.push(def);
        break;
      case Kind.SCHEMA_EXTENSION:
        schemaExtensions.push(def);
        break;
      case Kind.OBJECT_TYPE_EXTENSION:
      case Kind.INTERFACE_TYPE_EXTENSION:
      case Kind.INPUT_OBJECT_TYPE_EXTENSION:
      case Kind.UNION_TYPE_EXTENSION:
      case Kind.ENUM_TYPE_EXTENSION:
      case Kind.SCALAR_TYPE_EXTENSION:
        extensionDefs.push(def);
        break;
    }
  }
  return {
    typeDefinitions,
    directiveDefs,
    schemaDefs,
    schemaExtensions,
    extensionDefs
  };
}

var ValidationLevel = /* @__PURE__ */ ((ValidationLevel2) => {
  ValidationLevel2["Error"] = "error";
  ValidationLevel2["Warn"] = "warn";
  ValidationLevel2["Off"] = "off";
  return ValidationLevel2;
})(ValidationLevel || {});

function validateFieldConsistency(finalFieldConfig, candidates, typeMergingOptions) {
  const firstCandidate = candidates[0];
  if (!firstCandidate) {
    throw new Error("First candidate is null");
  }
  const fieldNamespace = `${firstCandidate.type.name}.${firstCandidate.fieldName}`;
  const finalFieldNull = isNonNullType(finalFieldConfig.type);
  validateTypeConsistency(
    finalFieldConfig,
    candidates.map((c) => c.fieldConfig),
    "field",
    fieldNamespace,
    typeMergingOptions
  );
  if (getValidationSettings(fieldNamespace, typeMergingOptions).strictNullComparison && candidates.some((c) => finalFieldNull !== isNonNullType(c.fieldConfig.type))) {
    validationMessage(
      `Nullability of field "${fieldNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
      fieldNamespace,
      typeMergingOptions
    );
  } else if (finalFieldNull && candidates.some((c) => !isNonNullType(c.fieldConfig.type))) {
    validationMessage(
      `Canonical definition of field "${fieldNamespace}" is not-null while some subschemas permit null. This will be an automatic error in future versions.`,
      fieldNamespace,
      typeMergingOptions
    );
  }
  const argCandidatesMap = /* @__PURE__ */ Object.create(null);
  for (const { fieldConfig } of candidates) {
    if (fieldConfig.args == null) {
      continue;
    }
    for (const argName in fieldConfig.args) {
      const arg = fieldConfig.args[argName];
      if (arg) {
        argCandidatesMap[argName] ||= [];
        argCandidatesMap[argName].push(arg);
      }
    }
  }
  if (Object.values(argCandidatesMap).some(
    (argCandidates) => candidates.length !== argCandidates.length
  )) {
    validationMessage(
      `Canonical definition of field "${fieldNamespace}" implements inconsistent argument names across subschemas. Input may be filtered from some requests.`,
      fieldNamespace,
      typeMergingOptions
    );
  }
  for (const argName in argCandidatesMap) {
    if (finalFieldConfig.args == null) {
      continue;
    }
    const argCandidates = argCandidatesMap[argName];
    if (!argCandidates) {
      throw new Error("argCandidates is null");
    }
    const argNamespace = `${fieldNamespace}.${argName}`;
    const finalArgConfig = finalFieldConfig.args[argName] || argCandidates[argCandidates.length - 1];
    if (!finalArgConfig) {
      throw new Error("finalArgConfig is null");
    }
    const finalArgType = getNamedType(finalArgConfig.type);
    const finalArgNull = isNonNullType(finalArgConfig.type);
    validateTypeConsistency(
      finalArgConfig,
      argCandidates,
      "argument",
      argNamespace,
      typeMergingOptions
    );
    if (getValidationSettings(argNamespace, typeMergingOptions).strictNullComparison && argCandidates.some((c) => finalArgNull !== isNonNullType(c.type))) {
      validationMessage(
        `Nullability of argument "${argNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
        argNamespace,
        typeMergingOptions
      );
    } else if (!finalArgNull && argCandidates.some((c) => isNonNullType(c.type))) {
      validationMessage(
        `Canonical definition of argument "${argNamespace}" permits null while some subschemas require not-null. This will be an automatic error in future versions.`,
        argNamespace,
        typeMergingOptions
      );
    }
    if (isEnumType(finalArgType)) {
      validateInputEnumConsistency(
        finalArgType,
        argCandidates,
        typeMergingOptions
      );
    }
  }
}
function validateInputObjectConsistency(fieldInclusionMap, candidates, typeMergingOptions) {
  for (const fieldName in fieldInclusionMap) {
    const count = fieldInclusionMap[fieldName];
    if (candidates.length !== count && candidates[0]) {
      const namespace = `${candidates[0].type.name}.${fieldName}`;
      validationMessage(
        `Definition of input field "${namespace}" is not implemented by all subschemas. Input may be filtered from some requests.`,
        namespace,
        typeMergingOptions
      );
    }
  }
}
function validateInputFieldConsistency(finalInputFieldConfig, candidates, typeMergingOptions) {
  const inputFieldNamespace = `${candidates[0]?.type.name}.${candidates[0]?.fieldName}`;
  const inputFieldConfigs = candidates.map((c) => c.inputFieldConfig);
  const finalInputFieldType = getNamedType(finalInputFieldConfig.type);
  const finalInputFieldNull = isNonNullType(finalInputFieldConfig.type);
  validateTypeConsistency(
    finalInputFieldConfig,
    inputFieldConfigs,
    "input field",
    inputFieldNamespace,
    typeMergingOptions
  );
  if (getValidationSettings(inputFieldNamespace, typeMergingOptions).strictNullComparison && candidates.some(
    (c) => finalInputFieldNull !== isNonNullType(c.inputFieldConfig.type)
  )) {
    validationMessage(
      `Nullability of input field "${inputFieldNamespace}" does not match across subschemas. Disable typeMergingOptions.validationSettings.strictNullComparison to permit safe divergences.`,
      inputFieldNamespace,
      typeMergingOptions
    );
  } else if (!finalInputFieldNull && candidates.some((c) => isNonNullType(c.inputFieldConfig.type))) {
    validationMessage(
      `Canonical definition of input field "${inputFieldNamespace}" permits null while some subschemas require not-null. This will be an automatic error in future versions.`,
      inputFieldNamespace,
      typeMergingOptions
    );
  }
  if (isEnumType(finalInputFieldType)) {
    validateInputEnumConsistency(
      finalInputFieldType,
      inputFieldConfigs,
      typeMergingOptions
    );
  }
}
function validateTypeConsistency(finalElementConfig, candidates, definitionType, settingNamespace, typeMergingOptions) {
  const finalNamedType = getNamedType(finalElementConfig.type);
  const finalIsScalar = isScalarType(finalNamedType);
  const finalIsList = hasListType(finalElementConfig.type);
  for (const c of candidates) {
    if (finalIsList !== hasListType(c.type)) {
      throw new Error(
        `Definitions of ${definitionType} "${settingNamespace}" implement inconsistent list types across subschemas and cannot be merged.`
      );
    }
    const currentNamedType = getNamedType(c.type);
    if (finalNamedType.toString() !== currentNamedType.toString()) {
      const proxiableScalar = !!typeMergingOptions?.validationSettings?.proxiableScalars?.[finalNamedType.toString()]?.includes(currentNamedType.toString());
      const bothScalars = finalIsScalar && isScalarType(currentNamedType);
      const permitScalar = proxiableScalar && bothScalars;
      if (proxiableScalar && !bothScalars) {
        throw new Error(
          `Types ${finalNamedType} and ${currentNamedType} are not proxiable scalars.`
        );
      }
      if (!permitScalar) {
        validationMessage(
          `Definitions of ${definitionType} "${settingNamespace}" implement inconsistent named types across subschemas. This will be an automatic error in future versions.`,
          settingNamespace,
          typeMergingOptions
        );
      }
    }
  }
}
function hasListType(type) {
  return isListType(getNullableType(type));
}
function validateInputEnumConsistency(inputEnumType, candidates, typeMergingOptions) {
  const enumValueInclusionMap = /* @__PURE__ */ Object.create(null);
  for (const candidate of candidates) {
    const enumType = getNamedType(candidate.type);
    if (isEnumType(enumType)) {
      for (const { value } of enumType.getValues()) {
        enumValueInclusionMap[value] = enumValueInclusionMap[value] || 0;
        enumValueInclusionMap[value] += 1;
      }
    }
  }
  if (Object.values(enumValueInclusionMap).some(
    (count) => candidates.length !== count
  )) {
    validationMessage(
      `Enum "${inputEnumType.name}" is used as an input with inconsistent values across subschemas. This will be an automatic error in future versions.`,
      inputEnumType.name,
      typeMergingOptions
    );
  }
}
function validationMessage(message, settingNamespace, typeMergingOptions) {
  const override = `typeMergingOptions.validationScopes['${settingNamespace}'].validationLevel`;
  const settings = getValidationSettings(settingNamespace, typeMergingOptions);
  switch (settings.validationLevel ?? ValidationLevel.Warn) {
    case ValidationLevel.Off:
      return;
    case ValidationLevel.Error:
      throw new Error(
        `${message} If this is intentional, you may disable this error by setting ${override} = "warn|off"`
      );
    default:
      console.warn(
        `${message} To disable this warning or elevate it to an error, set ${override} = "error|off"`
      );
  }
}
function getValidationSettings(settingNamespace, typeMergingOptions) {
  return {
    ...typeMergingOptions?.validationSettings ?? {},
    ...typeMergingOptions?.validationScopes?.[settingNamespace] ?? {}
  };
}

function mergeCandidates(typeName, candidates, typeMergingOptions) {
  const initialCandidateType = candidates[0]?.type;
  if (candidates.some(
    (candidate) => candidate.type.constructor !== initialCandidateType?.constructor
  )) {
    throw new Error(
      `Cannot merge different type categories into common type ${typeName}.`
    );
  }
  if (isObjectType(initialCandidateType)) {
    return mergeObjectTypeCandidates(typeName, candidates, typeMergingOptions);
  } else if (isInputObjectType(initialCandidateType)) {
    return mergeInputObjectTypeCandidates(
      typeName,
      candidates,
      typeMergingOptions
    );
  } else if (isInterfaceType(initialCandidateType)) {
    return mergeInterfaceTypeCandidates(
      typeName,
      candidates,
      typeMergingOptions
    );
  } else if (isUnionType(initialCandidateType)) {
    return mergeUnionTypeCandidates(typeName, candidates, typeMergingOptions);
  } else if (isEnumType(initialCandidateType)) {
    return mergeEnumTypeCandidates(typeName, candidates, typeMergingOptions);
  } else if (isScalarType(initialCandidateType)) {
    return mergeScalarTypeCandidates(typeName, candidates, typeMergingOptions);
  } else {
    throw new Error(`Type ${typeName} has unknown GraphQL type.`);
  }
}
function mergeObjectTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const fields = fieldConfigMapFromTypeCandidates(
    candidates,
    typeMergingOptions
  );
  const typeConfigs = candidates.map(
    (candidate) => candidate.type.toConfig()
  );
  const interfaceMap = typeConfigs.map((typeConfig2) => typeConfig2.interfaces).reduce((acc, interfaces2) => {
    if (interfaces2 != null) {
      for (const iface of interfaces2) {
        acc[iface.name] = iface;
      }
    }
    return acc;
  }, /* @__PURE__ */ Object.create(null));
  const interfaces = Object.values(interfaceMap);
  const astNodes = pluck("astNode", candidates);
  const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
  if (astNodes.length > 1 && fieldAstNodes.length) {
    astNodes.push({
      ...astNodes[astNodes.length - 1],
      fields: JSON.parse(JSON.stringify(fieldAstNodes))
    });
  }
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeType(astNode2, acc, {
      ignoreFieldConflicts: true
    }),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  const typeConfig = {
    name: typeName,
    description,
    fields,
    interfaces,
    astNode,
    extensionASTNodes,
    extensions
  };
  return new GraphQLObjectType(typeConfig);
}
function mergeInputObjectTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const fields = inputFieldConfigMapFromTypeCandidates(
    candidates,
    typeMergingOptions
  );
  const astNodes = pluck("astNode", candidates);
  const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
  if (astNodes.length > 1 && fieldAstNodes.length) {
    astNodes.push({
      ...astNodes[astNodes.length - 1],
      fields: JSON.parse(JSON.stringify(fieldAstNodes))
    });
  }
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeInputType(astNode2, acc, {
      ignoreFieldConflicts: true
    }),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  const typeConfig = {
    name: typeName,
    description,
    fields,
    astNode,
    extensionASTNodes,
    extensions
  };
  return new GraphQLInputObjectType(typeConfig);
}
function pluck(typeProperty, candidates) {
  return candidates.map((candidate) => candidate.type[typeProperty]).filter((value) => value != null);
}
function mergeInterfaceTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const fields = fieldConfigMapFromTypeCandidates(
    candidates,
    typeMergingOptions
  );
  const typeConfigs = candidates.map((candidate) => candidate.type.toConfig());
  const interfaceMap = typeConfigs.map(
    (typeConfig2) => "interfaces" in typeConfig2 ? typeConfig2.interfaces : []
  ).reduce((acc, interfaces2) => {
    if (interfaces2 != null) {
      for (const iface of interfaces2) {
        acc[iface.name] = iface;
      }
    }
    return acc;
  }, /* @__PURE__ */ Object.create(null));
  const interfaces = Object.values(interfaceMap);
  const astNodes = pluck("astNode", candidates);
  const fieldAstNodes = canonicalFieldNamesForType(candidates).map((fieldName) => fields[fieldName]?.astNode).filter((n) => n != null);
  if (astNodes.length > 1 && fieldAstNodes.length) {
    astNodes.push({
      ...astNodes[astNodes.length - 1],
      fields: JSON.parse(JSON.stringify(fieldAstNodes))
    });
  }
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeInterface(astNode2, acc, {
      ignoreFieldConflicts: true
    }),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  const typeConfig = {
    name: typeName,
    description,
    fields,
    interfaces,
    astNode,
    extensionASTNodes,
    extensions
  };
  return new GraphQLInterfaceType(typeConfig);
}
function mergeUnionTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const typeConfigs = candidates.map((candidate) => {
    if (!isUnionType(candidate.type)) {
      throw new Error(`Expected ${candidate.type} to be a union type!`);
    }
    return candidate.type.toConfig();
  });
  const typeMap = typeConfigs.reduce(
    (acc, typeConfig2) => {
      for (const type of typeConfig2.types) {
        acc[type.name] = type;
      }
      return acc;
    },
    /* @__PURE__ */ Object.create(null)
  );
  const types = Object.values(typeMap);
  const astNodes = pluck("astNode", candidates);
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeUnion(
      astNode2,
      acc
    ),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  const typeConfig = {
    name: typeName,
    description,
    types,
    astNode,
    extensionASTNodes,
    extensions
  };
  return new GraphQLUnionType(typeConfig);
}
function mergeEnumTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const values = enumValueConfigMapFromTypeCandidates(
    candidates,
    typeMergingOptions
  );
  const astNodes = pluck("astNode", candidates);
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeEnum(astNode2, acc, {
      consistentEnumMerge: true
    }),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  const typeConfig = {
    name: typeName,
    description,
    values,
    astNode,
    extensionASTNodes,
    extensions
  };
  return new GraphQLEnumType(typeConfig);
}
function enumValueConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
  const enumValueConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
  for (const candidate of candidates) {
    const valueMap = candidate.type.toConfig().values;
    for (const enumValue in valueMap) {
      const enumValueConfig = valueMap[enumValue];
      if (enumValueConfig) {
        const enumValueConfigCandidate = {
          enumValueConfig,
          enumValue,
          type: candidate.type,
          subschema: candidate.subschema,
          transformedSubschema: candidate.transformedSubschema
        };
        if (enumValueConfigCandidatesMap[enumValue]) {
          enumValueConfigCandidatesMap[enumValue].push(
            enumValueConfigCandidate
          );
        } else {
          enumValueConfigCandidatesMap[enumValue] = [enumValueConfigCandidate];
        }
      }
    }
  }
  const enumValueConfigMap = /* @__PURE__ */ Object.create(null);
  for (const enumValue in enumValueConfigCandidatesMap) {
    const enumValueConfigCandidates = enumValueConfigCandidatesMap[enumValue];
    if (enumValueConfigCandidates) {
      const enumValueConfigMerger = typeMergingOptions?.enumValueConfigMerger ?? defaultEnumValueConfigMerger;
      enumValueConfigMap[enumValue] = enumValueConfigMerger(
        enumValueConfigCandidates
      );
    }
  }
  return enumValueConfigMap;
}
function defaultEnumValueConfigMerger(candidates) {
  const preferred = candidates.find(
    ({ type, transformedSubschema }) => isSubschemaConfig(transformedSubschema) && transformedSubschema.merge?.[type.name]?.canonical
  );
  const lastCanonical = candidates[candidates.length - 1];
  if (!lastCanonical) {
    throw new Error("Last canonical is required");
  }
  return (preferred || lastCanonical).enumValueConfig;
}
function mergeScalarTypeCandidates(typeName, candidates, typeMergingOptions) {
  candidates = orderedTypeCandidates(candidates, typeMergingOptions);
  const description = mergeTypeDescriptions(candidates, typeMergingOptions);
  const serializeFns = pluck(
    "serialize",
    candidates
  );
  const serialize = serializeFns[serializeFns.length - 1];
  const parseValueFns = pluck(
    "parseValue",
    candidates
  );
  const parseValue = parseValueFns[parseValueFns.length - 1];
  const parseLiteralFns = pluck(
    "parseLiteral",
    candidates
  );
  const parseLiteral = parseLiteralFns[parseLiteralFns.length - 1];
  const astNodes = pluck("astNode", candidates);
  const astNode = astNodes.slice(1).reduce(
    (acc, astNode2) => mergeScalar(
      astNode2,
      acc
    ),
    astNodes[0]
  );
  const extensionASTNodes = pluck(
    "extensionASTNodes",
    candidates
  );
  const extensions = Object.assign(
    {},
    ...pluck("extensions", candidates)
  );
  let specifiedByURL;
  for (const candidate of candidates) {
    if ("specifiedByURL" in candidate.type && candidate.type.specifiedByURL) {
      specifiedByURL = candidate.type.specifiedByURL;
      break;
    }
  }
  const typeConfig = {
    name: typeName,
    description,
    serialize,
    parseValue,
    parseLiteral,
    astNode,
    extensionASTNodes,
    extensions,
    specifiedByURL
  };
  return new GraphQLScalarType(typeConfig);
}
function orderedTypeCandidates(candidates, typeMergingOptions) {
  const typeCandidateMerger = typeMergingOptions?.typeCandidateMerger ?? defaultTypeCandidateMerger;
  const candidate = typeCandidateMerger(candidates);
  return candidates.filter((c) => c !== candidate).concat([candidate]);
}
function defaultTypeCandidateMerger(candidates) {
  const canonical = candidates.filter(
    ({ type, transformedSubschema }) => isSubschemaConfig(transformedSubschema) ? transformedSubschema.merge?.[type.name]?.canonical : false
  );
  if (canonical.length > 1) {
    if (!canonical[0]) {
      throw new Error(`First canonical is required`);
    }
    throw new Error(
      `Multiple canonical definitions for "${canonical[0].type.name}"`
    );
  } else if (canonical.length) {
    if (!canonical[0]) {
      throw new Error(`First canonical is required`);
    }
    return canonical[0];
  }
  const lastCanonical = candidates[candidates.length - 1];
  if (!lastCanonical) {
    throw new Error(`Last canonical is required`);
  }
  return lastCanonical;
}
function mergeTypeDescriptions(candidates, typeMergingOptions) {
  const typeDescriptionsMerger = typeMergingOptions?.typeDescriptionsMerger ?? defaultTypeDescriptionMerger;
  return typeDescriptionsMerger(candidates);
}
function defaultTypeDescriptionMerger(candidates) {
  const lastCandidate = candidates[candidates.length - 1];
  return lastCandidate?.type.description;
}
function fieldConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
  const fieldConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
  for (const candidate of candidates) {
    const typeConfig = candidate.type.toConfig();
    const fieldConfigMap2 = typeConfig.fields;
    for (const fieldName in fieldConfigMap2) {
      const fieldConfig = fieldConfigMap2[fieldName];
      if (fieldConfig) {
        const fieldConfigCandidate = {
          fieldConfig,
          fieldName,
          type: candidate.type,
          subschema: candidate.subschema,
          transformedSubschema: candidate.transformedSubschema
        };
        if (fieldConfigCandidatesMap[fieldName]) {
          fieldConfigCandidatesMap[fieldName].push(fieldConfigCandidate);
        } else {
          fieldConfigCandidatesMap[fieldName] = [fieldConfigCandidate];
        }
      }
    }
  }
  const fieldConfigMap = /* @__PURE__ */ Object.create(null);
  for (const fieldName in fieldConfigCandidatesMap) {
    const fieldConfigCandidates = fieldConfigCandidatesMap[fieldName];
    if (fieldConfigCandidates) {
      fieldConfigMap[fieldName] = mergeFieldConfigs(
        fieldConfigCandidates,
        typeMergingOptions
      );
    }
  }
  return fieldConfigMap;
}
function mergeFieldConfigs(candidates, typeMergingOptions) {
  const fieldConfigMerger = typeMergingOptions?.fieldConfigMerger ?? getDefaultFieldConfigMerger(
    typeMergingOptions?.useNonNullableFieldOnConflict
  );
  const finalFieldConfig = fieldConfigMerger(candidates);
  validateFieldConsistency(finalFieldConfig, candidates, typeMergingOptions);
  return finalFieldConfig;
}
function getDefaultFieldConfigMerger(useNonNullableFieldOnConflict = false) {
  return function defaultFieldConfigMerger(candidates) {
    const nullables = [];
    const nonNullables = [];
    const canonicalByField = [];
    const canonicalByType = [];
    for (const {
      type,
      fieldName,
      fieldConfig,
      transformedSubschema
    } of candidates) {
      if (!isSubschemaConfig(transformedSubschema)) continue;
      if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) {
        canonicalByField.push(fieldConfig);
      } else if (transformedSubschema.merge?.[type.name]?.canonical) {
        canonicalByType.push(fieldConfig);
      }
      if (isNullableType(fieldConfig.type)) {
        nullables.push(fieldConfig);
      } else {
        nonNullables.push(fieldConfig);
      }
    }
    const nonNullableFinalField = nonNullables.length > 0 && nullables.length > 0 && useNonNullableFieldOnConflict;
    if (canonicalByField.length > 1 && candidates[0]) {
      throw new Error(
        `Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`
      );
    } else if (canonicalByField.length) {
      const finalField2 = canonicalByField[0];
      if (!finalField2) {
        throw new Error("Final field is required");
      }
      if (nonNullableFinalField) {
        return {
          ...finalField2,
          type: getNullableType(finalField2.type)
        };
      }
      return finalField2;
    } else if (canonicalByType.length) {
      const finalField2 = canonicalByType[0];
      if (!finalField2) {
        throw new Error("Final field is required");
      }
      if (nonNullableFinalField) {
        return {
          ...finalField2,
          type: getNullableType(finalField2.type)
        };
      }
      return finalField2;
    }
    const finalField = candidates[candidates.length - 1]?.fieldConfig;
    if (!finalField) {
      throw new Error("Field config is required");
    }
    if (nonNullableFinalField) {
      return {
        ...finalField,
        type: getNullableType(finalField.type)
      };
    }
    return finalField;
  };
}
function inputFieldConfigMapFromTypeCandidates(candidates, typeMergingOptions) {
  const inputFieldConfigCandidatesMap = /* @__PURE__ */ Object.create(null);
  const fieldInclusionMap = /* @__PURE__ */ Object.create(null);
  for (const candidate of candidates) {
    const typeConfig = candidate.type.toConfig();
    const inputFieldConfigMap2 = typeConfig.fields;
    for (const fieldName in inputFieldConfigMap2) {
      const inputFieldConfig = inputFieldConfigMap2[fieldName];
      if (inputFieldConfig == null) {
        throw new Error(`'inputFieldConfig' is required`);
      }
      fieldInclusionMap[fieldName] = fieldInclusionMap[fieldName] || 0;
      fieldInclusionMap[fieldName] += 1;
      const inputFieldConfigCandidate = {
        inputFieldConfig,
        fieldName,
        type: candidate.type,
        subschema: candidate.subschema,
        transformedSubschema: candidate.transformedSubschema
      };
      if (inputFieldConfigCandidatesMap[fieldName]) {
        inputFieldConfigCandidatesMap[fieldName].push(
          inputFieldConfigCandidate
        );
      } else {
        inputFieldConfigCandidatesMap[fieldName] = [inputFieldConfigCandidate];
      }
    }
  }
  validateInputObjectConsistency(
    fieldInclusionMap,
    candidates,
    typeMergingOptions
  );
  const inputFieldConfigMap = /* @__PURE__ */ Object.create(null);
  for (const fieldName in inputFieldConfigCandidatesMap) {
    const inputFieldConfigMerger = typeMergingOptions?.inputFieldConfigMerger ?? defaultInputFieldConfigMerger;
    const inputFieldConfigCandidate = inputFieldConfigCandidatesMap[fieldName];
    if (!inputFieldConfigCandidate) {
      throw new Error("Input field config candidate is required");
    }
    inputFieldConfigMap[fieldName] = inputFieldConfigMerger(
      inputFieldConfigCandidate
    );
    validateInputFieldConsistency(
      inputFieldConfigMap[fieldName],
      inputFieldConfigCandidate,
      typeMergingOptions
    );
  }
  return inputFieldConfigMap;
}
function defaultInputFieldConfigMerger(candidates) {
  const canonicalByField = [];
  const canonicalByType = [];
  for (const {
    type,
    fieldName,
    inputFieldConfig,
    transformedSubschema
  } of candidates) {
    if (!isSubschemaConfig(transformedSubschema)) continue;
    if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) {
      canonicalByField.push(inputFieldConfig);
    } else if (transformedSubschema.merge?.[type.name]?.canonical) {
      canonicalByType.push(inputFieldConfig);
    }
  }
  if (canonicalByField.length > 1 && candidates[0]) {
    throw new Error(
      `Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`
    );
  } else if (canonicalByField.length) {
    return canonicalByField[0];
  } else if (canonicalByType.length) {
    return canonicalByType[0];
  }
  const lastCandidate = candidates[candidates.length - 1];
  if (!lastCandidate) {
    throw new Error("Last candidate is required");
  }
  return lastCandidate.inputFieldConfig;
}
function canonicalFieldNamesForType(candidates) {
  const canonicalFieldNames = /* @__PURE__ */ Object.create(null);
  for (const { type, transformedSubschema } of candidates) {
    if (!isSubschemaConfig(transformedSubschema)) continue;
    const mergeConfig = transformedSubschema.merge?.[type.name];
    if (mergeConfig != null && mergeConfig.fields != null && !mergeConfig.canonical) {
      for (const fieldName in mergeConfig.fields) {
        const mergedFieldConfig = mergeConfig.fields[fieldName];
        if (mergedFieldConfig?.canonical) {
          canonicalFieldNames[fieldName] = true;
        }
      }
    }
  }
  return Object.keys(canonicalFieldNames);
}

function mergeDirectives(directives) {
  if (directives.size === 0) {
    return void 0;
  }
  if (directives.size === 1) {
    const directive = directives.values().next().value;
    return directive;
  }
  let name;
  let description;
  const locations = /* @__PURE__ */ new Set();
  const args = {};
  const extensionsSet = /* @__PURE__ */ new Set();
  let isRepeatable = false;
  for (const directive of directives) {
    name = directive.name;
    if (directive.description) {
      description = directive.description;
    }
    for (const location of directive.locations) {
      locations.add(location);
    }
    for (const arg of directive.args) {
      args[arg.name] = arg;
    }
    isRepeatable = isRepeatable || directive.isRepeatable;
    if (directive.extensions) {
      extensionsSet.add(directive.extensions);
    }
  }
  return new GraphQLDirective({
    name,
    description,
    locations: Array.from(locations),
    args,
    isRepeatable,
    extensions: extensionsSet.size > 0 ? mergeDeep([...extensionsSet]) : void 0
  });
}

const backcompatOptions = { commentDescriptions: true };
function typeFromAST(node) {
  switch (node.kind) {
    case Kind.OBJECT_TYPE_DEFINITION:
      return makeObjectType(node);
    case Kind.INTERFACE_TYPE_DEFINITION:
      return makeInterfaceType(node);
    case Kind.ENUM_TYPE_DEFINITION:
      return makeEnumType(node);
    case Kind.UNION_TYPE_DEFINITION:
      return makeUnionType(node);
    case Kind.SCALAR_TYPE_DEFINITION:
      return makeScalarType(node);
    case Kind.INPUT_OBJECT_TYPE_DEFINITION:
      return makeInputObjectType(node);
    case Kind.DIRECTIVE_DEFINITION:
      return makeDirective(node);
    default:
      return null;
  }
}
function makeObjectType(node) {
  const config = {
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    interfaces: () => node.interfaces?.map(
      (iface) => createNamedStub(iface.name.value, "interface")
    ) || [],
    fields: () => node.fields != null ? makeFields(node.fields) : {},
    astNode: node
  };
  return new GraphQLObjectType(config);
}
function makeInterfaceType(node) {
  const config = {
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    interfaces: () => node.interfaces?.map(
      (iface) => createNamedStub(iface.name.value, "interface")
    ),
    fields: () => node.fields != null ? makeFields(node.fields) : {},
    astNode: node
  };
  return new GraphQLInterfaceType(config);
}
function makeEnumType(node) {
  const values = node.values?.reduce(
    (prev, value) => ({
      ...prev,
      [value.name.value]: {
        description: getDescription(value, backcompatOptions),
        deprecationReason: getDeprecationReason(value),
        astNode: value
      }
    }),
    {}
  ) ?? {};
  return new GraphQLEnumType({
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    values,
    astNode: node
  });
}
function makeUnionType(node) {
  return new GraphQLUnionType({
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    types: () => node.types?.map((type) => createNamedStub(type.name.value, "object")) ?? [],
    astNode: node
  });
}
function makeScalarType(node) {
  return new GraphQLScalarType({
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    astNode: node,
    // TODO: serialize default property setting can be dropped once
    // upstream graphql-js TypeScript typings are updated, likely in v16
    serialize: (value) => value
  });
}
function makeInputObjectType(node) {
  return new GraphQLInputObjectType({
    name: node.name.value,
    description: getDescription(node, backcompatOptions),
    fields: () => node.fields ? makeValues(node.fields) : {},
    astNode: node
  });
}
function makeFields(nodes) {
  return nodes.reduce(
    (prev, node) => ({
      ...prev,
      [node.name.value]: {
        type: createStub(node.type, "output"),
        description: getDescription(node, backcompatOptions),
        args: makeValues(node.arguments ?? []),
        deprecationReason: getDeprecationReason(node),
        astNode: node
      }
    }),
    {}
  );
}
function makeValues(nodes) {
  return nodes.reduce(
    (prev, node) => ({
      ...prev,
      [node.name.value]: {
        type: createStub(node.type, "input"),
        defaultValue: node.defaultValue !== void 0 ? valueFromASTUntyped(node.defaultValue) : void 0,
        description: getDescription(node, backcompatOptions),
        astNode: node
      }
    }),
    {}
  );
}
function isLocationValue(value) {
  return value in DirectiveLocation;
}
function makeDirective(node) {
  const locations = [];
  for (const location of node.locations) {
    const locationValue = location.value;
    if (isLocationValue(locationValue)) {
      locations.push(locationValue);
    }
  }
  return new GraphQLDirective({
    name: node.name.value,
    description: node.description != null ? node.description.value : null,
    locations,
    isRepeatable: node.repeatable,
    args: makeValues(node.arguments ?? []),
    astNode: node
  });
}
function getDeprecationReason(node) {
  const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
  return deprecated?.["reason"];
}

function buildTypeCandidates({
  subschemas,
  originalSubschemaMap,
  types,
  typeDefs,
  parseOptions,
  directiveMap,
  schemaDefs,
  mergeDirectives: isMergeDirectives
}) {
  const directiveCandidates = new Map(
    Object.entries(directiveMap).map(([name, directive]) => [
      name,
      /* @__PURE__ */ new Set([directive])
    ])
  );
  const extensions = [];
  const typeCandidates = /* @__PURE__ */ Object.create(null);
  let schemaDef;
  let schemaExtensions = [];
  let document;
  let extraction;
  if (typeDefs && !Array.isArray(typeDefs) || Array.isArray(typeDefs) && typeDefs.length) {
    document = mergeTypeDefs(typeDefs, parseOptions);
    extraction = extractDefinitions(document);
    schemaDef = extraction.schemaDefs[0];
    schemaExtensions = schemaExtensions.concat(extraction.schemaExtensions);
  }
  schemaDefs.schemaDef = schemaDef ?? schemaDefs.schemaDef;
  schemaDefs.schemaExtensions = schemaExtensions;
  const rootTypeNameMap = getRootTypeNameMap(schemaDefs);
  for (const subschema of subschemas) {
    const schema = subschema.transformedSchema = wrapSchema(subschema);
    const rootTypeMap = getRootTypeMap(schema);
    const rootTypes = getRootTypes(schema);
    for (const [operation, rootType] of rootTypeMap.entries()) {
      addTypeCandidate(typeCandidates, rootTypeNameMap[operation], {
        type: rootType,
        subschema: originalSubschemaMap.get(subschema),
        transformedSubschema: subschema
      });
    }
    if (isMergeDirectives === true) {
      for (const directive of schema.getDirectives()) {
        let directiveCandidatesForName = directiveCandidates.get(
          directive.name
        );
        if (directiveCandidatesForName == null) {
          directiveCandidatesForName = /* @__PURE__ */ new Set();
          directiveCandidates.set(directive.name, directiveCandidatesForName);
        }
        directiveCandidatesForName.add(directive);
      }
    }
    const originalTypeMap = schema.getTypeMap();
    for (const typeName in originalTypeMap) {
      const type = originalTypeMap[typeName];
      if (isNamedType(type) && !isIntrospectionType(type) && !rootTypes.has(type)) {
        addTypeCandidate(typeCandidates, type.name, {
          type,
          subschema: originalSubschemaMap.get(subschema),
          transformedSubschema: subschema
        });
      }
    }
  }
  if (document != null && extraction != null) {
    for (const def of extraction.typeDefinitions) {
      const type = typeFromAST(def);
      if (!isNamedType(type)) {
        throw new Error(`Expected to get named typed but got ${inspect(def)}`);
      }
      if (type != null) {
        if (isInterfaceType(type)) {
          try {
            type.getInterfaces();
          } catch {
            Object.defineProperty(type, "_interfaces", {
              value: []
            });
          }
        }
        addTypeCandidate(typeCandidates, type.name, { type });
      }
    }
    for (const def of extraction.directiveDefs) {
      const directive = typeFromAST(def);
      if (!isDirective(directive)) {
        throw new Error(
          `Expected to get directive type but got ${inspect(def)}`
        );
      }
      let directiveCandidatesForName = directiveCandidates.get(directive.name);
      if (directiveCandidatesForName == null) {
        directiveCandidatesForName = /* @__PURE__ */ new Set();
        directiveCandidates.set(directive.name, directiveCandidatesForName);
      }
      directiveCandidatesForName.add(directive);
    }
    if (extraction.extensionDefs.length > 0) {
      extensions.push({
        ...document,
        definitions: extraction.extensionDefs
      });
    }
  }
  for (const type of types) {
    addTypeCandidate(typeCandidates, type.name, { type });
  }
  for (const [
    directiveName,
    directiveCandidatesForName
  ] of directiveCandidates) {
    directiveMap[directiveName] = mergeDirectives(directiveCandidatesForName);
  }
  return [typeCandidates, rootTypeNameMap, extensions];
}
function getRootTypeNameMap({
  schemaDef,
  schemaExtensions
}) {
  const rootTypeNameMap = {
    query: "Query",
    mutation: "Mutation",
    subscription: "Subscription"
  };
  const allNodes = schemaExtensions.slice();
  if (schemaDef != null) {
    allNodes.unshift(schemaDef);
  }
  for (const node of allNodes) {
    if (node.operationTypes != null) {
      for (const operationType of node.operationTypes) {
        rootTypeNameMap[operationType.operation] = operationType.type.name.value;
      }
    }
  }
  return rootTypeNameMap;
}
function addTypeCandidate(typeCandidates, name, typeCandidate) {
  if (!typeCandidates[name]) {
    typeCandidates[name] = [];
  }
  typeCandidates[name].push(typeCandidate);
}
function buildTypes({
  typeCandidates,
  directives,
  stitchingInfo,
  rootTypeNames,
  onTypeConflict,
  mergeTypes,
  typeMergingOptions
}) {
  const typeMap = /* @__PURE__ */ Object.create(null);
  for (const typeName in typeCandidates) {
    if (rootTypeNames.includes(typeName) || mergeTypes === true && !typeCandidates[typeName]?.some(
      (candidate) => isSpecifiedScalarType(candidate.type)
    ) || typeof mergeTypes === "function" && mergeTypes(typeCandidates[typeName], typeName) || Array.isArray(mergeTypes) && mergeTypes.includes(typeName) || stitchingInfo != null && typeName in stitchingInfo.mergedTypes) {
      typeMap[typeName] = mergeCandidates(
        typeName,
        typeCandidates[typeName],
        typeMergingOptions
      );
    } else {
      const candidateSelector = onTypeConflict != null ? onTypeConflictToCandidateSelector(onTypeConflict) : (cands) => cands[cands.length - 1];
      typeMap[typeName] = candidateSelector(typeCandidates[typeName]).type;
    }
  }
  return rewireTypes(typeMap, directives);
}
function onTypeConflictToCandidateSelector(onTypeConflict) {
  return (cands) => cands.reduce((prev, next) => {
    const type = onTypeConflict(prev.type, next.type, {
      left: {
        subschema: prev.subschema,
        transformedSubschema: prev.transformedSubschema
      },
      right: {
        subschema: next.subschema,
        transformedSubschema: next.transformedSubschema
      }
    });
    if (prev.type === type) {
      return prev;
    } else if (next.type === type) {
      return next;
    }
    return {
      schemaName: "unknown",
      type
    };
  });
}

function stitchSchemas({
  subschemas = [],
  types = [],
  typeDefs = [],
  onTypeConflict,
  mergeDirectives,
  mergeTypes = true,
  typeMergingOptions,
  subschemaConfigTransforms = [],
  resolvers = {},
  inheritResolversFromInterfaces = false,
  resolverValidationOptions = {},
  updateResolversInPlace = true,
  schemaExtensions,
  ...rest
}) {
  const transformedSubschemas = [];
  const subschemaMap = /* @__PURE__ */ new Map();
  const originalSubschemaMap = /* @__PURE__ */ new Map();
  for (const subschema of subschemas) {
    for (const transformedSubschemaConfig of applySubschemaConfigTransforms(
      subschemaConfigTransforms,
      subschema,
      subschemaMap,
      originalSubschemaMap
    )) {
      transformedSubschemas.push(transformedSubschemaConfig);
    }
  }
  const directiveMap = /* @__PURE__ */ Object.create(null);
  for (const directive of specifiedDirectives) {
    directiveMap[directive.name] = directive;
  }
  const schemaDefs = /* @__PURE__ */ Object.create(null);
  const [typeCandidates, rootTypeNameMap, extensions] = buildTypeCandidates({
    subschemas: transformedSubschemas,
    originalSubschemaMap,
    types,
    typeDefs: typeDefs || [],
    parseOptions: rest,
    directiveMap,
    schemaDefs,
    mergeDirectives
  });
  let stitchingInfo = createStitchingInfo(
    subschemaMap,
    typeCandidates,
    mergeTypes
  );
  const { typeMap: newTypeMap, directives: newDirectives } = buildTypes({
    typeCandidates,
    directives: Object.values(directiveMap),
    stitchingInfo,
    rootTypeNames: Object.values(rootTypeNameMap),
    onTypeConflict,
    mergeTypes,
    typeMergingOptions
  });
  let schema = new GraphQLSchema({
    query: newTypeMap[rootTypeNameMap.query],
    mutation: newTypeMap[rootTypeNameMap.mutation],
    subscription: newTypeMap[rootTypeNameMap.subscription],
    types: Object.values(newTypeMap),
    directives: newDirectives,
    astNode: schemaDefs.schemaDef,
    extensionASTNodes: schemaDefs.schemaExtensions,
    extensions: null,
    assumeValid: rest.assumeValid
  });
  for (const extension of extensions) {
    schema = extendSchema(schema, extension, {
      commentDescriptions: true
    });
  }
  const resolverMap = mergeResolvers(resolvers);
  const finalResolvers = inheritResolversFromInterfaces ? extendResolversFromInterfaces(schema, resolverMap) : resolverMap;
  stitchingInfo = completeStitchingInfo(stitchingInfo, finalResolvers, schema);
  schema = addResolversToSchema({
    schema,
    defaultFieldResolver: defaultMergedResolver,
    resolvers: finalResolvers,
    resolverValidationOptions,
    inheritResolversFromInterfaces: false,
    updateResolversInPlace
  });
  const resolverValidationOptionsEntries = Object.entries(
    resolverValidationOptions
  );
  if (resolverValidationOptionsEntries.length > 0 && resolverValidationOptionsEntries.some(([, o]) => o !== "ignore")) {
    assertResolversPresent(schema, resolverValidationOptions);
  }
  addStitchingInfo(schema, stitchingInfo);
  if (schemaExtensions) {
    if (Array.isArray(schemaExtensions)) {
      schemaExtensions = mergeExtensions(schemaExtensions);
    }
    applyExtensions(schema, schemaExtensions);
  }
  return schema;
}
const subschemaConfigTransformerPresets = [isolateComputedFieldsTransformer, splitMergedTypeEntryPointsTransformer];
function applySubschemaConfigTransforms(subschemaConfigTransforms, subschemaOrSubschemaConfig, subschemaMap, originalSubschemaMap) {
  let subschemaConfig;
  if (isSubschemaConfig(subschemaOrSubschemaConfig)) {
    subschemaConfig = subschemaOrSubschemaConfig;
  } else if (subschemaOrSubschemaConfig instanceof GraphQLSchema) {
    subschemaConfig = { schema: subschemaOrSubschemaConfig };
  } else {
    throw new TypeError(
      "Received invalid input." + inspect(subschemaOrSubschemaConfig)
    );
  }
  const transformedSubschemaConfigs = subschemaConfigTransforms.concat(subschemaConfigTransformerPresets).reduce(
    (transformedSubschemaConfigs2, subschemaConfigTransform) => transformedSubschemaConfigs2.flatMap(
      (ssConfig) => subschemaConfigTransform(ssConfig)
    ),
    [subschemaConfig]
  );
  const transformedSubschemas = transformedSubschemaConfigs.map(
    (ssConfig) => new Subschema(ssConfig)
  );
  const baseSubschema = transformedSubschemas[0];
  subschemaMap.set(subschemaOrSubschemaConfig, baseSubschema);
  for (const subschema of transformedSubschemas) {
    originalSubschemaMap.set(subschema, subschemaOrSubschemaConfig);
  }
  return transformedSubschemas;
}

const forwardArgsToSelectionSet = (selectionSet, mapping) => {
  const selectionSetDef = parseSelectionSet(selectionSet, { noLocation: true });
  return (field) => {
    const selections = selectionSetDef.selections.map(
      (selectionNode) => {
        if (selectionNode.kind === Kind.FIELD) {
          if (!mapping) {
            return { ...selectionNode, arguments: field.arguments?.slice() };
          } else if (selectionNode.name.value in mapping) {
            const selectionArgs = mapping[selectionNode.name.value];
            return {
              ...selectionNode,
              arguments: field.arguments?.filter(
                (arg) => !!selectionArgs?.includes(arg.name.value)
              )
            };
          }
        }
        return selectionNode;
      }
    );
    return { ...selectionSetDef, selections };
  };
};

const defaultRelayMergeConfig = {
  selectionSet: `{ id }`,
  fieldName: "node",
  args: ({ id }) => ({ id })
};
function handleRelaySubschemas(subschemas, getTypeNameFromId) {
  const typeNames = [];
  for (const subschema of subschemas) {
    const nodeType = subschema.schema.getType("Node");
    if (nodeType) {
      if (!isInterfaceType(nodeType)) {
        throw new Error(`Node type should be an interface!`);
      }
      const implementations = subschema.schema.getPossibleTypes(nodeType);
      for (const implementedType of implementations) {
        typeNames.push(implementedType.name);
        subschema.merge = subschema.merge || {};
        subschema.merge[implementedType.name] = defaultRelayMergeConfig;
      }
    }
  }
  const relaySubschemaConfig = {
    schema: makeExecutableSchema({
      typeDefs: (
        /* GraphQL */
        `
        type Query {
          node(id: ID!): Node
        }
        interface Node {
          id: ID!
        }
        ${typeNames.map(
          (typeName) => `
          type ${typeName} implements Node {
            id: ID!
          }
        `
        ).join("\n")}
      `
      ),
      resolvers: {
        Query: {
          node: (_, { id }) => ({ id })
        },
        Node: {
          __resolveType: ({ id }, _, info) => {
            if (!getTypeNameFromId) {
              const possibleTypeNames = /* @__PURE__ */ new Set();
              for (const fieldNode of info.fieldNodes) {
                if (fieldNode.selectionSet?.selections) {
                  for (const selection of fieldNode.selectionSet?.selections || []) {
                    switch (selection.kind) {
                      case Kind.FRAGMENT_SPREAD: {
                        const fragment = info.fragments[selection.name.value];
                        if (!fragment) {
                          throw new Error(
                            `Cannot find fragment ${selection.name.value}`
                          );
                        }
                        possibleTypeNames.add(
                          fragment.typeCondition.name.value
                        );
                        break;
                      }
                      case Kind.INLINE_FRAGMENT: {
                        const possibleTypeName = selection.typeCondition?.name.value;
                        if (possibleTypeName) {
                          possibleTypeNames.add(possibleTypeName);
                        }
                        break;
                      }
                    }
                  }
                }
              }
              if (possibleTypeNames.size !== 1) {
                console.warn(
                  `You need to define getTypeNameFromId as a parameter to handleRelaySubschemas or add a fragment for "node" operation with specific single type condition!`
                );
              }
              return [...possibleTypeNames][0] || typeNames[0];
            }
            return getTypeNameFromId(id);
          }
        }
      }
    })
  };
  subschemas.push(relaySubschemaConfig);
  return subschemas;
}

function createStitchingExecutor(stitchedSchema) {
  const subschemas = [
    ...(stitchedSchema.extensions?.["stitchingInfo"]).subschemaMap.values()
  ];
  return async function stitchingExecutor(executorRequest) {
    const fragments = getFragmentsFromDocument(executorRequest.document);
    const operation = getOperationASTFromRequest(executorRequest);
    const rootType = getDefinedRootType(stitchedSchema, operation.operation);
    const { fields } = collectFields(
      stitchedSchema,
      fragments,
      executorRequest.variables,
      rootType,
      operation.selectionSet
    );
    const data = {};
    for (const [fieldName, fieldNodes] of fields) {
      const responseKey = fieldNodes[0]?.alias?.value ?? fieldName;
      const subschemaForField = subschemas.find((subschema) => {
        const subschemaSchema = isSubschemaConfig(subschema) ? subschema.schema : subschema;
        const rootType2 = getDefinedRootType(
          subschemaSchema,
          operation.operation
        );
        return rootType2.getFields()[fieldName] != null;
      });
      let result = await delegateToSchema({
        schema: subschemaForField || stitchedSchema,
        rootValue: executorRequest.rootValue,
        context: executorRequest.context,
        info: {
          schema: stitchedSchema,
          fieldName,
          fieldNodes,
          operation,
          fragments,
          parentType: rootType,
          returnType: rootType.getFields()[fieldName]?.type,
          variableValues: executorRequest.variables
        }
      });
      if (Array.isArray(result)) {
        result = await Promise.all(result);
      }
      data[responseKey] = result;
    }
    return { data };
  };
}

export { ValidationLevel, calculateSelectionScore, createMergedTypeResolver, createStitchingExecutor, forwardArgsToSelectionSet, getDefaultFieldConfigMerger, handleRelaySubschemas, isolateComputedFieldsTransformer, splitMergedTypeEntryPointsTransformer, stitchSchemas };