UNPKG

@graphql-mesh/fusion-runtime

Version:

Runtime for GraphQL Mesh Fusion Supergraph

1,699 lines 60.7 kB
import { getInstrumented } from '@envelop/instrumentation';
import { Logger, LegacyLogger } from '@graphql-hive/logger';
import { loggerForRequest } from '@graphql-hive/logger/request';
import { defaultPrintFn, createDefaultExecutor } from '@graphql-mesh/transport-common';
import { resolveAdditionalResolversWithoutImport, getInContextSDK, isDisposable, iterateAsync, dispose } from '@graphql-mesh/utils';
import { getBatchingExecutor } from '@graphql-tools/batch-execute';
import { getDirectiveExtensions, mapSchema, MapperKind, astFromField, memoize1, asArray, getDocumentNodeFromSchema, isDocumentNode, printSchemaWithDirectives, mergeDeep, isAsyncIterable, createGraphQLError } from '@graphql-tools/utils';
import { handleMaybePromise, mapAsyncIterator, isPromise } from '@whatwg-node/promise-helpers';
import { isInterfaceType, Kind, isObjectType, typeFromAST, isOutputType, GraphQLSchema, GraphQLDirective, parseType, visit, GraphQLString, DirectiveLocation, isEnumType, isSchema, buildSchema, buildASTSchema, print } from 'graphql';
import { getStitchedSchemaFromSupergraphSdl } from '@graphql-tools/federation';
import { mergeTypeDefs } from '@graphql-tools/merge';
import { createMergedTypeResolver } from '@graphql-tools/stitch';
import { stitchingDirectives } from '@graphql-tools/stitching-directives';
import { HoistField, PruneSchema, RenameTypes, RenameObjectFields, RenameInputObjectFields, RenameInterfaceFields, TransformEnumValues, RenameObjectFieldArguments } from '@graphql-tools/wrap';
import { CRITICAL_ERROR } from '@graphql-tools/executor';
import { AsyncDisposableStack, DisposableSymbols } from '@whatwg-node/disposablestack';

// Regexps involved with splitting words in various case formats.
const SPLIT_LOWER_UPPER_RE = /([\p{Ll}\d])(\p{Lu})/gu;
const SPLIT_UPPER_UPPER_RE = /(\p{Lu})([\p{Lu}][\p{Ll}])/gu;
// Used to iterate over the initial split result and separate numbers.
const SPLIT_SEPARATE_NUMBER_RE = /(\d)\p{Ll}|(\p{L})\d/u;
// Regexp involved with stripping non-word characters from the result.
const DEFAULT_STRIP_REGEXP = /[^\p{L}\d]+/giu;
// The replacement value for splits.
const SPLIT_REPLACE_VALUE = "$1\0$2";
// The default characters to keep after transforming case.
const DEFAULT_PREFIX_SUFFIX_CHARACTERS = "";
/**
 * Split any cased input strings into an array of words.
 */
function split(value) {
    let result = value.trim();
    result = result
        .replace(SPLIT_LOWER_UPPER_RE, SPLIT_REPLACE_VALUE)
        .replace(SPLIT_UPPER_UPPER_RE, SPLIT_REPLACE_VALUE);
    result = result.replace(DEFAULT_STRIP_REGEXP, "\0");
    let start = 0;
    let end = result.length;
    // Trim the delimiter from around the output string.
    while (result.charAt(start) === "\0")
        start++;
    if (start === end)
        return [];
    while (result.charAt(end - 1) === "\0")
        end--;
    return result.slice(start, end).split(/\0/g);
}
/**
 * Split the input string into an array of words, separating numbers.
 */
function splitSeparateNumbers(value) {
    const words = split(value);
    for (let i = 0; i < words.length; i++) {
        const word = words[i];
        const match = SPLIT_SEPARATE_NUMBER_RE.exec(word);
        if (match) {
            const offset = match.index + (match[1] ?? match[2]).length;
            words.splice(i, 1, word.slice(0, offset), word.slice(offset));
        }
    }
    return words;
}
/**
 * Convert a string to constant case (`FOO_BAR`).
 */
function constantCase(input, options) {
    const [prefix, words, suffix] = splitPrefixSuffix(input, options);
    return (prefix +
        words.map(upperFactory(options?.locale)).join("_") +
        suffix);
}
function upperFactory(locale) {
    return locale === false
        ? (input) => input.toUpperCase()
        : (input) => input.toLocaleUpperCase(locale);
}
function splitPrefixSuffix(input, options = {}) {
    const splitFn = options.split ?? (options.separateNumbers ? splitSeparateNumbers : split);
    const prefixCharacters = options.prefixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
    const suffixCharacters = options.suffixCharacters ?? DEFAULT_PREFIX_SUFFIX_CHARACTERS;
    let prefixIndex = 0;
    let suffixIndex = input.length;
    while (prefixIndex < input.length) {
        const char = input.charAt(prefixIndex);
        if (!prefixCharacters.includes(char))
            break;
        prefixIndex++;
    }
    while (suffixIndex > prefixIndex) {
        const index = suffixIndex - 1;
        const char = input.charAt(index);
        if (!suffixCharacters.includes(char))
            break;
        suffixIndex = index;
    }
    return [
        input.slice(0, prefixIndex),
        splitFn(input.slice(prefixIndex, suffixIndex)),
        input.slice(suffixIndex),
    ];
}

