text: "use strict";
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.TransformerOutput = exports.objectExtension = exports.blankObject = void 0;
  const graphql_1 = require("graphql");
  const strip_directives_1 = require("../utils/strip-directives");
  const defaultSchema_1 = require("../utils/defaultSchema");
  const assert_1 = __importDefault(require("assert"));
  function blankObject(name) {
      return {
          kind: 'ObjectTypeDefinition',
          name: {
              kind: 'Name',
              value: name,
          },
          fields: [],
          directives: [],
          interfaces: [],
      };
  }
  exports.blankObject = blankObject;
  function objectExtension(name, fields = []) {
      return {
          kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
          name: {
              kind: 'Name',
              value: name,
          },
          fields,
          directives: [],
          interfaces: [],
      };
  }
  exports.objectExtension = objectExtension;
  class TransformerOutput {
      constructor(inputDocument) {
          this.nodeMap = {};
          const extensionNodes = [];
          for (const inputDef of inputDocument.definitions) {
              switch (inputDef.kind) {
                  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
                  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
                  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
                  case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
                  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
                  case graphql_1.Kind.UNION_TYPE_DEFINITION:
                      const typeDef = inputDef;
                      if (!this.getType(typeDef.name.value)) {
                          this.addType(typeDef);
                      }
                      break;
                  case graphql_1.Kind.SCHEMA_DEFINITION:
                      if (!this.getSchema()) {
                          const typeDef = inputDef;
                          this.putSchema(typeDef);
                      }
                      break;
                  case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
                  case graphql_1.Kind.ENUM_TYPE_EXTENSION:
                  case graphql_1.Kind.UNION_TYPE_EXTENSION:
                  case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
                  case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
                      extensionNodes.push(inputDef);
                      break;
                  default:
              }
              for (const ext of extensionNodes) {
                  switch (ext.kind) {
                      case graphql_1.Kind.OBJECT_TYPE_EXTENSION:
                          this.addObjectExtension(ext);
                          break;
                      case graphql_1.Kind.INTERFACE_TYPE_EXTENSION:
                          this.addInterfaceExtension(ext);
                          break;
                      case graphql_1.Kind.UNION_TYPE_EXTENSION:
                          this.addUnionExtension(ext);
                          break;
                      case graphql_1.Kind.ENUM_TYPE_EXTENSION:
                          this.addEnumExtension(ext);
                          break;
                      case graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION:
                          this.addInputExtension(ext);
                          break;
                      default:
                          continue;
                  }
              }
              if (!this.getSchema()) {
                  this.putSchema(defaultSchema_1.DEFAULT_SCHEMA_DEFINITION);
              }
          }
      }
      getTypeDefinitionsOfKind(kind) {
          const typeDefs = [];
          for (const key of Object.keys(this.nodeMap)) {
              const definition = this.nodeMap[key];
              if (definition.kind === kind) {
                  typeDefs.push(definition);
              }
          }
          return typeDefs;
      }
      putSchema(obj) {
          this.nodeMap.__schema = obj;
      }
      getSchema() {
          return this.nodeMap.__schema;
      }
      getQueryTypeName() {
          const schemaNode = this.getSchema();
          const queryTypeName = schemaNode.operationTypes.find((op) => op.operation === 'query');
          if (queryTypeName && queryTypeName.type && queryTypeName.type.name) {
              return queryTypeName.type.name.value;
          }
      }
      getQuery() {
          const queryTypeName = this.getQueryTypeName();
          if (queryTypeName) {
              return this.nodeMap[queryTypeName];
          }
      }
      getMutationTypeName() {
          const schemaNode = this.getSchema();
          const mutationTypeName = schemaNode.operationTypes.find((op) => op.operation === 'mutation');
          if (mutationTypeName && mutationTypeName.type && mutationTypeName.type.name) {
              return mutationTypeName.type.name.value;
          }
      }
      getMutation() {
          const mutationTypeName = this.getMutationTypeName();
          if (mutationTypeName) {
              return this.nodeMap[mutationTypeName];
          }
      }
      getSubscriptionTypeName() {
          const schemaNode = this.getSchema();
          const subscriptionTypeName = schemaNode.operationTypes.find((op) => op.operation === 'subscription');
          if (subscriptionTypeName && subscriptionTypeName.type && subscriptionTypeName.type.name) {
              return subscriptionTypeName.type.name.value;
          }
      }
      getSubscription() {
          const subscriptionTypeName = this.getSubscriptionTypeName();
          if (subscriptionTypeName) {
              return this.nodeMap[subscriptionTypeName];
          }
      }
      addType(obj) {
          if (this.nodeMap[obj.name.value]) {
              throw new Error(`Conflicting type '${obj.name.value}' found.`);
          }
          this.nodeMap[obj.name.value] = obj;
      }
      putType(obj) {
          this.nodeMap[obj.name.value] = obj;
      }
      getType(name) {
          return this.nodeMap[name];
      }
      hasType(name) {
          return name in this.nodeMap;
      }
      addObject(obj) {
          if (this.nodeMap[obj.name.value]) {
              throw new Error(`Conflicting type '${obj.name.value}' found.`);
          }
          this.nodeMap[obj.name.value] = obj;
      }
      updateObject(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Type ${obj.name.value} does not exist.`);
          }
          this.nodeMap[obj.name.value] = obj;
      }
      getObject(name) {
          if (this.nodeMap[name]) {
              const node = this.nodeMap[name];
              if (node.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
                  return node;
              }
          }
      }
      addUnion(obj) {
          if (this.nodeMap[obj.name.value]) {
              throw new Error(`Conflicting union '${obj.name.value}' found.`);
          }
          this.nodeMap[obj.name.value] = obj;
      }
      addQueryFields(fields) {
          const queryTypeName = this.getQueryTypeName();
          if (queryTypeName) {
              if (!this.getType(queryTypeName)) {
                  this.addType(blankObject(queryTypeName));
              }
              let queryType = objectExtension(queryTypeName, fields);
              this.addObjectExtension(queryType);
          }
      }
      addMutationFields(fields) {
          const mutationTypeName = this.getMutationTypeName();
          if (mutationTypeName) {
              if (!this.getType(mutationTypeName)) {
                  this.addType(blankObject(mutationTypeName));
              }
              let mutationType = objectExtension(mutationTypeName, fields);
              this.addObjectExtension(mutationType);
          }
      }
      addSubscriptionFields(fields) {
          const subscriptionTypeName = this.getSubscriptionTypeName();
          if (subscriptionTypeName) {
              if (!this.getType(subscriptionTypeName)) {
                  this.addType(blankObject(subscriptionTypeName));
              }
              let subscriptionType = objectExtension(subscriptionTypeName, fields);
              this.addObjectExtension(subscriptionType);
          }
      }
      addObjectExtension(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Cannot extend nonexistent type '${obj.name.value}'.`);
          }
          const oldNode = this.getObject(obj.name.value);
          assert_1.default(oldNode);
          const newDirs = [];
          const oldDirs = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.directives) || [];
          if (obj.directives) {
              for (const newDir of obj.directives) {
                  if (Boolean(oldDirs.find(d => d.name.value === newDir.name.value)) === false) {
                      newDirs.push(newDir);
                  }
              }
          }
          const mergedDirs = [...oldDirs, ...newDirs];
          const oldFields = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.fields) || [];
          const oldFieldMap = oldFields.reduce((acc, field) => ({
              ...acc,
              [field.name.value]: field,
          }), {});
          const newFields = obj.fields || [];
          const mergedFields = [...oldFields];
          for (const newField of newFields) {
              if (oldFieldMap[newField.name.value]) {
                  throw new Error(`Object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
              }
              mergedFields.push(newField);
          }
          const oldInterfaces = (oldNode === null || oldNode === void 0 ? void 0 : oldNode.interfaces) || [];
          const oldInterfaceMap = oldInterfaces.reduce((acc, field) => ({
              ...acc,
              [field.name.value]: field,
          }), {});
          const newInterfaces = obj.interfaces || [];
          const mergedInterfaces = [...oldInterfaces];
          for (const newInterface of newInterfaces) {
              if (oldInterfaceMap[newInterface.name.value]) {
                  throw new Error(`Object type extension '${obj.name.value}' cannot redeclare interface ${newInterface.name.value}`);
              }
              mergedInterfaces.push(newInterface);
          }
          this.nodeMap[oldNode.name.value] = {
              ...oldNode,
              interfaces: mergedInterfaces,
              directives: mergedDirs,
              fields: mergedFields,
          };
      }
      addInputExtension(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Cannot extend nonexistent input '${obj.name.value}'.`);
          }
          const oldNode = this.getType(obj.name.value);
          const newDirs = obj.directives || [];
          const oldDirs = oldNode.directives || [];
          const mergedDirs = [...oldDirs, ...newDirs];
          const oldFields = oldNode.fields || [];
          const oldFieldMap = oldFields.reduce((acc, field) => ({
              ...acc,
              [field.name.value]: field,
          }), {});
          const newFields = obj.fields || [];
          const mergedFields = [...oldFields];
          for (const newField of newFields) {
              if (oldFieldMap[newField.name.value]) {
                  throw new Error(`Input object type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
              }
              mergedFields.push(newField);
          }
          this.nodeMap[oldNode.name.value] = {
              ...oldNode,
              directives: mergedDirs,
              fields: mergedFields,
          };
      }
      addInterfaceExtension(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Cannot extend nonexistent interface '${obj.name.value}'.`);
          }
          const oldNode = this.getType(obj.name.value);
          const newDirs = obj.directives || [];
          const oldDirs = oldNode.directives || [];
          const mergedDirs = [...oldDirs, ...newDirs];
          const oldFields = oldNode.fields || [];
          const oldFieldMap = oldFields.reduce((acc, field) => ({
              ...acc,
              [field.name.value]: field,
          }), {});
          const newFields = obj.fields || [];
          const mergedFields = [...oldFields];
          for (const newField of newFields) {
              if (oldFieldMap[newField.name.value]) {
                  throw new Error(`Interface type extension '${obj.name.value}' cannot redeclare field ${newField.name.value}`);
              }
              mergedFields.push(newField);
          }
          this.nodeMap[oldNode.name.value] = {
              ...oldNode,
              directives: mergedDirs,
              fields: mergedFields,
          };
      }
      addUnionExtension(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Cannot extend nonexistent union '${obj.name.value}'.`);
          }
          const oldNode = this.getType(obj.name.value);
          const newDirs = obj.directives || [];
          const oldDirs = oldNode.directives || [];
          const mergedDirs = [...oldDirs, ...newDirs];
          const oldTypes = oldNode.types || [];
          const oldTypeMap = oldTypes.reduce((acc, type) => ({
              ...acc,
              [type.name.value]: true,
          }), {});
          const newTypes = obj.types || [];
          const mergedFields = [...oldTypes];
          for (const newType of newTypes) {
              if (oldTypeMap[newType.name.value]) {
                  throw new Error(`Union type extension '${obj.name.value}' cannot redeclare type ${newType.name.value}`);
              }
              mergedFields.push(newType);
          }
          this.nodeMap[oldNode.name.value] = {
              ...oldNode,
              directives: mergedDirs,
              types: mergedFields,
          };
      }
      addEnumExtension(obj) {
          if (!this.nodeMap[obj.name.value]) {
              throw new Error(`Cannot extend nonexistent enum '${obj.name.value}'.`);
          }
          const oldNode = this.getType(obj.name.value);
          const newDirs = obj.directives || [];
          const oldDirs = oldNode.directives || [];
          const mergedDirs = [...oldDirs, ...newDirs];
          const oldValues = oldNode.values || [];
          const oldValuesMap = oldValues.reduce((acc, type) => ({
              ...acc,
              [type.name.value]: true,
          }), {});
          const newValues = obj.values || [];
          const mergedValues = [...oldValues];
          for (const newValue of newValues) {
              if (oldValuesMap[newValue.name.value]) {
                  throw new Error(`Enum type extension '${obj.name.value}' cannot redeclare value ${newValue.name.value}`);
              }
              mergedValues.push(newValue);
          }
          this.nodeMap[oldNode.name.value] = {
              ...oldNode,
              directives: mergedDirs,
              values: mergedValues,
          };
      }
      addInput(inp) {
          if (this.nodeMap[inp.name.value]) {
              throw new Error(`Conflicting input type '${inp.name.value}' found.`);
          }
          this.nodeMap[inp.name.value] = inp;
      }
      addEnum(en) {
          if (this.nodeMap[en.name.value]) {
              throw new Error(`Conflicting enum type '${en.name.value}' found.`);
          }
          this.nodeMap[en.name.value] = en;
      }
      buildSchema() {
          var _a, _b, _c;
          const mutationNode = this.getMutation();
          const queryNode = this.getQuery();
          const subscriptionNode = this.getSubscription();
          let includeMutation = true;
          let includeQuery = true;
          let includeSubscription = true;
          if (!mutationNode || ((_a = mutationNode === null || mutationNode === void 0 ? void 0 : mutationNode.fields) === null || _a === void 0 ? void 0 : _a.length) === 0) {
              delete this.nodeMap.Mutation;
              includeMutation = false;
          }
          if (!queryNode || ((_b = queryNode === null || queryNode === void 0 ? void 0 : queryNode.fields) === null || _b === void 0 ? void 0 : _b.length) === 0) {
              delete this.nodeMap.Query;
              includeQuery = false;
          }
          if (!subscriptionNode || ((_c = subscriptionNode === null || subscriptionNode === void 0 ? void 0 : subscriptionNode.fields) === null || _c === void 0 ? void 0 : _c.length) === 0) {
              delete this.nodeMap.Subscription;
              includeSubscription = false;
          }
          const ops = [];
          if (includeQuery) {
              ops.push(TransformerOutput.makeOperationType('query', queryNode.name.value));
          }
          if (includeMutation) {
              ops.push(TransformerOutput.makeOperationType('mutation', mutationNode.name.value));
          }
          if (includeSubscription) {
              ops.push(TransformerOutput.makeOperationType('subscription', subscriptionNode.name.value));
          }
          const schema = TransformerOutput.makeSchema(ops);
          this.putSchema(schema);
          const astSansDirectives = strip_directives_1.stripDirectives({
              kind: 'Document',
              definitions: Object.values(this.nodeMap),
          }, ['aws_subscribe', 'aws_auth', 'aws_api_key', 'aws_iam', 'aws_oidc', 'aws_cognito_user_pools', 'deprecated']);
          const SDL = graphql_1.print(astSansDirectives);
          return SDL;
      }
      static makeOperationType(operation, type) {
          return {
              kind: 'OperationTypeDefinition',
              operation,
              type: {
                  kind: 'NamedType',
                  name: {
                      kind: 'Name',
                      value: type,
                  },
              },
          };
      }
      static makeSchema(operationTypes) {
          return {
              kind: graphql_1.Kind.SCHEMA_DEFINITION,
              operationTypes,
              directives: [],
          };
      }
  }
  exports.TransformerOutput = TransformerOutput;
  //# sourceMappingURL=output.js.map