text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  const graphql_1 = require("graphql");
  const graphql_2 = require("./utilities/graphql");
  const predicates_1 = require("./utilities/predicates");
  function buildServiceDefinition(modules) {
      const errors = [];
      const typeDefinitionsMap = Object.create(null);
      const typeExtensionsMap = Object.create(null);
      const directivesMap = Object.create(null);
      const schemaDefinitions = [];
      const schemaExtensions = [];
      for (let module of modules) {
          if (graphql_2.isNode(module) && graphql_2.isDocumentNode(module)) {
              module = { typeDefs: module };
          }
          for (const definition of module.typeDefs.definitions) {
              if (graphql_1.isTypeDefinitionNode(definition)) {
                  const typeName = definition.name.value;
                  if (typeDefinitionsMap[typeName]) {
                      typeDefinitionsMap[typeName].push(definition);
                  }
                  else {
                      typeDefinitionsMap[typeName] = [definition];
                  }
              }
              else if (graphql_1.isTypeExtensionNode(definition)) {
                  const typeName = definition.name.value;
                  if (typeExtensionsMap[typeName]) {
                      typeExtensionsMap[typeName].push(definition);
                  }
                  else {
                      typeExtensionsMap[typeName] = [definition];
                  }
              }
              else if (definition.kind === graphql_1.Kind.DIRECTIVE_DEFINITION) {
                  const directiveName = definition.name.value;
                  if (directivesMap[directiveName]) {
                      directivesMap[directiveName].push(definition);
                  }
                  else {
                      directivesMap[directiveName] = [definition];
                  }
              }
              else if (definition.kind === graphql_1.Kind.SCHEMA_DEFINITION) {
                  schemaDefinitions.push(definition);
              }
              else if (definition.kind === graphql_1.Kind.SCHEMA_EXTENSION) {
                  schemaExtensions.push(definition);
              }
          }
      }
      for (const [typeName, typeDefinitions] of Object.entries(typeDefinitionsMap)) {
          if (typeDefinitions.length > 1) {
              errors.push(new graphql_1.GraphQLError(`Type "${typeName}" was defined more than once.`, typeDefinitions));
          }
      }
      for (const [directiveName, directives] of Object.entries(directivesMap)) {
          if (directives.length > 1) {
              errors.push(new graphql_1.GraphQLError(`Directive "${directiveName}" was defined more than once.`, directives));
          }
      }
      let operationTypeMap;
      if (schemaDefinitions.length > 0 || schemaExtensions.length > 0) {
          operationTypeMap = {};
          const schemaDefinition = schemaDefinitions[schemaDefinitions.length - 1];
          const operationTypes = [schemaDefinition, ...schemaExtensions]
              .map(node => node.operationTypes)
              .filter(predicates_1.isNotNullOrUndefined)
              .flat();
          for (const operationType of operationTypes) {
              const typeName = operationType.type.name.value;
              const operation = operationType.operation;
              if (operationTypeMap[operation]) {
                  throw new graphql_1.GraphQLError(`Must provide only one ${operation} type in schema.`, [schemaDefinition]);
              }
              if (!(typeDefinitionsMap[typeName] || typeExtensionsMap[typeName])) {
                  throw new graphql_1.GraphQLError(`Specified ${operation} type "${typeName}" not found in document.`, [schemaDefinition]);
              }
              operationTypeMap[operation] = typeName;
          }
      }
      else {
          operationTypeMap = {
              query: "Query",
              mutation: "Mutation",
              subscription: "Subscription"
          };
      }
      for (const [typeName, typeExtensions] of Object.entries(typeExtensionsMap)) {
          if (!typeDefinitionsMap[typeName]) {
              if (Object.values(operationTypeMap).includes(typeName)) {
                  typeDefinitionsMap[typeName] = [
                      {
                          kind: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
                          name: {
                              kind: graphql_1.Kind.NAME,
                              value: typeName
                          }
                      }
                  ];
              }
              else {
                  errors.push(new graphql_1.GraphQLError(`Cannot extend type "${typeName}" because it does not exist in the existing schema.`, typeExtensions));
              }
          }
      }
      if (errors.length > 0) {
          return { errors };
      }
      try {
          const typeDefinitions = Object.values(typeDefinitionsMap).flat();
          const directives = Object.values(directivesMap).flat();
          let schema = graphql_1.buildASTSchema({
              kind: graphql_1.Kind.DOCUMENT,
              definitions: [...typeDefinitions, ...directives]
          });
          const typeExtensions = Object.values(typeExtensionsMap).flat();
          if (typeExtensions.length > 0) {
              schema = graphql_1.extendSchema(schema, {
                  kind: graphql_1.Kind.DOCUMENT,
                  definitions: typeExtensions
              });
          }
          for (const module of modules) {
              if (!module.resolvers)
                  continue;
              addResolversToSchema(schema, module.resolvers);
          }
          return { schema };
      }
      catch (error) {
          return { errors: [error] };
      }
  }
  exports.buildServiceDefinition = buildServiceDefinition;
  function addResolversToSchema(schema, resolvers) {
      for (const [typeName, fieldConfigs] of Object.entries(resolvers)) {
          const type = schema.getType(typeName);
          if (!graphql_1.isObjectType(type))
              continue;
          const fieldMap = type.getFields();
          for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
              if (fieldName.startsWith("__")) {
                  type[fieldName.substring(2)] = fieldConfig;
                  continue;
              }
              const field = fieldMap[fieldName];
              if (!field)
                  continue;
              if (typeof fieldConfig === "function") {
                  field.resolve = fieldConfig;
              }
              else {
                  if (fieldConfig.resolve) {
                      field.resolve = fieldConfig.resolve;
                  }
                  if (fieldConfig.subscribe) {
                      field.subscribe = fieldConfig.subscribe;
                  }
              }
          }
      }
  }
  //# sourceMappingURL=buildServiceDefinition.js.map