function handleFederationSubschema({
  subschemaConfig,
  unifiedGraphDirectives,
  realSubgraphNameMap,
  additionalTypeDefs,
  stitchingDirectivesTransformer,
  onSubgraphExecute
}) {
  const subgraphName = (subschemaConfig.name = realSubgraphNameMap?.get(subschemaConfig.name || "") || subschemaConfig.name) || "";
  const subgraphDirectives = getDirectiveExtensions(subschemaConfig.schema);
  const directivesToLook = unifiedGraphDirectives || subgraphDirectives;
  for (const directiveName in directivesToLook) {
    if (!subgraphDirectives[directiveName]?.length && unifiedGraphDirectives?.[directiveName]?.length) {
      const directives = unifiedGraphDirectives[directiveName];
      for (const directive of directives) {
        if (directive.subgraph && directive.subgraph !== subgraphName) {
          continue;
        }
        subgraphDirectives[directiveName] ||= [];
        subgraphDirectives[directiveName].push(directive);
      }
    }
  }
  const subgraphExtensions = subschemaConfig.schema.extensions ||= {};
  subgraphExtensions["directives"] = subgraphDirectives;
  const renameTypeNames = {};
  const renameTypeNamesReversed = {};
  const renameFieldByObjectTypeNames = {};
  const renameFieldByInputTypeNames = {};
  const renameFieldByInterfaceTypeNames = {};
  const renameEnumValueByEnumTypeNames = {};
  const renameFieldByTypeNamesReversed = {};
  const renameArgByFieldByTypeNames = {};
  const transforms = subschemaConfig.transforms ||= [];
  const entitiesWithKeys = /* @__PURE__ */ new Set();
  let mergeDirectiveUsed = false;
  subschemaConfig.schema = mapSchema(subschemaConfig.schema, {
    [MapperKind.TYPE]: (type) => {
      const typeDirectives = getDirectiveExtensions(type);
      const keyDirectives = typeDirectives["key"];
      const keys = [];
      if (keyDirectives?.length) {
        for (const keyDirective of keyDirectives) {
          const fields = keyDirective.fields;
          if (fields) {
            keys.push(fields);
          }
        }
        entitiesWithKeys.add([type.name, keys]);
      }
      const sourceDirectives = typeDirectives.source;
      const sourceDirective = sourceDirectives?.find(
        (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
      );
      if (sourceDirective != null) {
        const realName = sourceDirective.name || type.name;
        if (type.name !== realName) {
          renameTypeNames[realName] = type.name;
          renameTypeNamesReversed[type.name] = realName;
          return new (Object.getPrototypeOf(type)).constructor({
            ...type.toConfig(),
            name: realName
          });
        }
      }
    },
    [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName, schema) => {
      const fieldDirectives = getDirectiveExtensions(fieldConfig);
      if (fieldDirectives.merge?.length) {
        mergeDirectiveUsed = true;
      }
      const resolveToDirectives = fieldDirectives.resolveTo;
      if (resolveToDirectives?.length) {
        const type = schema.getType(typeName);
        if (!isObjectType(type)) {
          throw new Error(
            `Type ${typeName} for field ${fieldName} is not an object type`
          );
        }
        const fieldMap = type.getFields();
        const field = fieldMap[fieldName];
        if (!field) {
          throw new Error(`Field ${typeName}.${fieldName} not found`);
        }
        additionalTypeDefs.push({
          kind: Kind.DOCUMENT,
          definitions: [
            {
              kind: Kind.OBJECT_TYPE_DEFINITION,
              name: { kind: Kind.NAME, value: typeName },
              fields: [astFromField(field, schema)]
            }
          ]
        });
      }
      const additionalFieldDirectives = fieldDirectives.additionalField;
      if (additionalFieldDirectives?.length) {
        return null;
      }
      const sourceDirectives = fieldDirectives.source;
      const sourceDirective = sourceDirectives?.find(
        (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
      );
      const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
      const realName = sourceDirective?.name ?? fieldName;
      if (fieldName !== realName) {
        if (!renameFieldByObjectTypeNames[realTypeName]) {
          renameFieldByObjectTypeNames[realTypeName] = {};
        }
        renameFieldByObjectTypeNames[realTypeName][realName] = fieldName;
        if (!renameFieldByTypeNamesReversed[realTypeName]) {
          renameFieldByTypeNamesReversed[realTypeName] = {};
        }
        renameFieldByTypeNamesReversed[realTypeName][fieldName] = realName;
      }
      const hoistDirectives = fieldDirectives.hoist;
      if (hoistDirectives?.length) {
        for (const hoistDirective of hoistDirectives) {
          if (hoistDirective.subgraph === subgraphName) {
            const pathConfig = hoistDirective.pathConfig.map((annotation) => {
              if (typeof annotation === "string") {
                return {
                  fieldName: annotation,
                  argFilter: () => true
                };
              }
              return {
                fieldName: annotation.fieldName,
                argFilter: annotation.filterArgs ? (arg) => !annotation.filterArgs.includes(arg.name) : () => true
              };
            });
            transforms.push(
              new HoistField(realTypeName, pathConfig, fieldName),
              new PruneSchema()
            );
          }
        }
      }
      const newArgs = {};
      if (fieldConfig.args) {
        for (const argName in fieldConfig.args) {
          const argConfig = fieldConfig.args[argName];
          const argDirectives = getDirectiveExtensions(argConfig);
          const argSourceDirectives = argDirectives.source;
          const argSourceDirective = argSourceDirectives?.find(
            (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
          );
          if (argSourceDirective != null) {
            const realArgName = argSourceDirective.name ?? argName;
            newArgs[realArgName] = argConfig;
            if (realArgName !== argName) {
              if (!renameArgByFieldByTypeNames[realTypeName]) {
                renameArgByFieldByTypeNames[realTypeName] = {};
              }
              if (!renameArgByFieldByTypeNames[realTypeName][realName]) {
                renameArgByFieldByTypeNames[realTypeName][realName] = {};
              }
              renameArgByFieldByTypeNames[realTypeName][realName][realArgName] = argName;
            }
          } else {
            newArgs[argName] = argConfig;
          }
        }
      }
      let fieldType = fieldConfig.type;
      if (sourceDirective?.type) {
        const fieldTypeNode = parseTypeNodeWithRenames(
          sourceDirective.type,
          renameTypeNames
        );
        const newType = typeFromAST(subschemaConfig.schema, fieldTypeNode);
        if (!newType) {
          throw new Error(
            `Type ${sourceDirective.type} for field ${typeName}.${fieldName} is not defined in the schema`
          );
        }
        if (!isOutputType(newType)) {
          throw new Error(
            `Type ${sourceDirective.type} for field ${typeName}.${fieldName} is not an output type`
          );
        }
        fieldType = newType;
      }
      return [
        realName,
        {
          ...fieldConfig,
          type: fieldType,
          args: newArgs
        }
      ];
    },
    [MapperKind.INPUT_OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
      const fieldDirectives = getDirectiveExtensions(fieldConfig);
      const sourceDirectives = fieldDirectives.source;
      const sourceDirective = sourceDirectives?.find(
        (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
      );
      if (sourceDirective != null) {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        const realName = sourceDirective.name ?? fieldName;
        if (fieldName !== realName) {
          if (!renameFieldByInputTypeNames[realTypeName]) {
            renameFieldByInputTypeNames[realTypeName] = {};
          }
          renameFieldByInputTypeNames[realTypeName][realName] = fieldName;
        }
        return [realName, fieldConfig];
      }
      const additionalFieldDirectives = fieldDirectives.additionalField;
      if (additionalFieldDirectives?.length) {
        return null;
      }
      return void 0;
    },
    [MapperKind.INTERFACE_FIELD]: (fieldConfig, fieldName, typeName, schema) => {
      const fieldDirectives = getDirectiveExtensions(fieldConfig);
      const resolveToDirectives = fieldDirectives.resolveTo;
      if (resolveToDirectives?.length) {
        const type = schema.getType(typeName);
        if (!isInterfaceType(type)) {
          throw new Error(
            `Type ${typeName} for field ${fieldName} is not an object type`
          );
        }
        const fieldMap = type.getFields();
        const field = fieldMap[fieldName];
        if (!field) {
          throw new Error(`Field ${typeName}.${fieldName} not found`);
        }
        additionalTypeDefs.push({
          kind: Kind.DOCUMENT,
          definitions: [
            {
              kind: Kind.INTERFACE_TYPE_DEFINITION,
              name: { kind: Kind.NAME, value: typeName },
              fields: [astFromField(field, schema)]
            }
          ]
        });
      }
      const additionalFieldDirectives = fieldDirectives.additionalField;
      if (additionalFieldDirectives?.length) {
        return null;
      }
      const sourceDirectives = fieldDirectives.source;
      const sourceDirective = sourceDirectives?.find(
        (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
      );
      if (sourceDirective != null) {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        const realName = sourceDirective.name ?? fieldName;
        if (fieldName !== realName) {
          if (!renameFieldByInterfaceTypeNames[realTypeName]) {
            renameFieldByInterfaceTypeNames[realTypeName] = {};
          }
          renameFieldByInterfaceTypeNames[realTypeName][realName] = fieldName;
        }
        return [realName, fieldConfig];
      }
      return void 0;
    },
    [MapperKind.ENUM_VALUE]: (enumValueConfig, typeName, _schema, externalValue) => {
      const enumDirectives = getDirectiveExtensions(enumValueConfig);
      const sourceDirectives = enumDirectives.source;
      const sourceDirective = sourceDirectives?.find(
        (directive) => compareSubgraphNames(directive.subgraph, subgraphName)
      );
      if (sourceDirective != null) {
        const realValue = sourceDirective.name ?? externalValue;
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        if (externalValue !== realValue) {
          if (!renameEnumValueByEnumTypeNames[realTypeName]) {
            renameEnumValueByEnumTypeNames[realTypeName] = {};
          }
          renameEnumValueByEnumTypeNames[realTypeName][realValue] = externalValue;
        }
        return [
          realValue,
          {
            ...enumValueConfig,
            value: realValue
          }
        ];
      }
      return void 0;
    }
  });
  if (Object.keys(renameTypeNames).length > 0) {
    transforms.push(
      new RenameTypes((typeName) => renameTypeNames[typeName] || typeName)
    );
  }
  if (Object.keys(renameFieldByObjectTypeNames).length > 0) {
    transforms.push(
      new RenameObjectFields((typeName, fieldName, _fieldConfig) => {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        return renameFieldByObjectTypeNames[realTypeName]?.[fieldName] ?? fieldName;
      })
    );
  }
  if (Object.keys(renameFieldByInputTypeNames).length > 0) {
    transforms.push(
      new RenameInputObjectFields((typeName, fieldName, _fieldConfig) => {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        return renameFieldByInputTypeNames[realTypeName]?.[fieldName] ?? fieldName;
      })
    );
  }
  if (Object.keys(renameFieldByInterfaceTypeNames).length > 0) {
    transforms.push(
      new RenameInterfaceFields((typeName, fieldName, _fieldConfig) => {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        return renameFieldByInterfaceTypeNames[realTypeName]?.[fieldName] ?? fieldName;
      })
    );
  }
  if (Object.keys(renameEnumValueByEnumTypeNames).length > 0) {
    transforms.push(
      new TransformEnumValues((typeName, externalValue, enumValueConfig) => {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        const realValue = renameEnumValueByEnumTypeNames[realTypeName]?.[enumValueConfig.value || externalValue] ?? enumValueConfig.value;
        return [
          realValue,
          {
            ...enumValueConfig,
            value: realValue
          }
        ];
      })
    );
  }
  if (Object.keys(renameArgByFieldByTypeNames).length > 0) {
    transforms.push(
      new RenameObjectFieldArguments((typeName, fieldName, argName) => {
        const realTypeName = renameTypeNamesReversed[typeName] ?? typeName;
        const realFieldName = renameFieldByTypeNamesReversed[realTypeName]?.[fieldName] ?? fieldName;
        return renameArgByFieldByTypeNames[realTypeName]?.[realFieldName]?.[argName] ?? argName;
      })
    );
  }
  if (mergeDirectiveUsed) {
    const existingMergeConfig = subschemaConfig.merge || {};
    subschemaConfig.merge = {};
    const subgraphSchemaConfig = subschemaConfig.schema.toConfig();
    subschemaConfig.schema = new GraphQLSchema({
      ...subgraphSchemaConfig,
      directives: [...subgraphSchemaConfig.directives, mergeDirective],
      assumeValid: true
    });
    subschemaConfig.merge = Object.assign(
      existingMergeConfig,
      stitchingDirectivesTransformer(subschemaConfig).merge
    );
    const queryType = subschemaConfig.schema.getQueryType();
    if (!queryType) {
      throw new Error("Query type is required");
    }
    if (transforms.length && subschemaConfig.merge) {
      const subschemaConfigMerge = subschemaConfig.merge;
      const mergeConfig = {};
      for (const realTypeName in subschemaConfig.merge) {
        const renamedTypeName = renameTypeNames[realTypeName] ?? realTypeName;
        mergeConfig[renamedTypeName] = subschemaConfigMerge[realTypeName];
        const realQueryFieldName = mergeConfig[renamedTypeName].fieldName;
        if (realQueryFieldName) {
          mergeConfig[renamedTypeName].fieldName = renameFieldByObjectTypeNames[queryType.name]?.[realQueryFieldName] ?? realQueryFieldName;
        }
        mergeConfig[renamedTypeName].entryPoints = subschemaConfigMerge[realTypeName]?.entryPoints?.map((entryPoint) => ({
          ...entryPoint,
          fieldName: entryPoint.fieldName && (renameFieldByObjectTypeNames[queryType.name]?.[entryPoint.fieldName] ?? entryPoint.fieldName)
        }));
      }
      subschemaConfig.merge = mergeConfig;
    }
  }
  for (const [entityName, keys] of entitiesWithKeys) {
    const mergeConfig = subschemaConfig.merge ||= {};
    const entryPoints = keys.map((key) => ({
      selectionSet: `{ ${key} }`
    }));
    if (entryPoints.length > 1) {
      mergeConfig[entityName] ||= {
        entryPoints
      };
    } else {
      mergeConfig[entityName] ||= entryPoints[0] || {
        selectionSet: `{ __typename }`
      };
    }
  }
  subschemaConfig.executor = function subschemaExecutor(req) {
    return onSubgraphExecute(subgraphName, req);
  };
  return subschemaConfig;
}
const mergeDirective = new GraphQLDirective({
  name: "merge",
  isRepeatable: true,
  locations: [DirectiveLocation.FIELD],
  args: {
    subgraph: {
      type: GraphQLString
    },
    key: {
      type: GraphQLString
    },
    keyField: {
      type: GraphQLString
    },
    keyArg: {
      type: GraphQLString
    },
    argsExpr: {
      type: GraphQLString
    }
  }
});
function parseTypeNodeWithRenames(typeString, renameTypeNames) {
  const typeNode = parseType(typeString);
  return visit(typeNode, {
    NamedType: (node) => {
      const realName = renameTypeNames[node.name.value] ?? node.name.value;
      return {
        ...node,
        name: {
          ...node.name,
          value: realName
        }
      };
    }
  });
}

const restoreExtraDirectives = memoize1(function restoreExtraDirectives2(schema) {
  const queryType = schema.getQueryType();
  if (!queryType) {
    throw new Error("Query type is required");
  }
  const queryTypeExtensions = getDirectiveExtensions(queryType);
  const extraSchemaDefinitionDirectives = queryTypeExtensions?.extraSchemaDefinitionDirective;
  if (extraSchemaDefinitionDirectives?.length) {
    schema = mapSchema(schema, {
      [MapperKind.TYPE]: (type) => {
        const typeDirectiveExtensions = getDirectiveExtensions(type) || {};
        const TypeCtor = Object.getPrototypeOf(type).constructor;
        if (type.name === queryType.name) {
          const typeConfig = type.toConfig();
          return new TypeCtor({
            ...typeConfig,
            extensions: {
              ...type.extensions || {},
              directives: {
                ...typeDirectiveExtensions,
                extraSchemaDefinitionDirective: []
              }
            },
            // Cleanup ASTNode to prevent conflicts
            astNode: void 0
          });
        }
      }
    });
    if (extraSchemaDefinitionDirectives?.length) {
      const schemaDirectives = getDirectiveExtensions(schema);
      for (const extensionObj of extraSchemaDefinitionDirectives) {
        if (extensionObj != null) {
          const { directives } = extensionObj;
          for (const directiveName in directives) {
            const directiveObjects = directives[directiveName];
            if (Array.isArray(directiveObjects)) {
              schemaDirectives[directiveName] ||= [];
              schemaDirectives[directiveName].push(...directiveObjects);
            }
          }
        }
      }
      const schemaExtensions = schema.extensions ||= {};
      schemaExtensions["directives"] = schemaDirectives;
    }
  }
  return schema;
});
function getStitchingDirectivesTransformerForSubschema() {
  const { stitchingDirectivesTransformer } = stitchingDirectives({
    keyDirectiveName: "stitch__key",
    computedDirectiveName: "stitch__computed",
    mergeDirectiveName: "merge",
    canonicalDirectiveName: "stitch__canonical"
  });
  return stitchingDirectivesTransformer;
}
function handleResolveToDirectives(typeDefsOpt, additionalTypeDefs, additionalResolvers) {
  const mergedTypeDefs = mergeTypeDefs([typeDefsOpt, additionalTypeDefs]);
  visit(mergedTypeDefs, {
    [Kind.FIELD_DEFINITION](field, _key, _parent, _path, ancestors) {
      const fieldDirectives = getDirectiveExtensions({ astNode: field });
      const resolveToDirectives = fieldDirectives?.resolveTo;
      if (resolveToDirectives?.length) {
        const targetTypeName = ancestors[ancestors.length - 1].name.value;
        const targetFieldName = field.name.value;
        for (const resolveToDirective of resolveToDirectives) {
          additionalResolvers.push(
            resolveAdditionalResolversWithoutImport({
              ...resolveToDirective,
              targetTypeName,
              targetFieldName
            })
          );
        }
      }
    }
  });
  return mergedTypeDefs;
}
const handleFederationSupergraph = function({
  unifiedGraph,
  onSubgraphExecute,
  onDelegationPlanHooks,
  onDelegationStageExecuteHooks,
  onDelegateHooks,
  additionalTypeDefs: additionalTypeDefsFromConfig = [],
  additionalResolvers: additionalResolversFromConfig = [],
  // no logger was provided, use a muted logger for consistency across plugin hooks
  log: rootLog = new Logger({ level: false })
}) {
  const additionalTypeDefs = [...asArray(additionalTypeDefsFromConfig)];
  const additionalResolvers = [...asArray(additionalResolversFromConfig)];
  let subschemas = [];
  const stitchingDirectivesTransformer = getStitchingDirectivesTransformerForSubschema();
  const realSubgraphNameMap = /* @__PURE__ */ new Map();
  const joinGraphType = unifiedGraph.getType("join__Graph");
  if (isEnumType(joinGraphType)) {
    for (const enumValue of joinGraphType.getValues()) {
      const enumValueDirectives = getDirectiveExtensions(enumValue);
      const joinGraphDirectives = enumValueDirectives?.join__graph;
      if (joinGraphDirectives?.length) {
        for (const joinGraphDirective of joinGraphDirectives) {
          if (joinGraphDirective) {
            realSubgraphNameMap.set(enumValue.name, joinGraphDirective.name);
          }
        }
      }
    }
  }
  const unifiedGraphDirectives = getDirectiveExtensions(unifiedGraph);
  let executableUnifiedGraph = getStitchedSchemaFromSupergraphSdl({
    supergraphSdl: getDocumentNodeFromSchema(unifiedGraph),
    /**
     * This visits over the subgraph schema to get;
     * - Extra Type Defs and Resolvers (additionalTypeDefs & additionalResolvers)
     * - Transport Entries (transportEntryMap)
     * - Type Merging Configuration for the subgraph (subschemaConfig.merge)
     * - Set the executor for the subschema (subschemaConfig.executor)
     */
    onSubschemaConfig: (subschemaConfig) => handleFederationSubschema({
      subschemaConfig,
      unifiedGraphDirectives,
      realSubgraphNameMap,
      additionalTypeDefs,
      stitchingDirectivesTransformer,
      onSubgraphExecute
    }),
    onStitchingOptions(opts) {
      subschemas = opts.subschemas;
      opts.typeDefs = handleResolveToDirectives(
        opts.typeDefs,
        additionalTypeDefs,
        additionalResolvers
      );
      opts.resolvers = additionalResolvers;
      opts.inheritResolversFromInterfaces = true;
      if (onDelegationStageExecuteHooks?.length) {
        for (const subschema of subschemas) {
          if (subschema.merge) {
            for (const typeName in subschema.merge) {
              const mergedTypeConfig = subschema.merge[typeName];
              if (mergedTypeConfig) {
                const originalResolver = createMergedTypeResolver(
                  mergedTypeConfig,
                  typeName
                );
                if (originalResolver) {
                  mergedTypeConfig.resolve = wrapMergedTypeResolver(
                    originalResolver,
                    typeName,
                    onDelegationStageExecuteHooks,
                    rootLog
                  );
                }
              }
            }
          }
        }
      }
    },
    onSubgraphAST(_name, subgraphAST) {
      return visit(subgraphAST, {
        [Kind.OBJECT_TYPE_DEFINITION](node) {
          const typeName = node.name.value;
          return {
            ...node,
            fields: node.fields?.filter((fieldNode) => {
              const fieldDirectives = getDirectiveExtensions({ astNode: fieldNode });
              const resolveToDirectives = fieldDirectives.resolveTo;
              if (resolveToDirectives?.length) {
                additionalTypeDefs.push({
                  kind: Kind.DOCUMENT,
                  definitions: [
                    {
                      kind: Kind.OBJECT_TYPE_DEFINITION,
                      name: { kind: Kind.NAME, value: typeName },
                      fields: [fieldNode]
                    }
                  ]
                });
              }
              const additionalFieldDirectives = fieldDirectives.additionalField;
              if (additionalFieldDirectives?.length) {
                return false;
              }
              return true;
            })
          };
        }
      });
    }
  });
  const inContextSDK = getInContextSDK(
    executableUnifiedGraph,
    // @ts-expect-error Legacy Mesh RawSource is not compatible with new Mesh
    subschemas,
    LegacyLogger.from(rootLog),
    onDelegateHooks || []
  );
  const stitchingInfo = executableUnifiedGraph.extensions?.["stitchingInfo"];
  if (stitchingInfo && onDelegationPlanHooks?.length) {
    for (const typeName in stitchingInfo.mergedTypes) {
      const mergedTypeInfo = stitchingInfo.mergedTypes[typeName];
      if (mergedTypeInfo) {
        const originalDelegationPlanBuilder = mergedTypeInfo.nonMemoizedDelegationPlanBuilder;
        mergedTypeInfo.nonMemoizedDelegationPlanBuilder = (supergraph, sourceSubschema, variables, fragments, fieldNodes, context, info) => {
          let delegationPlanBuilder = originalDelegationPlanBuilder;
          function setDelegationPlanBuilder(newDelegationPlanBuilder) {
            delegationPlanBuilder = newDelegationPlanBuilder;
          }
          const onDelegationPlanDoneHooks = [];
          let log = context.log;
          if (sourceSubschema.name) {
            log = log.child({
              subgraph: sourceSubschema.name
            });
          }
          for (const onDelegationPlan of onDelegationPlanHooks) {
            const onDelegationPlanDone = onDelegationPlan({
              supergraph,
              subgraph: sourceSubschema.name,
              sourceSubschema,
              typeName: mergedTypeInfo.typeName,
              variables,
              fragments,
              fieldNodes,
              log,
              context,
              info,
              delegationPlanBuilder,
              setDelegationPlanBuilder
            });
            if (onDelegationPlanDone) {
              onDelegationPlanDoneHooks.push(onDelegationPlanDone);
            }
          }
          let delegationPlan = delegationPlanBuilder(
            supergraph,
            sourceSubschema,
            variables,
            fragments,
            fieldNodes,
            context,
            info
          );
          function setDelegationPlan(newDelegationPlan) {
            delegationPlan = newDelegationPlan;
          }
          for (const onDelegationPlanDone of onDelegationPlanDoneHooks) {
            onDelegationPlanDone({
              delegationPlan,
              setDelegationPlan
            });
          }
          return delegationPlan;
        };
      }
    }
  }
  return {
    unifiedGraph: executableUnifiedGraph,
    inContextSDK,
    getSubgraphSchema(subgraphName) {
      const subgraph = subschemas.find(
        (s) => s.name && compareSubgraphNames(s.name, subgraphName)
      );
      if (!subgraph) {
        throw new Error(`Subgraph ${subgraphName} not found`);
      }
      return subgraph.schema;
    }
  };
};

function defaultTransportsGetter(kind) {
  const moduleName = `@graphql-mesh/transport-${kind}`;
  return handleMaybePromise(
    () => import(moduleName),
    (transport) => {
      if (!transport) {
        throw new Error(`${moduleName} module is empty`);
      }
      if (typeof transport !== "object") {
        throw new Error(`${moduleName} module does not export an object`);
      }
      let getSubgraphExecutor;
      while (true) {
        if (transport.getSubgraphExecutor) {
          getSubgraphExecutor = transport.getSubgraphExecutor;
          break;
        }
        if (!transport.default) {
          break;
        }
        transport = transport.default;
      }
      if (!getSubgraphExecutor) {
        throw new Error(
          `${moduleName} module does not export "getSubgraphExecutor"`
        );
      }
      if (typeof getSubgraphExecutor !== "function") {
        throw new Error(
          `${moduleName} module's export "getSubgraphExecutor" is not a function`
        );
      }
      return { getSubgraphExecutor };
    }
  );
}
function getTransportExecutor({
  transportContext,
  transportEntry,
  subgraphName = "",
  subgraph,
  transports = defaultTransportsGetter,
  getDisposeReason
}) {
  const kind = transportEntry?.kind || "";
  transportContext?.log.debug(`Loading transport "${kind}"`);
  return handleMaybePromise(
    () => typeof transports === "function" ? transports(kind) : transports[kind],
    (transport) => {
      if (!transport) {
        throw new Error(`Transport "${kind}" is empty`);
      }
      if (typeof transport !== "object") {
        throw new Error(`Transport "${kind}" is not an object`);
      }
      let getSubgraphExecutor;
      if ("default" in transport) {
        getSubgraphExecutor = transport.default?.getSubgraphExecutor;
      } else {
        getSubgraphExecutor = transport.getSubgraphExecutor;
      }
      if (!getSubgraphExecutor) {
        throw new Error(
          `Transport "${kind}" does not have "getSubgraphExecutor"`
        );
      }
      if (typeof getSubgraphExecutor !== "function") {
        throw new Error(
          `Transport "${kind}" "getSubgraphExecutor" is not a function`
        );
      }
      const log = transportContext?.log || // if the logger is not provided by the context, create a new silent one just for consistency in the hooks
      new Logger({ level: false });
      const logger = transportContext?.logger || LegacyLogger.from(log);
      return getSubgraphExecutor({
        subgraphName,
        subgraph,
        transportEntry,
        getTransportExecutor(transportEntry2) {
          return getTransportExecutor({
            transportContext,
            transportEntry: transportEntry2,
            subgraphName,
            subgraph,
            transports,
            getDisposeReason
          });
        },
        getDisposeReason,
        ...transportContext,
        log,
        logger
      });
    }
  );
}
function getOnSubgraphExecute({
  onSubgraphExecuteHooks,
  transportContext,
  transportEntryMap,
  getSubgraphSchema,
  transportExecutorStack,
  transports,
  getDisposeReason,
  batch = true,
  instrumentation
}) {
  const subgraphExecutorMap = /* @__PURE__ */ new Map();
  return function onSubgraphExecute(subgraphName, executionRequest) {
    let executor = subgraphExecutorMap.get(subgraphName);
    if (executor == null) {
      if (transportContext) {
        let log = executionRequest.context?.request ? loggerForRequest(
          transportContext.log,
          executionRequest.context.request
        ) : transportContext.log;
        if (subgraphName) {
          log = log.child({ subgraph: subgraphName });
        }
        transportContext.log = log;
        log.debug("Initializing executor");
      }
      executor = function lazyExecutor(subgraphExecReq) {
        return handleMaybePromise(
          () => (
            // Gets the transport executor for the given subgraph
            getTransportExecutor({
              transportContext,
              subgraphName,
              get subgraph() {
                return getSubgraphSchema(subgraphName);
              },
              get transportEntry() {
                return transportEntryMap[subgraphName];
              },
              transports,
              getDisposeReason
            })
          ),
          (executor_) => {
            if (isDisposable(executor_)) {
              transportExecutorStack.use(executor_);
            }
            executor = wrapExecutorWithHooks({
              executor: executor_,
              onSubgraphExecuteHooks,
              subgraphName,
              transportEntryMap,
              transportContext,
              getSubgraphSchema,
              instrumentation
            });
            subgraphExecutorMap.set(subgraphName, executor);
            return executor(subgraphExecReq);
          }
        );
      };
      subgraphExecutorMap.set(subgraphName, executor);
    }
    if (batch) {
      executor = getBatchingExecutor(
        executionRequest.context || subgraphExecutorMap,
        executor
      );
    }
    return executor(executionRequest);
  };
}
function wrapExecutorWithHooks({
  executor: baseExecutor,
  onSubgraphExecuteHooks,
  subgraphName,
  transportEntryMap,
  getSubgraphSchema,
  transportContext,
  instrumentation
}) {
  function executorWithHooks(baseExecutionRequest) {
    baseExecutionRequest.info = baseExecutionRequest.info || {};
    baseExecutionRequest.info.executionRequest = baseExecutionRequest;
    baseExecutionRequest.rootValue = {
      executionRequest: baseExecutionRequest
    };
    const log = transportContext?.log.child({ subgraph: subgraphName }) || new Logger({ attrs: { subgraph: subgraphName } });
    if (onSubgraphExecuteHooks.length === 0) {
      return baseExecutor(baseExecutionRequest);
    }
    let executor = baseExecutor;
    let executionRequest = baseExecutionRequest;
    const onSubgraphExecuteDoneHooks = [];
    return handleMaybePromise(
      () => iterateAsync(
        onSubgraphExecuteHooks,
        (onSubgraphExecuteHook) => onSubgraphExecuteHook({
          get subgraph() {
            return getSubgraphSchema(subgraphName);
          },
          subgraphName,
          get transportEntry() {
            return transportEntryMap?.[subgraphName];
          },
          executionRequest,
          setExecutionRequest(newExecutionRequest) {
            executionRequest = newExecutionRequest;
          },
          executor,
          setExecutor(newExecutor) {
            log.debug("executor has been updated");
            executor = newExecutor;
          },
          log
        }),
        onSubgraphExecuteDoneHooks
      ),
      () => {
        if (onSubgraphExecuteDoneHooks.length === 0) {
          return executor(executionRequest);
        }
        return handleMaybePromise(
          () => executor(executionRequest),
          (currentResult) => {
            const executeDoneResults = [];
            return handleMaybePromise(
              () => iterateAsync(
                onSubgraphExecuteDoneHooks,
                (onSubgraphExecuteDoneHook) => onSubgraphExecuteDoneHook({
                  result: currentResult,
                  setResult(newResult) {
                    log.debug("overriding result with: ", newResult);
                    currentResult = newResult;
                  }
                }),
                executeDoneResults
              ),
              () => {
                if (!isAsyncIterable(currentResult)) {
                  return currentResult;
                }
                if (executeDoneResults.length === 0) {
                  return currentResult;
                }
                const onNextHooks = [];
                const onEndHooks = [];
                for (const executeDoneResult of executeDoneResults) {
                  if (executeDoneResult.onNext) {
                    onNextHooks.push(executeDoneResult.onNext);
                  }
                  if (executeDoneResult.onEnd) {
                    onEndHooks.push(executeDoneResult.onEnd);
                  }
                }
                if (onNextHooks.length === 0 && onEndHooks.length === 0) {
                  return currentResult;
                }
                return mapAsyncIterator(
                  currentResult,
                  (currentResult2) => handleMaybePromise(
                    () => iterateAsync(
                      onNextHooks,
                      (onNext) => onNext({
                        result: currentResult2,
                        setResult: (res) => {
                          log.debug("overriding result with: ", res);
                          currentResult2 = res;
                        }
                      })
                    ),
                    () => currentResult2
                  ),
                  void 0,
                  () => onEndHooks.length === 0 ? void 0 : iterateAsync(onEndHooks, (onEnd) => onEnd())
                );
              }
            );
          }
        );
      }
    );
  }
  return function instrumentedExecutor(executionRequest) {
    const subgraphInstrument = instrumentation()?.subgraphExecute;
    return getInstrumented({ executionRequest, subgraphName }).asyncFn(
      subgraphInstrument,
      executorWithHooks
    )(executionRequest);
  };
}
function compareSchemas(a, b) {
  if (a === b) {
    return true;
  }
  let aStr;
  if (typeof a === "string") {
    aStr = a;
  } else if (isDocumentNode(a)) {
    aStr = defaultPrintFn(a);
  } else {
    aStr = printSchemaWithDirectives(a);
  }
  let bStr;
  if (typeof b === "string") {
    bStr = b;
  } else if (isDocumentNode(b)) {
    bStr = defaultPrintFn(b);
  } else {
    bStr = printSchemaWithDirectives(b);
  }
  return aStr === bStr;
}
function compareSubgraphNames(name1, name2) {
  return constantCase(name1) === constantCase(name2);
}
function wrapMergedTypeResolver(originalResolver, typeName, onDelegationStageExecuteHooks, log) {
  return (object, context, info, subschema, selectionSet, key, type) => {
    if (subschema.name) {
      log = log.child({ subgraph: subschema.name });
    }
    let resolver = originalResolver;
    function setResolver(newResolver) {
      resolver = newResolver;
    }
    const onDelegationStageExecuteDoneHooks = [];
    for (const onDelegationStageExecute of onDelegationStageExecuteHooks) {
      const onDelegationStageExecuteDone = onDelegationStageExecute({
        object,
        context,
        info,
        subgraph: subschema.name,
        subschema,
        selectionSet,
        key,
        typeName,
        type,
        log,
        resolver,
        setResolver
      });
      if (onDelegationStageExecuteDone) {
        onDelegationStageExecuteDoneHooks.push(onDelegationStageExecuteDone);
      }
    }
    return handleMaybePromise(
      () => resolver(
        object,
        context,
        info,
        subschema,
        selectionSet,
        key,
        type
      ),
      (result) => {
        function setResult(newResult) {
          result = newResult;
        }
        for (const onDelegationStageExecuteDone of onDelegationStageExecuteDoneHooks) {
          onDelegationStageExecuteDone({
            result,
            setResult
          });
        }
        return result;
      }
    );
  };
}
function millisecondsToStr(milliseconds) {
  function numberEnding(number) {
    return number > 1 ? "s" : "";
  }
  let temp = Math.floor(milliseconds / 1e3);
  const days = Math.floor((temp %= 31536e3) / 86400);
  if (days) {
    return days + " day" + numberEnding(days);
  }
  const hours = Math.floor((temp %= 86400) / 3600);
  if (hours) {
    return hours + " hour" + numberEnding(hours);
  }
  const minutes = Math.floor((temp %= 3600) / 60);
  if (minutes) {
    return minutes + " minute" + numberEnding(minutes);
  }
  const seconds = temp % 60;
  if (seconds) {
    return seconds + " second" + numberEnding(seconds);
  }
  return "less than a second";
}
function getTransportEntryMapUsingFusionAndFederationDirectives(unifiedGraph, transportEntryAdditions) {
  unifiedGraph = restoreExtraDirectives(unifiedGraph);
  const transportEntryMap = {};
  const joinGraph = unifiedGraph.getType("join__Graph");
  const schemaDirectives = getDirectiveExtensions(unifiedGraph);
  if (isEnumType(joinGraph)) {
    for (const enumValue of joinGraph.getValues()) {
      const enumValueDirectives = getDirectiveExtensions(enumValue);
      if (enumValueDirectives?.join__graph?.length) {
        for (const joinGraphDirective of enumValueDirectives.join__graph) {
          if (joinGraphDirective.url) {
            transportEntryMap[joinGraphDirective.name] = {
              subgraph: joinGraphDirective.name,
              kind: "http",
              location: joinGraphDirective.url
            };
          }
        }
      }
    }
  }
  if (schemaDirectives?.transport?.length) {
    for (const transportDirective of schemaDirectives.transport) {
      transportEntryMap[transportDirective.subgraph] = transportDirective;
    }
  }
  if (transportEntryAdditions) {
    const wildcardTransportOptions = transportEntryAdditions["*"];
    for (const subgraphName in transportEntryMap) {
      const toBeMerged = [];
      const transportEntry = transportEntryMap[subgraphName];
      if (transportEntry) {
        toBeMerged.push(transportEntry);
      }
      const transportOptionBySubgraph = transportEntryAdditions[subgraphName];
      if (transportOptionBySubgraph) {
        toBeMerged.push(transportOptionBySubgraph);
      }
      const transportOptionByKind = transportEntryAdditions["*." + transportEntry?.kind];
      if (transportOptionByKind) {
        toBeMerged.push(transportOptionByKind);
      }
      if (wildcardTransportOptions) {
        toBeMerged.push(wildcardTransportOptions);
      }
      transportEntryMap[subgraphName] = mergeDeep(toBeMerged);
    }
  }
  const schemaExtensions = unifiedGraph.extensions ||= {};
  const directivesInExtensions = schemaExtensions.directives ||= {};
  const transportEntriesInExtensions = directivesInExtensions.transport = [];
  for (const subgraphName in transportEntryMap) {
    const transportEntry = transportEntryMap[subgraphName];
    if (transportEntry) {
      transportEntriesInExtensions.push(transportEntry);
    }
  }
  return transportEntryMap;
}

function ensureSchema(source) {
  if (isSchema(source)) {
    return source;
  }
  if (typeof source === "string") {
    return buildSchema(source, { assumeValid: true, assumeValidSDL: true });
  }
  if (isDocumentNode(source)) {
    return buildASTSchema(source, { assumeValid: true, assumeValidSDL: true });
  }
  return source;
}
const UNIFIEDGRAPH_CACHE_KEY = "hive-gateway:supergraph";
class UnifiedGraphManager {
  constructor(opts) {
    this.opts = opts;
    this.batch = opts.batch ?? true;
    this.handleUnifiedGraph = opts.handleUnifiedGraph || handleFederationSupergraph;
    this.instrumentation = opts.instrumentation ?? (() => void 0);
    this.onSubgraphExecuteHooks = opts?.onSubgraphExecuteHooks || [];
    this.onDelegationPlanHooks = opts?.onDelegationPlanHooks || [];
    this.onDelegationStageExecuteHooks = opts?.onDelegationStageExecuteHooks || [];
    if (opts.pollingInterval != null) {
      opts.transportContext?.log.debug(
        `Starting polling to Supergraph with interval ${millisecondsToStr(opts.pollingInterval)}`
      );
    }
  }
  batch;
  handleUnifiedGraph;
  unifiedGraph;
  lastLoadedUnifiedGraph;
  onSubgraphExecuteHooks;
  onDelegationPlanHooks;
  onDelegationStageExecuteHooks;
  inContextSDK;
  initialUnifiedGraph$;
  polling$;
  _transportEntryMap;
  _transportExecutorStack;
  lastLoadTime;
  executor;
  instrumentation;
  ensureUnifiedGraph() {
    if (this.polling$ == null && this.opts?.pollingInterval != null && this.lastLoadTime != null && Date.now() - this.lastLoadTime >= this.opts.pollingInterval) {
      this.opts?.transportContext?.log.debug(`Polling Supergraph`);
      this.polling$ = handleMaybePromise(
        () => this.getAndSetUnifiedGraph(),
        () => {
          this.polling$ = void 0;
        },
        (err) => {
          this.opts.transportContext?.log.error(
            err,
            "Failed to poll Supergraph"
          );
          this.polling$ = void 0;
        }
      );
    }
    if (!this.unifiedGraph) {
      if (!this.initialUnifiedGraph$) {
        this.opts?.transportContext?.log.debug(
          "Fetching the initial Supergraph"
        );
        if (this.opts.transportContext?.cache) {
          this.opts.transportContext?.log.debug(
            { key: UNIFIEDGRAPH_CACHE_KEY },
            "Searching for Supergraph in cache..."
          );
          this.initialUnifiedGraph$ = handleMaybePromise(
            () => this.opts.transportContext?.cache?.get(UNIFIEDGRAPH_CACHE_KEY),
            (cachedUnifiedGraph) => {
              if (cachedUnifiedGraph) {
                this.opts.transportContext?.log.debug(
                  { key: UNIFIEDGRAPH_CACHE_KEY },
                  "Found Supergraph in cache"
                );
                return this.handleLoadedUnifiedGraph(cachedUnifiedGraph, true);
              }
              return this.getAndSetUnifiedGraph();
            },
            () => {
              return this.getAndSetUnifiedGraph();
            }
          );
        } else {
          this.initialUnifiedGraph$ = this.getAndSetUnifiedGraph();
        }
        this.initialUnifiedGraph$ = handleMaybePromise(
          () => this.initialUnifiedGraph$,
          (v) => {
            this.initialUnifiedGraph$ = void 0;
            this.opts.transportContext?.log.debug(
              { key: UNIFIEDGRAPH_CACHE_KEY },
              "Initial Supergraph fetched"
            );
            return v;
          }
        );
      }
      return this.initialUnifiedGraph$ || this.unifiedGraph;
    }
    return this.unifiedGraph;
  }
  disposeReason;
  handleLoadedUnifiedGraph(loadedUnifiedGraph, doNotCache) {
    if (loadedUnifiedGraph != null) {
      if (this.lastLoadedUnifiedGraph != null && compareSchemas(loadedUnifiedGraph, this.lastLoadedUnifiedGraph)) {
        this.opts.transportContext?.log.debug(
          "Supergraph has not been changed, skipping..."
        );
        this.lastLoadTime = Date.now();
        if (!this.unifiedGraph) {
          throw new Error(`This should not happen!`);
        }
        return this.unifiedGraph;
      }
      if (!doNotCache && this.opts.transportContext?.cache) {
        let serializedUnifiedGraph;
        if (typeof loadedUnifiedGraph === "string") {
          serializedUnifiedGraph = loadedUnifiedGraph;
        } else if (isSchema(loadedUnifiedGraph)) {
          serializedUnifiedGraph = printSchemaWithDirectives(loadedUnifiedGraph);
        } else if (isDocumentNode(loadedUnifiedGraph)) {
          serializedUnifiedGraph = print(loadedUnifiedGraph);
        }
        if (serializedUnifiedGraph != null) {
          try {
            const ttl = this.opts.pollingInterval ? this.opts.pollingInterval * 1e-3 : (
              // if no polling interval (cache TTL) is configured, default to
              // 60 seconds making sure the unifiedgraph is not kept forever
              // NOTE: we default to 60s because Cloudflare KV TTL does not accept anything less
              60
            );
            this.opts.transportContext?.log.debug(
              { ttl, key: UNIFIEDGRAPH_CACHE_KEY },
              "Caching Supergraph"
            );
            const logCacheSetError = (err) => {
              this.opts.transportContext?.log.debug(
                { err, ttl, key: UNIFIEDGRAPH_CACHE_KEY },
                "Unable to cache Supergraph"
              );
            };
            try {
              const cacheSet$ = this.opts.transportContext?.cache.set(
                UNIFIEDGRAPH_CACHE_KEY,
                serializedUnifiedGraph,
                { ttl }
              );
              if (isPromise(cacheSet$)) {
                cacheSet$.then(() => {
                }, logCacheSetError);
                this._transportExecutorStack?.defer(() => cacheSet$);
              }
            } catch (e) {
              logCacheSetError(e);
            }
          } catch (err) {
            this.opts.transportContext?.log.error(
              err,
              "Failed to initiate caching of Supergraph"
            );
          }
        }
      }
      const ensuredSchema = ensureSchema(loadedUnifiedGraph);
      const transportEntryMap = getTransportEntryMapUsingFusionAndFederationDirectives(
        ensuredSchema,
        this.opts.transportEntryAdditions
      );
      const {
        unifiedGraph: newUnifiedGraph,
        inContextSDK,
        getSubgraphSchema,
        executor
      } = this.handleUnifiedGraph({
        unifiedGraph: ensuredSchema,
        additionalTypeDefs: this.opts.additionalTypeDefs,
        additionalResolvers: this.opts.additionalResolvers,
        onSubgraphExecute(subgraphName, execReq) {
          return onSubgraphExecute(subgraphName, execReq);
        },
        onDelegationPlanHooks: this.onDelegationPlanHooks,
        onDelegationStageExecuteHooks: this.onDelegationStageExecuteHooks,
        onDelegateHooks: this.opts.onDelegateHooks,
        log: this.opts.transportContext?.log
      });
      const transportExecutorStack = new AsyncDisposableStack();
      const onSubgraphExecute = getOnSubgraphExecute({
        onSubgraphExecuteHooks: this.onSubgraphExecuteHooks,
        transports: this.opts.transports,
        transportContext: this.opts.transportContext,
        transportEntryMap,
        getSubgraphSchema,
        transportExecutorStack,
        getDisposeReason: () => this.disposeReason,
        batch: this.batch,
        instrumentation: () => this.instrumentation()
      });
      const previousTransportExecutorStack = this._transportExecutorStack;
      const previousExecutor = this.executor;
      const previousUnifiedGraph = this.lastLoadedUnifiedGraph;
      this.lastLoadedUnifiedGraph = loadedUnifiedGraph;
      this.unifiedGraph = newUnifiedGraph;
      this.executor = executor;
      this._transportExecutorStack = transportExecutorStack;
      this.inContextSDK = inContextSDK;
      this.lastLoadTime = Date.now();
      this._transportEntryMap = transportEntryMap;
      this.opts?.onUnifiedGraphChange?.(newUnifiedGraph);
      this.polling$ = void 0;
      if (previousUnifiedGraph != null) {
        this.disposeReason = createGraphQLError(
          "operation has been aborted due to a schema reload",
          {
            extensions: {
              code: "SCHEMA_RELOAD",
              http: {
                status: 503
              },
              [CRITICAL_ERROR]: true
            }
          }
        );
        this.opts.transportContext?.log.debug(
          "Supergraph has been changed, updating..."
        );
      }
      return handleMaybePromise(
        () => disposeAll([previousTransportExecutorStack, previousExecutor]),
        () => {
          this.disposeReason = void 0;
          return this.unifiedGraph;
        },
        (err) => {
          this.disposeReason = void 0;
          this.opts.transportContext?.log.error(
            err,
            "Failed to dispose the existing transports and executors"
          );
          return this.unifiedGraph;
        }
      );
    } else if (!this.unifiedGraph) {
      throw new Error(
        `Failed to fetch the supergraph, check your supergraph configuration.`
      );
    }
    this.disposeReason = void 0;
    this.polling$ = void 0;
    this.lastLoadTime = Date.now();
    return this.unifiedGraph;
  }
  getAndSetUnifiedGraph() {
    return handleMaybePromise(
      () => this.opts.getUnifiedGraph(this.opts.transportContext),
      (loadedUnifiedGraph) => this.handleLoadedUnifiedGraph(loadedUnifiedGraph),
      (err) => {
        this.opts.transportContext?.log.error(err, "Failed to load Supergraph");
        this.lastLoadTime = Date.now();
        this.disposeReason = void 0;
        this.polling$ = void 0;
        if (!this.unifiedGraph) {
          throw err;
        }
        return this.unifiedGraph;
      }
    );
  }
  getUnifiedGraph() {
    return handleMaybePromise(
      () => this.ensureUnifiedGraph(),
      () => {
        if (!this.unifiedGraph) {
          throw new Error(`This should not happen!`);
        }
        return this.unifiedGraph;
      }
    );
  }
  getExecutor() {
    return handleMaybePromise(
      () => this.ensureUnifiedGraph(),
      () => this.executor
    );
  }
  getContext(base = {}) {
    return handleMaybePromise(
      () => this.ensureUnifiedGraph(),
      () => {
        if (this.inContextSDK) {
          Object.assign(base, this.inContextSDK);
        }
        Object.assign(base, this.opts.transportContext);
        return base;
      }
    );
  }
  getTransportEntryMap() {
    return handleMaybePromise(
      () => this.ensureUnifiedGraph(),
      () => {
        if (!this._transportEntryMap) {
          throw new Error(`This should not happen!`);
        }
        return this._transportEntryMap;
      }
    );
  }
  invalidateUnifiedGraph() {
    return this.getAndSetUnifiedGraph();
  }
  [DisposableSymbols.asyncDispose]() {
    this.disposeReason = createGraphQLError(
      "operation has been aborted because the server is shutting down",
      {
        extensions: {
          code: "SHUTTING_DOWN",
          [CRITICAL_ERROR]: true
        }
      }
    );
    return handleMaybePromise(
      () => disposeAll([this._transportExecutorStack, this.executor]),
      () => {
        this.unifiedGraph = void 0;
        this.initialUnifiedGraph$ = void 0;
        this.lastLoadedUnifiedGraph = void 0;
        this.inContextSDK = void 0;
        this.lastLoadTime = void 0;
        this.polling$ = void 0;
        this.executor = void 0;
        this._transportEntryMap = void 0;
        this._transportExecutorStack = void 0;
        this.executor = void 0;
      }
    );
  }
}
function disposeAll(disposables) {
  const disposalJobs = disposables.map(
    (disposable) => isDisposable(disposable) ? dispose(disposable) : void 0
  ).filter(isPromise);
  if (disposalJobs.length === 0) {
    return void 0;
  } else if (disposalJobs.length === 1) {
    return disposalJobs[0];
  }
  return Promise.all(disposalJobs).then(() => {
  });
}

function getExecutorForUnifiedGraph(opts) {
  const unifiedGraphManager = new UnifiedGraphManager(opts);
  const unifiedGraphExecutor = function unifiedGraphExecutor2(execReq) {
    return handleMaybePromise(
      () => unifiedGraphManager.getContext(execReq.context),
      (context) => {
        function handleExecutor(executor) {
          opts?.transportContext?.log.debug(
            "Executing request on unified graph",
            () => print(execReq.document)
          );
          return executor({
            ...execReq,
            context
          });
        }
        return handleMaybePromise(
          () => unifiedGraphManager.getExecutor(),
          (executor) => {
            if (!executor) {
              return handleMaybePromise(
                () => unifiedGraphManager.getUnifiedGraph(),
                (unifiedGraph) => {
                  opts?.transportContext?.log.debug(
                    "Executing request on unified graph",
                    () => print(execReq.document)
                  );
                  executor = createDefaultExecutor(unifiedGraph);
                  return handleExecutor(executor);
                }
              );
            }
            return handleExecutor(executor);
          }
        );
      }
    );
  };
  Object.defineProperty(unifiedGraphExecutor, DisposableSymbols.asyncDispose, {
    configurable: true,
    enumerable: true,
    get() {
      return function unifiedGraphExecutorDispose() {
        opts?.transportContext?.log.debug("Disposing unified graph executor");
        return unifiedGraphManager[DisposableSymbols.asyncDispose]();
      };
    }
  });
  return unifiedGraphExecutor;
}
function getSdkRequesterForUnifiedGraph(opts) {
  const unifiedGraphExecutor = getExecutorForUnifiedGraph(opts);
  return function sdkRequester(document, variables, operationContext) {
    return handleMaybePromise(
      () => unifiedGraphExecutor({
        document,
        variables,
        context: operationContext
      }),
      (result) => {
        if (isAsyncIterable(result)) {
          return mapAsyncIterator(result, extractDataOrThrowErrors);
        }
        return extractDataOrThrowErrors(result);
      }
    );
  };
}
function extractDataOrThrowErrors(result) {
  if (result.errors) {
    if (result.errors.length === 1) {
      throw result.errors[0];
    }
    throw new AggregateError(result.errors);
  }
  if (!result.data) {
    return null;
  }
  return result.data;
}

export { UnifiedGraphManager, compareSchemas, compareSubgraphNames, ensureSchema, getExecutorForUnifiedGraph, getOnSubgraphExecute, getSdkRequesterForUnifiedGraph, getStitchingDirectivesTransformerForSubschema, getTransportEntryMapUsingFusionAndFederationDirectives, handleFederationSubschema, handleFederationSupergraph, handleResolveToDirectives, millisecondsToStr, restoreExtraDirectives, wrapExecutorWithHooks, wrapMergedTypeResolver };