text: 'use strict';
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.addTemporalTypes = exports.possiblyAddArgument = exports.augmentResolvers = exports.augmentTypeMap = undefined;
  
  var _typeof2 = require('babel-runtime/helpers/typeof');
  
  var _typeof3 = _interopRequireDefault(_typeof2);
  
  var _values = require('babel-runtime/core-js/object/values');
  
  var _values2 = _interopRequireDefault(_values);
  
  var _keys = require('babel-runtime/core-js/object/keys');
  
  var _keys2 = _interopRequireDefault(_keys);
  
  var _index = require('./index');
  
  var _graphql = require('graphql');
  
  var _utils = require('./utils');
  
  var _cloneDeep = require('lodash/cloneDeep');
  
  var _cloneDeep2 = _interopRequireDefault(_cloneDeep);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  var augmentTypeMap = exports.augmentTypeMap = function augmentTypeMap(typeMap, config) {
    var types = (0, _keys2.default)(typeMap);
    // For now, the Query and Mutation type names have been
    // elevated from various use cases to be set here
    var queryType = 'Query';
    var mutationType = 'Mutation';
    typeMap = initializeOperationTypes({
      types: types,
      typeMap: typeMap,
      config: config,
      queryType: queryType,
      mutationType: mutationType
    });
    // adds relation directives on relation types
    // if not written, with default args
    typeMap = computeRelationTypeDirectiveDefaults(typeMap);
    typeMap = addTemporalTypes(typeMap, config);
    var queryMap = (0, _utils.createOperationMap)(typeMap.Query);
    var mutationMap = (0, _utils.createOperationMap)(typeMap.Mutation);
    var astNode = {};
    var typeName = '';
    (0, _keys2.default)(typeMap).forEach(function (t) {
      astNode = typeMap[t];
      typeName = astNode.name.value;
      if (!(0, _utils.isTemporalType)(typeName)) {
        astNode = augmentType(astNode, typeMap, config, queryType);
        // Query API Only
        // config is used in augmentQueryArguments to prevent adding node list args
        if (shouldAugmentType({
          config: config,
          operationType: queryType,
          type: typeName
        })) {
          typeMap = possiblyAddQuery(astNode, typeMap, queryMap);
          typeMap = possiblyAddOrderingEnum(astNode, typeMap);
        }
        // Mutation API Only
        // adds node selection input types for each type
        if (shouldAugmentType({
          config: config,
          operationType: mutationType,
          type: typeName
        })) {
          typeMap = possiblyAddTypeInput({
            astNode: astNode,
            typeMap: typeMap,
            mutationType: mutationType,
            config: config
          });
          typeMap = possiblyAddTypeMutations({
            astNode: astNode,
            typeMap: typeMap,
            mutationMap: mutationMap,
            config: config,
            mutationType: mutationType,
            typeName: typeName
          });
        }
        // Relation Type SDL support and Relation Mutation API
        typeMap = handleRelationFields({
          astNode: astNode,
          typeMap: typeMap,
          mutationMap: mutationMap,
          config: config,
          queryType: queryType,
          mutationType: mutationType
        });
        typeMap[t] = astNode;
      }
    });
    typeMap = augmentQueryArguments(typeMap, config, queryType);
    // add directive declarations for graphql@14 support
    typeMap = (0, _utils.addDirectiveDeclarations)(typeMap);
    return typeMap;
  };
  
  var augmentType = function augmentType(astNode, typeMap, config, queryType) {
    if ((0, _utils.isNodeType)(astNode)) {
      astNode.fields = addOrReplaceNodeIdField(astNode);
      astNode.fields = possiblyAddTypeFieldArguments(astNode, typeMap, config, queryType);
    }
    return astNode;
  };
  
  var augmentQueryArguments = function augmentQueryArguments(typeMap, config, queryType) {
    // adds first / offset / orderBy to queries returning node type lists
    var queryMap = (0, _utils.createOperationMap)(typeMap.Query);
    var args = [];
    var valueTypeName = '';
    var valueType = {};
    var field = {};
    var queryNames = (0, _keys2.default)(queryMap);
    if (queryNames.length > 0) {
      queryNames.forEach(function (t) {
        field = queryMap[t];
        valueTypeName = (0, _utils.getNamedType)(field).name.value;
        valueType = typeMap[valueTypeName];
        if ((0, _utils.isNodeType)(valueType) && (0, _utils.isListType)(field) && shouldAugmentType({
          config: config,
          operationType: queryType,
          type: valueTypeName
        })) {
          // does not add arguments if the field value type is excluded
          args = field.arguments;
          queryMap[t].arguments = possiblyAddArgument(args, 'first', 'Int');
          queryMap[t].arguments = possiblyAddArgument(args, 'offset', 'Int');
          queryMap[t].arguments = possiblyAddArgument(args, 'orderBy', '_' + valueTypeName + 'Ordering');
        }
      });
      typeMap.Query.fields = (0, _values2.default)(queryMap);
    }
    return typeMap;
  };
  
  var augmentResolvers = exports.augmentResolvers = function augmentResolvers(augmentedTypeMap, resolvers) {
    var queryResolvers = resolvers && resolvers.Query ? resolvers.Query : {};
    var generatedQueryMap = (0, _utils.createOperationMap)(augmentedTypeMap.Query);
    queryResolvers = possiblyAddResolvers(generatedQueryMap, queryResolvers);
    if ((0, _keys2.default)(queryResolvers).length > 0) {
      resolvers.Query = queryResolvers;
    }
    var mutationResolvers = resolvers && resolvers.Mutation ? resolvers.Mutation : {};
    var generatedMutationMap = (0, _utils.createOperationMap)(augmentedTypeMap.Mutation);
    mutationResolvers = possiblyAddResolvers(generatedMutationMap, mutationResolvers);
    if ((0, _keys2.default)(mutationResolvers).length > 0) {
      resolvers.Mutation = mutationResolvers;
    }
    // must implement __resolveInfo for every Interface type
    // we use "FRAGMENT_TYPE" key to identify the Interface implementation
    // type at runtime, so grab this value
    var interfaceTypes = (0, _keys2.default)(augmentedTypeMap).filter(function (e) {
      return augmentedTypeMap[e].kind === 'InterfaceTypeDefinition';
    });
    interfaceTypes.map(function (e) {
      resolvers[e] = {};
  
      resolvers[e]['__resolveType'] = function (obj, context, info) {
        return obj['FRAGMENT_TYPE'];
      };
    });
  
    return resolvers;
  };
  
  var possiblyAddArgument = exports.possiblyAddArgument = function possiblyAddArgument(args, fieldName, fieldType) {
    var fieldIndex = args.findIndex(function (e) {
      return e.name.value === fieldName;
    });
    if (fieldIndex === -1) {
      args.push({
        kind: 'InputValueDefinition',
        name: {
          kind: 'Name',
          value: fieldName
        },
        type: {
          kind: 'NamedType',
          name: {
            kind: 'Name',
            value: fieldType
          }
        },
        directives: []
      });
    }
    return args;
  };
  
  var possiblyAddResolvers = function possiblyAddResolvers(operationTypeMap, resolvers) {
    var operationName = '';
    return (0, _keys2.default)(operationTypeMap).reduce(function (acc, t) {
      // if no resolver provided for this operation type field
      operationName = operationTypeMap[t].name.value;
      if (acc[operationName] === undefined) {
        acc[operationName] = _index.neo4jgraphql;
      }
      return acc;
    }, resolvers);
  };
  
  var possiblyAddTypeInput = function possiblyAddTypeInput(_ref) {
    var astNode = _ref.astNode,
        typeMap = _ref.typeMap,
        mutationType = _ref.mutationType,
        config = _ref.config;
  
    var inputName = '_' + astNode.name.value + 'Input';
    if ((0, _utils.isNodeType)(astNode)) {
      if (typeMap[inputName] === undefined) {
        var pk = (0, _utils.getPrimaryKey)(astNode);
        if (pk) {
          var nodeInputType = '\n          input ' + inputName + ' { ' + pk.name.value + ': ' +
          // Always exactly require the pk of a node type
          decideFieldType((0, _utils.getNamedType)(pk).name.value) + '! }';
          typeMap[inputName] = (0, _graphql.parse)(nodeInputType);
        }
      }
    } else if ((0, _utils.getTypeDirective)(astNode, 'relation')) {
      // Only used for the .data argument in generated relation creation mutations
      if (typeMap[inputName] === undefined) {
        var fieldName = '';
        var valueType = {};
        var valueTypeName = '';
        var fields = astNode.fields;
        // The .data arg on add relation mutations,
        // which is the only arg in the API that uses
        // relation input types, is only generate if there
        // is at least one non-directed field (property field)
        var hasSomePropertyField = fields.find(function (e) {
          return e.name.value !== 'from' && e.name.value !== 'to';
        });
        var fromField = fields.find(function (e) {
          return e.name.value === 'from';
        });
        var fromName = (0, _utils.getNamedType)(fromField).name.value;
        var toField = fields.find(function (e) {
          return e.name.value === 'to';
        });
        var toName = (0, _utils.getNamedType)(toField).name.value;
        // only generate an input type for the relationship if we know that both
        // the from and to nodes are not excluded, since thus we know that
        // relation mutations are generated for this relation, which would
        // make use of the relation input type
        var shouldCreateRelationInput = shouldAugmentRelationField({
          config: config,
          operationType: mutationType,
          fromName: fromName,
          toName: toName
        });
        if (hasSomePropertyField && shouldCreateRelationInput) {
          var _field = {};
          typeMap[inputName] = (0, _graphql.parse)('input ' + inputName + ' {' + fields.reduce(function (acc, t) {
            fieldName = t.name.value;
            valueTypeName = (0, _utils.getNamedType)(t).name.value;
            valueType = typeMap[valueTypeName];
            _field = (0, _cloneDeep2.default)(t);
            if (fieldName !== '_id' && fieldName !== 'to' && fieldName !== 'from' && !(0, _utils.getFieldDirective)(t, 'cypher') && ((0, _utils.isBasicScalar)(valueTypeName) || (0, _utils.isKind)(valueType, 'EnumTypeDefinition') || (0, _utils.isKind)(valueType, 'ScalarTypeDefinition') || (0, _utils.isTemporalType)(valueTypeName))) {
              _field.kind = 'InputValueDefinition';
              _field.type = transformManagedFieldTypes(_field.type);
              acc.push((0, _graphql.print)(_field));
            }
            return acc;
          }, []).join('\n') + '}');
        }
      }
    }
    return typeMap;
  };
  
  var possiblyAddQuery = function possiblyAddQuery(astNode, typeMap, queryMap) {
    if ((0, _utils.isNodeType)(astNode)) {
      var name = astNode.name.value;
      if (queryMap[name] === undefined) {
        typeMap.Query.fields.push({
          kind: 'FieldDefinition',
          name: {
            kind: 'Name',
            value: name
          },
          arguments: createQueryArguments(astNode, typeMap),
          type: {
            kind: 'ListType',
            type: {
              kind: 'NamedType',
              name: {
                kind: 'Name',
                value: name
              }
            }
          },
          directives: []
        });
      }
    }
    return typeMap;
  };
  
  var possiblyAddOrderingEnum = function possiblyAddOrderingEnum(astNode, typeMap) {
    if ((0, _utils.isNodeType)(astNode)) {
      var name = '_' + astNode.name.value + 'Ordering';
      var values = createOrderingFields(astNode.fields, typeMap);
      // Add ordering enum if it does not exist already and if
      // there is at least one basic scalar field on this type
      if (typeMap[name] === undefined && values.length > 0) {
        typeMap[name] = {
          kind: 'EnumTypeDefinition',
          name: {
            kind: 'Name',
            value: name
          },
          directives: [],
          values: values
        };
      }
    }
    return typeMap;
  };
  
  var possiblyAddTypeMutations = function possiblyAddTypeMutations(_ref2) {
    var astNode = _ref2.astNode,
        typeMap = _ref2.typeMap,
        mutationMap = _ref2.mutationMap,
        config = _ref2.config,
        mutationType = _ref2.mutationType,
        typeName = _ref2.typeName;
  
    if ((0, _utils.isNodeType)(astNode) && shouldAugmentType({
      config: config,
      operationType: mutationType,
      type: typeName
    })) {
      typeMap = possiblyAddTypeMutation('Create', astNode, typeMap, mutationMap);
      typeMap = possiblyAddTypeMutation('Update', astNode, typeMap, mutationMap);
      typeMap = possiblyAddTypeMutation('Delete', astNode, typeMap, mutationMap);
    }
    return typeMap;
  };
  
  var handleRelationFields = function handleRelationFields(_ref3) {
    var astNode = _ref3.astNode,
        typeMap = _ref3.typeMap,
        mutationMap = _ref3.mutationMap,
        config = _ref3.config,
        queryType = _ref3.queryType,
        mutationType = _ref3.mutationType;
  
    var typeName = astNode.name.value;
    var fields = astNode.fields;
    var fieldCount = fields ? fields.length : 0;
    var relationFieldDirective = {};
    var fieldValueName = '';
    var relatedAstNode = {};
    var relationTypeDirective = {};
    var capitalizedFieldName = '';
    var field = {};
    var fieldIndex = 0;
    if ((0, _utils.isNodeType)(astNode)) {
      for (; fieldIndex < fieldCount; ++fieldIndex) {
        field = fields[fieldIndex];
        fieldValueName = (0, _utils.getNamedType)(field).name.value;
        capitalizedFieldName = capitalizeName(field.name.value);
        relatedAstNode = typeMap[fieldValueName];
        if (relatedAstNode) {
          relationTypeDirective = (0, _utils.getTypeDirective)(relatedAstNode, 'relation');
          relationFieldDirective = (0, _utils.getFieldDirective)(field, 'relation');
          // continue if typeName is allowed
          // in either Query or Mutation
          if ((0, _utils.isNodeType)(relatedAstNode)) {
            // the field has a node type
            if (relationFieldDirective) {
              // Relation Mutation API
              // relation directive exists on field
              typeMap = handleRelationFieldDirective({
                relatedAstNode: relatedAstNode,
                typeName: typeName,
                capitalizedFieldName: capitalizedFieldName,
                fieldValueName: fieldValueName,
                relationFieldDirective: relationFieldDirective,
                mutationMap: mutationMap,
                typeMap: typeMap,
                config: config,
                mutationType: mutationType
              });
            }
          } else if (relationTypeDirective) {
            // Query and Relation Mutation API
            // the field value is a non-node type using a relation type directive
            typeMap = handleRelationTypeDirective({
              relatedAstNode: relatedAstNode,
              typeName: typeName,
              fields: fields,
              field: field,
              fieldIndex: fieldIndex,
              capitalizedFieldName: capitalizedFieldName,
              relationTypeDirective: relationTypeDirective,
              config: config,
              queryType: queryType,
              mutationType: mutationType,
              typeMap: typeMap,
              mutationMap: mutationMap
            });
          }
        }
      }
    }
    return typeMap;
  };
  
  var validateRelationTypeDirectedFields = function validateRelationTypeDirectedFields(typeName, fromName, toName) {
    // directive to and from are not the same and neither are equal to this
    if (fromName !== toName && toName !== typeName && fromName !== typeName) {
      throw new Error('The \'' + field.name.value + '\' field on the \'' + typeName + '\' type uses the \'' + relatedAstNode.name.value + '\'\n    but \'' + relatedAstNode.name.value + '\' comes from \'' + fromName + '\' and goes to \'' + toName + '\'');
    }
    return true;
  };
  
  var shouldAugmentRelationField = function shouldAugmentRelationField(_ref4) {
    var config = _ref4.config,
        operationType = _ref4.operationType,
        fromName = _ref4.fromName,
        toName = _ref4.toName;
  
    // validate that both the fromName and toName node types
    // have not been excluded
    return shouldAugmentType({
      config: config,
      operationType: operationType,
      type: fromName
    }) && shouldAugmentType({
      config: config,
      operationType: operationType,
      type: toName
    });
  };
  
  var handleRelationTypeDirective = function handleRelationTypeDirective(_ref5) {
    var relatedAstNode = _ref5.relatedAstNode,
        typeName = _ref5.typeName,
        fields = _ref5.fields,
        field = _ref5.field,
        fieldIndex = _ref5.fieldIndex,
        capitalizedFieldName = _ref5.capitalizedFieldName,
        relationTypeDirective = _ref5.relationTypeDirective,
        config = _ref5.config,
        queryType = _ref5.queryType,
        mutationType = _ref5.mutationType,
        typeMap = _ref5.typeMap,
        mutationMap = _ref5.mutationMap;
  
    var typeDirectiveArgs = relationTypeDirective ? relationTypeDirective.arguments : [];
    var nameArgument = typeDirectiveArgs.find(function (e) {
      return e.name.value === 'name';
    });
    var fromArgument = typeDirectiveArgs.find(function (e) {
      return e.name.value === 'from';
    });
    var toArgument = typeDirectiveArgs.find(function (e) {
      return e.name.value === 'to';
    });
    var relationName = nameArgument.value.value;
    var fromName = fromArgument.value.value;
    var toName = toArgument.value.value;
    // Relation Mutation API, adds relation mutation to Mutation
    if (shouldAugmentRelationField({
      config: config,
      operationType: mutationType,
      fromName: fromName,
      toName: toName
    }) && validateRelationTypeDirectedFields(typeName, fromName, toName)) {
      typeMap = possiblyAddRelationMutationField(typeName, capitalizedFieldName, fromName, toName, mutationMap, typeMap, relationName, relatedAstNode, true);
    }
    // Relation type field payload transformation for selection sets
    typeMap = possiblyAddRelationTypeFieldPayload(relatedAstNode, capitalizedFieldName, typeName, typeMap, field);
    // Replaces the field's value with the generated payload type
    fields[fieldIndex] = replaceRelationTypeValue(fromName, toName, field, capitalizedFieldName, typeName);
    return typeMap;
  };
  
  var handleRelationFieldDirective = function handleRelationFieldDirective(_ref6) {
    var relatedAstNode = _ref6.relatedAstNode,
        typeName = _ref6.typeName,
        capitalizedFieldName = _ref6.capitalizedFieldName,
        fieldValueName = _ref6.fieldValueName,
        relationFieldDirective = _ref6.relationFieldDirective,
        mutationMap = _ref6.mutationMap,
        typeMap = _ref6.typeMap,
        config = _ref6.config,
        mutationType = _ref6.mutationType;
  
    var fromName = typeName;
    var toName = fieldValueName;
    // Mutation API, relation mutations for field directives
    if (shouldAugmentRelationField({
      config: config,
      operationType: mutationType,
      fromName: fromName,
      toName: toName
    })) {
      var relationName = (0, _utils.getRelationName)(relationFieldDirective);
      var direction = (0, _utils.getRelationDirection)(relationFieldDirective);
      // possibly swap directions to fit assertion of fromName = typeName
      if (direction === 'IN' || direction === 'in') {
        var temp = fromName;
        fromName = toName;
        toName = temp;
      }
      // (Mutation API) add relation mutation to Mutation
      typeMap = possiblyAddRelationMutationField(typeName, capitalizedFieldName, fromName, toName, mutationMap, typeMap, relationName, relatedAstNode, false);
    }
    return typeMap;
  };
  
  var possiblyAddTypeFieldArguments = function possiblyAddTypeFieldArguments(astNode, typeMap, config, queryType) {
    var fields = astNode.fields;
    var relationTypeName = '';
    var relationType = {};
    var args = [];
    fields.forEach(function (field) {
      relationTypeName = (0, _utils.getNamedType)(field).name.value;
      relationType = typeMap[relationTypeName];
      if (
      // only adds args if node payload type has not been excluded
      shouldAugmentType({
        config: config,
        operationType: queryType,
        type: relationTypeName
      }) &&
      // we know astNode is a node type, so this field should be a node type
      // as well, since the generated args are only for node type lists
      (0, _utils.isNodeType)(relationType) &&
      // the args (first / offset / orderBy) are only generated for list fields
      (0, _utils.isListType)(field) && ((0, _utils.getFieldDirective)(field, 'relation') || (0, _utils.getFieldDirective)(field, 'cypher'))) {
        args = field.arguments;
        field.arguments = possiblyAddArgument(args, 'first', 'Int');
        field.arguments = possiblyAddArgument(args, 'offset', 'Int');
        field.arguments = possiblyAddArgument(args, 'orderBy', '_' + relationTypeName + 'Ordering');
      }
    });
    return fields;
  };
  
  var possiblyAddObjectType = function possiblyAddObjectType(typeMap, name) {
    if (typeMap[name] === undefined) {
      typeMap[name] = {
        kind: 'ObjectTypeDefinition',
        name: {
          kind: 'Name',
          value: name
        },
        interfaces: [],
        directives: [],
        fields: []
      };
    }
    return typeMap;
  };
  
  var decideFieldType = function decideFieldType(name) {
    if ((0, _utils.isTemporalType)(name)) {
      name = name + 'Input';
    }
    return name;
  };
  
  var createOrderingFields = function createOrderingFields(fields, typeMap) {
    var type = {};
    return fields.reduce(function (acc, t) {
      type = (0, _utils.getNamedType)(t);
      if ((0, _utils.isBasicScalar)(type.name.value)) {
        acc.push({
          kind: 'EnumValueDefinition',
          name: {
            kind: 'Name',
            value: t.name.value + '_asc'
          },
          directives: []
        });
        acc.push({
          kind: 'EnumValueDefinition',
          name: {
            kind: 'Name',
            value: t.name.value + '_desc'
          },
          directives: []
        });
      }
      return acc;
    }, []);
  };
  
  var transformManagedFieldTypes = function transformManagedFieldTypes(type) {
    if (type.kind !== 'NamedType') {
      type.type = transformManagedFieldTypes(type.type);
      return type;
    }
    if (type.kind === 'NamedType') {
      var name = type.name.value;
      if ((0, _utils.isTemporalType)(name)) {
        type.name.value = name + 'Input';
      }
    }
    return type;
  };
  
  var buildAllFieldArguments = function buildAllFieldArguments(namePrefix, astNode, typeMap) {
    var fields = [];
    var type = {};
    var fieldName = '';
    var valueTypeName = '';
    var valueType = {};
    switch (namePrefix) {
      case 'Create':
        {
          var firstIdField = undefined;
          var _field2 = {};
          astNode.fields.reduce(function (acc, t) {
            type = (0, _utils.getNamedType)(t);
            fieldName = t.name.value;
            valueTypeName = type.name.value;
            valueType = typeMap[valueTypeName];
            if ((0, _utils.isTemporalType)(valueTypeName) || fieldName !== '_id' && !(0, _utils.getFieldDirective)(t, 'cypher') && ((0, _utils.isBasicScalar)(valueTypeName) || (0, _utils.isKind)(valueType, 'EnumTypeDefinition') || (0, _utils.isKind)(valueType, 'ScalarTypeDefinition'))) {
              var isNonNullable = (0, _utils.isNonNullType)(t);
              _field2 = (0, _cloneDeep2.default)(t);
              if (isNonNullable) {
                var isList = (0, _utils.isListType)(t);
                // Don't require the first ID field discovered
                if (!isList && valueTypeName === 'ID' && !firstIdField) {
                  // will only be true once, this field will
                  // by default recieve an auto-generated uuid,
                  // if no value is provided
                  firstIdField = t;
                  var idField = {
                    kind: 'InputValueDefinition',
                    name: {
                      kind: 'Name',
                      value: fieldName
                    },
                    type: {
                      kind: 'NamedType',
                      name: {
                        kind: 'Name',
                        value: decideFieldType(valueTypeName)
                      }
                    }
                  };
                  acc.push(idField);
                } else {
                  _field2.kind = 'InputValueDefinition';
                  _field2.type = transformManagedFieldTypes(_field2.type);
                  acc.push(_field2);
                }
              } else {
                _field2.kind = 'InputValueDefinition';
                _field2.type = transformManagedFieldTypes(_field2.type);
                acc.push(_field2);
              }
            }
            return acc;
          }, fields);
          break;
        }
      case 'Update':
        {
          var primaryKey = (0, _utils.getPrimaryKey)(astNode);
          var augmentedFields = [];
          if (primaryKey) {
            // Primary key field is first field and required
            var primaryKeyName = primaryKey.name.value;
            var primaryKeyType = (0, _utils.getNamedType)(primaryKey);
            var parsedPrimaryKeyField = (0, _utils.parseFieldSdl)('\n          ' + primaryKeyName + ': ' + decideFieldType(primaryKeyType.name.value) + '!\n        ');
            parsedPrimaryKeyField.kind = 'InputValueDefinition';
            augmentedFields.push(parsedPrimaryKeyField);
            var _field3 = {};
            astNode.fields.reduce(function (acc, t) {
              type = (0, _utils.getNamedType)(t);
              fieldName = t.name.value;
              valueTypeName = type.name.value;
              valueType = typeMap[valueTypeName];
              _field3 = (0, _cloneDeep2.default)(t);
              if (fieldName !== primaryKeyName && fieldName !== '_id' && !(0, _utils.getFieldDirective)(t, 'cypher') && ((0, _utils.isBasicScalar)(valueTypeName) || (0, _utils.isKind)(valueType, 'EnumTypeDefinition') || (0, _utils.isKind)(valueType, 'ScalarTypeDefinition')) || (0, _utils.isTemporalType)(valueTypeName)) {
                if ((0, _utils.isNonNullType)(_field3)) {
                  // Don't require update fields, that wouldn't be very flexible
                  _field3.type = _field3.type.type;
                }
                _field3.kind = 'InputValueDefinition';
                _field3.type = transformManagedFieldTypes(_field3.type);
                acc.push(_field3);
              }
              return acc;
            }, augmentedFields);
            // Use if there is at least one field other than
            // the primaryKey field used for node selection
            if (augmentedFields.length > 1) {
              fields = augmentedFields;
            }
          }
          break;
        }
      case 'Delete':
        {
          var _primaryKey = (0, _utils.getPrimaryKey)(astNode);
          var _primaryKeyName = _primaryKey.name.value;
          var _primaryKeyType = (0, _utils.getNamedType)(_primaryKey);
          fields.push({
            kind: 'InputValueDefinition',
            name: {
              kind: 'Name',
              value: _primaryKeyName
            },
            type: {
              kind: 'NonNullType',
              type: {
                kind: 'NamedType',
                name: {
                  kind: 'Name',
                  value: decideFieldType(_primaryKeyType.name.value)
                }
              }
            }
          });
          break;
        }
    }
    return fields;
  };
  
  var possiblyAddTypeMutation = function possiblyAddTypeMutation(namePrefix, astNode, typeMap, mutationMap) {
    var typeName = astNode.name.value;
    var mutationName = namePrefix + typeName;
    // Only generate if the mutation named mutationName does not already exist
    if (mutationMap[mutationName] === undefined) {
      var args = buildAllFieldArguments(namePrefix, astNode, typeMap);
      if (args.length > 0) {
        typeMap.Mutation.fields.push({
          kind: 'FieldDefinition',
          name: {
            kind: 'Name',
            value: mutationName
          },
          arguments: args,
          type: {
            kind: 'NamedType',
            name: {
              kind: 'Name',
              value: typeName
            }
          },
          directives: []
        });
      }
    }
    return typeMap;
  };
  
  var replaceRelationTypeValue = function replaceRelationTypeValue(fromName, toName, field, capitalizedFieldName, typeName) {
    var isList = (0, _utils.isListType)(field);
    var type = {
      kind: 'NamedType',
      name: {
        kind: 'Name',
        value: '_' + typeName + capitalizedFieldName + (fromName === toName ? 'Directions' : '')
      }
    };
    if (isList && fromName !== toName) {
      type = {
        kind: 'ListType',
        type: type
      };
    }
    field.type = type;
    return field;
  };
  
  var possiblyAddRelationTypeFieldPayload = function possiblyAddRelationTypeFieldPayload(relationAstNode, capitalizedFieldName, typeName, typeMap, field) {
    var fieldTypeName = '_' + typeName + capitalizedFieldName;
    if (!typeMap[fieldTypeName]) {
      var fieldName = '';
      var fieldValueName = '';
      var fromField = {};
      var toField = {};
      var _fromField = {};
      var _toField = {};
      var fromValue = undefined;
      var toValue = undefined;
      var fields = relationAstNode.fields;
      var relationTypeDirective = (0, _utils.getRelationTypeDirectiveArgs)(relationAstNode);
      if (relationTypeDirective) {
        var relationPropertyFields = fields.reduce(function (acc, t) {
          fieldValueName = (0, _utils.getNamedType)(t).name.value;
          fieldName = t.name.value;
          if (fieldName === 'from') {
            fromValue = fieldValueName;
            fromField = t;
          } else if (fieldName === 'to') {
            toValue = fieldValueName;
            toField = t;
          } else {
            // Exclude .to and .from, but gather them from along the way
            // using previous branches above
            acc.push((0, _graphql.print)(t));
          }
          return acc;
        }, []).join('\n');
        if (fromValue && fromValue === toValue) {
          // If field is a list type, then make .from and .to list types
          var fieldIsList = (0, _utils.isListType)(field);
  
          typeMap[fieldTypeName + 'Directions'] = (0, _graphql.parse)('\n            type ' + fieldTypeName + 'Directions ' + (0, _graphql.print)(relationAstNode.directives) + ' {\n              from' + (0, _utils.getFieldArgumentsFromAst)(field, typeName) + ': ' + (fieldIsList ? '[' : '') + fieldTypeName + (fieldIsList ? ']' : '') + '\n              to' + (0, _utils.getFieldArgumentsFromAst)(field, typeName) + ': ' + (fieldIsList ? '[' : '') + fieldTypeName + (fieldIsList ? ']' : '') + '\n            }');
  
          typeMap[fieldTypeName] = (0, _graphql.parse)('\n            type ' + fieldTypeName + ' ' + (0, _graphql.print)(relationAstNode.directives) + ' {\n              ' + relationPropertyFields + '\n              ' + fromValue + ': ' + fromValue + '\n            }\n          ');
  
          // remove arguments on field
          field.arguments = [];
        } else {
          // Non-reflexive case, (User)-[RATED]->(Movie)
          typeMap[fieldTypeName] = (0, _graphql.parse)('\n            type ' + fieldTypeName + ' ' + (0, _graphql.print)(relationAstNode.directives) + ' {\n              ' + relationPropertyFields + '\n              ' + (typeName === toValue ? // If this is the from, the allow selecting the to
          fromValue + ': ' + fromValue : // else this is the to, so allow selecting the from
          typeName === fromValue ? toValue + ': ' + toValue : '') + '\n              }\n          ');
        }
      }
    }
    return typeMap;
  };
  
  var addOrReplaceNodeIdField = function addOrReplaceNodeIdField(astNode) {
    var fields = astNode ? astNode.fields : [];
    var index = fields.findIndex(function (e) {
      return e.name.value === '_id';
    });
    var definition = {
      kind: 'FieldDefinition',
      name: {
        kind: 'Name',
        value: '_id'
      },
      arguments: [],
      type: {
        kind: 'NamedType',
        name: {
          kind: 'Name',
          value: 'String'
        }
      },
      directives: []
    };
    // If it has already been provided, replace it to force valueType,
    // else add it as the last field
    index >= 0 ? fields.splice(index, 1, definition) : fields.push(definition);
    return fields;
  };
  
  var possiblyAddRelationMutationField = function possiblyAddRelationMutationField(typeName, capitalizedFieldName, fromName, toName, mutationMap, typeMap, relationName, relatedAstNode, relationHasProps) {
    var mutationTypes = ['Add', 'Remove'];
    var mutationName = '';
    var payloadTypeName = '';
    var hasSomePropertyField = false;
    mutationTypes.forEach(function (action) {
      mutationName = '' + action + typeName + capitalizedFieldName;
      // Prevents overwriting
      if (mutationMap[mutationName] === undefined) {
        payloadTypeName = '_' + mutationName + 'Payload';
        hasSomePropertyField = relatedAstNode.fields.find(function (e) {
          return e.name.value !== 'from' && e.name.value !== 'to';
        });
        // If we know we should expect data properties (from context: relationHasProps)
        // and if there is at least 1 field that is not .to or .from (hasSomePropertyField)
        // and if we are generating the add relation mutation, then add the .data argument
        var shouldUseRelationDataArgument = relationHasProps && hasSomePropertyField && action === 'Add';
        // Relation mutation type
        typeMap.Mutation.fields.push((0, _utils.parseFieldSdl)('\n        ' + mutationName + '(from: _' + fromName + 'Input!, to: _' + toName + 'Input!' + (shouldUseRelationDataArgument ? ', data: _' + relatedAstNode.name.value + 'Input!' : '') + '): ' + payloadTypeName + ' @MutationMeta(relationship: "' + relationName + '", from: "' + fromName + '", to: "' + toName + '")\n      '));
        // Prevents overwriting
        if (typeMap[payloadTypeName] === undefined) {
          typeMap[payloadTypeName] = (0, _graphql.parse)('\n          type ' + payloadTypeName + ' @relation(name: "' + relationName + '", from: "' + fromName + '", to: "' + toName + '") {\n            from: ' + fromName + '\n            to: ' + toName + '\n            ' + (shouldUseRelationDataArgument ? (0, _utils.getRelationMutationPayloadFieldsFromAst)(relatedAstNode) : '') + '\n          }\n        ');
        }
      }
    });
    return typeMap;
  };
  
  var capitalizeName = function capitalizeName(name) {
    return name.charAt(0).toUpperCase() + name.substr(1);
  };
  
  var createQueryArguments = function createQueryArguments(astNode, typeMap) {
    var type = {};
    var valueTypeName = '';
    astNode.fields = addOrReplaceNodeIdField(astNode);
    return astNode.fields.reduce(function (acc, t) {
      type = (0, _utils.getNamedType)(t);
      valueTypeName = type.name.value;
      if (isQueryArgumentFieldType(type, typeMap[valueTypeName])) {
        acc.push({
          kind: 'InputValueDefinition',
          name: {
            kind: 'Name',
            value: t.name.value
          },
          type: type,
          directives: []
        });
      } else if ((0, _utils.isTemporalType)(valueTypeName)) {
        acc.push({
          kind: 'InputValueDefinition',
          name: {
            kind: 'Name',
            value: t.name.value
          },
          type: {
            kind: 'NamedType',
            name: {
              kind: 'Name',
              value: valueTypeName + 'Input'
            }
          },
          directives: []
        });
      }
      return acc;
    }, []);
  };
  
  var isQueryArgumentFieldType = function isQueryArgumentFieldType(type, valueType) {
    return (0, _utils.isBasicScalar)(type.name.value) || (0, _utils.isKind)(valueType, 'EnumTypeDefinition') || (0, _utils.isKind)(valueType, 'ScalarTypeDefinition');
  };
  
  var hasNonExcludedNodeType = function hasNonExcludedNodeType(types, typeMap, operationType, config) {
    return types.find(function (e) {
      return (0, _utils.isNodeType)(typeMap[e]) && shouldAugmentType({
        config: config,
        operationType: operationType,
        type: typeMap[e].name.value
      });
    });
  };
  
  var initializeOperationTypes = function initializeOperationTypes(_ref7) {
    var types = _ref7.types,
        typeMap = _ref7.typeMap,
        config = _ref7.config,
        queryType = _ref7.queryType,
        mutationType = _ref7.mutationType;
  
    if (hasNonExcludedNodeType(types, typeMap, queryType, config)) {
      typeMap = possiblyAddObjectType(typeMap, queryType);
    }
    if (hasNonExcludedNodeType(types, typeMap, mutationType, config)) {
      typeMap = possiblyAddObjectType(typeMap, mutationType);
    }
    return typeMap;
  };
  
  var computeRelationTypeDirectiveDefaults = function computeRelationTypeDirectiveDefaults(typeMap) {
    var astNode = {};
    var fields = [];
    var name = '';
    var to = {};
    var from = {};
    var fromTypeName = '';
    var toTypeName = '';
    var fromAstNode = {};
    var toAstNode = '';
    var typeDirective = {};
    var relationName = '';
    var toName = '';
    var fromName = '';
    var typeDirectiveIndex = -1;
    (0, _keys2.default)(typeMap).forEach(function (typeName) {
      astNode = typeMap[typeName];
      name = astNode.name.value;
      fields = astNode.fields;
      to = fields ? fields.find(function (e) {
        return e.name.value === 'to';
      }) : undefined;
      from = fields ? fields.find(function (e) {
        return e.name.value === 'from';
      }) : undefined;
      if (to && !from) throw new Error('Relationship type ' + name + ' has a \'to\' field but no corresponding \'from\' field');
      if (from && !to) throw new Error('Relationship type ' + name + ' has a \'from\' field but no corresponding \'to\' field');
      if (from && to) {
        // get values of .to and .from fields
        fromTypeName = (0, _utils.getNamedType)(from).name.value;
        toTypeName = (0, _utils.getNamedType)(to).name.value;
        // get the astNodes of those object values
        fromAstNode = typeMap[fromTypeName];
        toAstNode = typeMap[toTypeName];
        // assume the default relationship name
        relationName = transformRelationName(astNode);
        // get its relation type directive
        typeDirectiveIndex = astNode.directives.findIndex(function (e) {
          return e.name.value === 'relation';
        });
        if (typeDirectiveIndex >= 0) {
          typeDirective = astNode.directives[typeDirectiveIndex];
          // get the arguments of type directive
          var args = typeDirective ? typeDirective.arguments : [];
          if (args.length > 0) {
            // get its name argument
            var nameArg = args.find(function (e) {
              return e.name.value === 'name';
            });
            if (nameArg) {
              relationName = nameArg.value.value;
            }
          }
          // replace it if it exists in order to force correct configuration
          astNode.directives[typeDirectiveIndex] = {
            kind: 'Directive',
            name: {
              kind: 'Name',
              value: 'relation'
            },
            arguments: [{
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'name'
              },
              value: {
                kind: 'StringValue',
                value: relationName
              }
            }, {
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'from'
              },
              value: {
                kind: 'StringValue',
                value: fromTypeName
              }
            }, {
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'to'
              },
              value: {
                kind: 'StringValue',
                value: toTypeName
              }
            }]
          };
        } else {
          astNode.directives.push({
            kind: 'Directive',
            name: {
              kind: 'Name',
              value: 'relation'
            },
            arguments: [{
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'name'
              },
              value: {
                kind: 'StringValue',
                value: relationName
              }
            }, {
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'from'
              },
              value: {
                kind: 'StringValue',
                value: fromTypeName
              }
            }, {
              kind: 'Argument',
              name: {
                kind: 'Name',
                value: 'to'
              },
              value: {
                kind: 'StringValue',
                value: toTypeName
              }
            }]
          });
        }
        typeMap[typeName] = astNode;
      }
    });
    return typeMap;
  };
  
  var transformRelationName = function transformRelationName(relatedAstNode) {
    var name = relatedAstNode.name.value;
    var char = '';
    var uppercased = '';
    return (0, _keys2.default)(name).reduce(function (acc, t) {
      char = name.charAt(t);
      uppercased = char.toUpperCase();
      if (char === uppercased && t > 0) {
        // already uppercased
        acc.push('_' + uppercased);
      } else {
        acc.push(uppercased);
      }
      return acc;
    }, []).join('');
  };
  
  var shouldAugmentType = function shouldAugmentType(_ref8) {
    var _ref8$config = _ref8.config,
        config = _ref8$config === undefined ? {} : _ref8$config,
        _ref8$operationType = _ref8.operationType,
        operationType = _ref8$operationType === undefined ? '' : _ref8$operationType,
        type = _ref8.type;
  
    operationType = (0, _utils.lowFirstLetter)(operationType);
    var typeValue = config[operationType];
    var configType = typeof typeValue === 'undefined' ? 'undefined' : (0, _typeof3.default)(typeValue);
    if (configType === 'boolean') {
      return config[operationType];
    } else if (configType === 'object') {
      var excludes = typeValue.exclude;
      if (Array.isArray(excludes)) {
        if (type) {
          return !excludes.includes(type);
        }
      }
    }
    return true;
  };
  
  var temporalTypes = function temporalTypes(typeMap, types) {
    if (types.time === true) {
      typeMap['_Neo4jTime'] = (0, _graphql.parse)('\n      type _Neo4jTime {\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        timezone: String\n        formatted: String\n      }\n    ').definitions[0];
      typeMap['_Neo4jTimeInput'] = (0, _graphql.parse)('\n      input _Neo4jTimeInput {\n        hour: Int\n        minute: Int\n        second: Int\n        nanosecond: Int\n        millisecond: Int\n        microsecond: Int\n        timezone: String\n        formatted: String\n      }\n    ').definitions[0];
    }
    if (types.date === true) {
      typeMap['_Neo4jDate'] = (0, _graphql.parse)('\n      type _Neo4jDate {\n        year: Int\n        month: Int\n        day: Int\n        formatted: String\n      }\n    ').definitions[0];
      typeMap['_Neo4jDateInput'] = (0, _graphql.parse)('\n      input _Neo4jDateInput {\n        year: Int\n        month: Int\n        day: Int\n        formatted: String\n      }\n    ').definitions[0];
    }
    if (types.datetime === true) {
      typeMap['_Neo4jDateTime'] = (0, _graphql.parse)('\n      type _Neo4jDateTime {\n        year: Int\n        month: Int\n        day: Int\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        timezone: String\n        formatted: String\n      }\n    ').definitions[0];
      typeMap['_Neo4jDateTimeInput'] = (0, _graphql.parse)('\n      input _Neo4jDateTimeInput {\n        year: Int\n        month: Int\n        day: Int\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        timezone: String \n        formatted: String\n      }\n    ').definitions[0];
    }
    if (types.localtime === true) {
      typeMap['_Neo4jLocalTime'] = (0, _graphql.parse)('\n      type _Neo4jLocalTime {\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        formatted: String\n      }\n    ').definitions[0];
      typeMap['_Neo4jLocalTimeInput'] = (0, _graphql.parse)('\n      input _Neo4jLocalTimeInput {\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        formatted: String\n      }\n    ').definitions[0];
    }
    if (types.localdatetime === true) {
      typeMap['_Neo4jLocalDateTime'] = (0, _graphql.parse)('\n      type _Neo4jLocalDateTime {\n        year: Int\n        month: Int\n        day: Int\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        formatted: String\n      }\n    ').definitions[0];
      typeMap['_Neo4jLocalDateTimeInput'] = (0, _graphql.parse)('\n      input _Neo4jLocalDateTimeInput {\n        year: Int\n        month: Int\n        day: Int\n        hour: Int\n        minute: Int\n        second: Int\n        millisecond: Int\n        microsecond: Int\n        nanosecond: Int\n        formatted: String\n      }\n    ').definitions[0];
    }
    return typeMap;
  };
  
  var transformTemporalFieldArgs = function transformTemporalFieldArgs(field, config) {
    field.arguments.forEach(function (arg) {
      arg.type = transformTemporalTypeName(arg.type, config, true);
    });
    return field;
  };
  
  var transformTemporalFields = function transformTemporalFields(typeMap, config) {
    var astNode = {};
    (0, _keys2.default)(typeMap).forEach(function (t) {
      astNode = typeMap[t];
      if (astNode &&
      // must be graphql object type
      astNode.kind === 'ObjectTypeDefinition'
      // does not have relation type directive
      // getTypeDirective(astNode, 'relation') === undefined &&
      // does not have from and to fields; not relation type
      // astNode.fields &&
      // astNode.fields.find(e => e.name.value === 'from') === undefined &&
      // astNode.fields.find(e => e.name.value === 'to') === undefined
      ) {
          if (!(0, _utils.isTemporalType)(t)) {
            astNode.fields.forEach(function (field) {
              // released: DateTime -> released: _Neo4jDateTime
              field.type = transformTemporalTypeName(field.type, config);
              field = transformTemporalFieldArgs(field, config);
            });
          }
        }
    });
    return typeMap;
  };
  
  var transformTemporalTypeName = function transformTemporalTypeName(type, config, isArgument) {
    if (type.kind !== 'NamedType') {
      type.type = transformTemporalTypeName(type.type, config);
      return type;
    }
    if (type.kind === 'NamedType') {
      switch (type.name.value) {
        case 'Time':
          {
            if (config.time === true) {
              type.name.value = '_Neo4jTime' + (isArgument ? 'Input' : '');
            }
            break;
          }
        case 'Date':
          {
            if (config.date === true) {
              type.name.value = '_Neo4jDate' + (isArgument ? 'Input' : '');
            }
            break;
          }
        case 'DateTime':
          {
            if (config.datetime === true) {
              type.name.value = '_Neo4jDateTime' + (isArgument ? 'Input' : '');
            }
            break;
          }
        case 'LocalTime':
          {
            if (config.localtime === true) {
              type.name.value = '_Neo4jLocalTime' + (isArgument ? 'Input' : '');
            }
            break;
          }
        case 'LocalDateTime':
          {
            if (config.localdatetime === true) {
              type.name.value = '_Neo4jLocalDateTime' + (isArgument ? 'Input' : '');
            }
            break;
          }
        default:
          break;
      }
    }
    return type;
  };
  
  var decideTemporalConfig = function decideTemporalConfig(config) {
    var defaultConfig = {
      time: true,
      date: true,
      datetime: true,
      localtime: true,
      localdatetime: true
    };
    var providedConfig = config ? config.temporal : defaultConfig;
    if (typeof providedConfig === 'boolean') {
      if (providedConfig === false) {
        defaultConfig.time = false;
        defaultConfig.date = false;
        defaultConfig.datetime = false;
        defaultConfig.localtime = false;
        defaultConfig.localdatetime = false;
      }
    } else if ((typeof providedConfig === 'undefined' ? 'undefined' : (0, _typeof3.default)(providedConfig)) === 'object') {
      (0, _keys2.default)(defaultConfig).forEach(function (e) {
        if (providedConfig[e] === undefined) {
          providedConfig[e] = defaultConfig[e];
        }
      });
      defaultConfig = providedConfig;
    }
    return defaultConfig;
  };
  
  var addTemporalTypes = exports.addTemporalTypes = function addTemporalTypes(typeMap, config) {
    config = decideTemporalConfig(config);
    typeMap = temporalTypes(typeMap, config);
    return transformTemporalFields(typeMap, config);
  };