import { __values, __read, __spreadArray, __assign, __awaiter, __generator } from 'tslib';
import { mergeDeep, isSome, compareNodes, isNotEqual, getDocumentNodeFromSchema, isDocumentNode, getResolversFromSchema, asArray } from '@graphql-tools/utils/es5';
import { getDescription, visit, print, Source, Kind, isSchema, parse, isDefinitionNode, isScalarType, isSpecifiedScalarType, isIntrospectionType, isObjectType, isInterfaceType, isInputObjectType, isUnionType, isEnumType, buildSchema, buildASTSchema } from 'graphql';
import { addResolversToSchema } from '@graphql-tools/schema/es5';

/**
 * Deep merges multiple resolver definition objects into a single definition.
 * @param resolversDefinitions Resolver definitions to be merged
 * @param options Additional options
 *
 * ```js
 * const { mergeResolvers } = require('@graphql-tools/merge/es5');
 * const clientResolver = require('./clientResolver');
 * const productResolver = require('./productResolver');
 *
 * const resolvers = mergeResolvers([
 *  clientResolver,
 *  productResolver,
 * ]);
 * ```
 *
 * If you don't want to manually create the array of resolver objects, you can
 * also use this function along with loadFiles:
 *
 * ```js
 * const path = require('path');
 * const { mergeResolvers } = require('@graphql-tools/merge/es5');
 * const { loadFilesSync } = require('@graphql-tools/load-files/es5');
 *
 * const resolversArray = loadFilesSync(path.join(__dirname, './resolvers'));
 *
 * const resolvers = mergeResolvers(resolversArray)
 * ```
 */
function mergeResolvers(resolversDefinitions, options) {
    var e_1, _a, e_2, _b;
    if (!resolversDefinitions || (Array.isArray(resolversDefinitions) && resolversDefinitions.length === 0)) {
        return {};
    }
    if (!Array.isArray(resolversDefinitions)) {
        return resolversDefinitions;
    }
    if (resolversDefinitions.length === 1) {
        return resolversDefinitions[0] || {};
    }
    var resolvers = new Array();
    try {
        for (var resolversDefinitions_1 = __values(resolversDefinitions), resolversDefinitions_1_1 = resolversDefinitions_1.next(); !resolversDefinitions_1_1.done; resolversDefinitions_1_1 = resolversDefinitions_1.next()) {
            var resolversDefinition = resolversDefinitions_1_1.value;
            if (Array.isArray(resolversDefinition)) {
                resolversDefinition = mergeResolvers(resolversDefinition);
            }
            if (typeof resolversDefinition === 'object' && resolversDefinition) {
                resolvers.push(resolversDefinition);
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (resolversDefinitions_1_1 && !resolversDefinitions_1_1.done && (_a = resolversDefinitions_1.return)) _a.call(resolversDefinitions_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    var result = mergeDeep(resolvers, true);
    if (options === null || options === void 0 ? void 0 : options.exclusions) {
        try {
            for (var _c = __values(options.exclusions), _d = _c.next(); !_d.done; _d = _c.next()) {
                var exclusion = _d.value;
                var _e = __read(exclusion.split('.'), 2), typeName = _e[0], fieldName = _e[1];
                if (!fieldName || fieldName === '*') {
                    delete result[typeName];
                }
                else if (result[typeName]) {
                    delete result[typeName][fieldName];
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
            }
            finally { if (e_2) throw e_2.error; }
        }
    }
    return result;
}

function mergeArguments(args1, args2, config) {
    var result = deduplicateArguments(__spreadArray(__spreadArray([], __read(args2)), __read(args1)).filter(isSome));
    if (config && config.sort) {
        result.sort(compareNodes);
    }
    return result;
}
function deduplicateArguments(args) {
    return args.reduce(function (acc, current) {
        var dup = acc.find(function (arg) { return arg.name.value === current.name.value; });
        if (!dup) {
            return acc.concat([current]);
        }
        return acc;
    }, []);
}

var MAX_LINE_LENGTH = 80;
var commentsRegistry = {};
function resetComments() {
    commentsRegistry = {};
}
function collectComment(node) {
    var e_1, _a, e_2, _b, e_3, _c;
    var _d;
    var entityName = (_d = node.name) === null || _d === void 0 ? void 0 : _d.value;
    if (entityName == null) {
        return;
    }
    pushComment(node, entityName);
    switch (node.kind) {
        case 'EnumTypeDefinition':
            if (node.values) {
                try {
                    for (var _e = __values(node.values), _f = _e.next(); !_f.done; _f = _e.next()) {
                        var value = _f.value;
                        pushComment(value, entityName, value.name.value);
                    }
                }
                catch (e_1_1) { e_1 = { error: e_1_1 }; }
                finally {
                    try {
                        if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
                    }
                    finally { if (e_1) throw e_1.error; }
                }
            }
            break;
        case 'ObjectTypeDefinition':
        case 'InputObjectTypeDefinition':
        case 'InterfaceTypeDefinition':
            if (node.fields) {
                try {
                    for (var _g = __values(node.fields), _h = _g.next(); !_h.done; _h = _g.next()) {
                        var field = _h.value;
                        pushComment(field, entityName, field.name.value);
                        if (isFieldDefinitionNode(field) && field.arguments) {
                            try {
                                for (var _j = (e_3 = void 0, __values(field.arguments)), _k = _j.next(); !_k.done; _k = _j.next()) {
                                    var arg = _k.value;
                                    pushComment(arg, entityName, field.name.value, arg.name.value);
                                }
                            }
                            catch (e_3_1) { e_3 = { error: e_3_1 }; }
                            finally {
                                try {
                                    if (_k && !_k.done && (_c = _j.return)) _c.call(_j);
                                }
                                finally { if (e_3) throw e_3.error; }
                            }
                        }
                    }
                }
                catch (e_2_1) { e_2 = { error: e_2_1 }; }
                finally {
                    try {
                        if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
                    }
                    finally { if (e_2) throw e_2.error; }
                }
            }
            break;
    }
}
function pushComment(node, entity, field, argument) {
    var comment = getDescription(node, { commentDescriptions: true });
    if (typeof comment !== 'string' || comment.length === 0) {
        return;
    }
    var keys = [entity];
    if (field) {
        keys.push(field);
        if (argument) {
            keys.push(argument);
        }
    }
    var path = keys.join('.');
    if (!commentsRegistry[path]) {
        commentsRegistry[path] = [];
    }
    commentsRegistry[path].push(comment);
}
function printComment(comment) {
    return '\n# ' + comment.replace(/\n/g, '\n# ');
}
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
/**
 * NOTE: ==> This file has been modified just to add comments to the printed AST
 * This is a temp measure, we will move to using the original non modified printer.js ASAP.
 */
// import { visit, VisitFn } from 'graphql';
/**
 * Given maybeArray, print an empty string if it is null or empty, otherwise
 * print all items together separated by separator if provided
 */
function join(maybeArray, separator) {
    return maybeArray ? maybeArray.filter(function (x) { return x; }).join(separator || '') : '';
}
function hasMultilineItems(maybeArray) {
    var _a;
    return (_a = maybeArray === null || maybeArray === void 0 ? void 0 : maybeArray.some(function (str) { return str.includes('\n'); })) !== null && _a !== void 0 ? _a : false;
}
function addDescription(cb) {
    return function (node, _key, _parent, path, ancestors) {
        var _a;
        var keys = [];
        var parent = path.reduce(function (prev, key) {
            if (['fields', 'arguments', 'values'].includes(key) && prev.name) {
                keys.push(prev.name.value);
            }
            return prev[key];
        }, ancestors[0]);
        var key = __spreadArray(__spreadArray([], __read(keys)), [(_a = parent === null || parent === void 0 ? void 0 : parent.name) === null || _a === void 0 ? void 0 : _a.value]).filter(Boolean).join('.');
        var items = [];
        if (node.kind.includes('Definition') && commentsRegistry[key]) {
            items.push.apply(items, __spreadArray([], __read(commentsRegistry[key])));
        }
        return join(__spreadArray(__spreadArray([], __read(items.map(printComment))), [node.description, cb(node)]), '\n');
    };
}
function indent(maybeString) {
    return maybeString && "  " + maybeString.replace(/\n/g, '\n  ');
}
/**
 * Given array, print each item on its own line, wrapped in an
 * indented "{ }" block.
 */
function block(array) {
    return array && array.length !== 0 ? "{\n" + indent(join(array, '\n')) + "\n}" : '';
}
/**
 * If maybeString is not null or empty, then wrap with start and end, otherwise
 * print an empty string.
 */
function wrap(start, maybeString, end) {
    return maybeString ? start + maybeString + (end || '') : '';
}
/**
 * Print a block string in the indented block form by adding a leading and
 * trailing blank line. However, if a block string starts with whitespace and is
 * a single-line, adding a leading blank line would strip that whitespace.
 */
function printBlockString(value, isDescription) {
    if (isDescription === void 0) { isDescription = false; }
    var escaped = value.replace(/"""/g, '\\"""');
    return (value[0] === ' ' || value[0] === '\t') && value.indexOf('\n') === -1
        ? "\"\"\"" + escaped.replace(/"$/, '"\n') + "\"\"\""
        : "\"\"\"\n" + (isDescription ? escaped : indent(escaped)) + "\n\"\"\"";
}
var printDocASTReducer = {
    Name: { leave: function (node) { return node.value; } },
    Variable: { leave: function (node) { return '$' + node.name; } },
    // Document
    Document: {
        leave: function (node) { return join(node.definitions, '\n\n'); },
    },
    OperationDefinition: {
        leave: function (node) {
            var varDefs = wrap('(', join(node.variableDefinitions, ', '), ')');
            var prefix = join([node.operation, join([node.name, varDefs]), join(node.directives, ' ')], ' ');
            // the query short form.
            return prefix + ' ' + node.selectionSet;
        },
    },
    VariableDefinition: {
        leave: function (_a) {
            var variable = _a.variable, type = _a.type, defaultValue = _a.defaultValue, directives = _a.directives;
            return variable + ': ' + type + wrap(' = ', defaultValue) + wrap(' ', join(directives, ' '));
        },
    },
    SelectionSet: { leave: function (_a) {
            var selections = _a.selections;
            return block(selections);
        } },
    Field: {
        leave: function (_a) {
            var alias = _a.alias, name = _a.name, args = _a.arguments, directives = _a.directives, selectionSet = _a.selectionSet;
            var prefix = wrap('', alias, ': ') + name;
            var argsLine = prefix + wrap('(', join(args, ', '), ')');
            if (argsLine.length > MAX_LINE_LENGTH) {
                argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)');
            }
            return join([argsLine, join(directives, ' '), selectionSet], ' ');
        },
    },
    Argument: { leave: function (_a) {
            var name = _a.name, value = _a.value;
            return name + ': ' + value;
        } },
    // Fragments
    FragmentSpread: {
        leave: function (_a) {
            var name = _a.name, directives = _a.directives;
            return '...' + name + wrap(' ', join(directives, ' '));
        },
    },
    InlineFragment: {
        leave: function (_a) {
            var typeCondition = _a.typeCondition, directives = _a.directives, selectionSet = _a.selectionSet;
            return join(['...', wrap('on ', typeCondition), join(directives, ' '), selectionSet], ' ');
        },
    },
    FragmentDefinition: {
        leave: function (_a) {
            var name = _a.name, typeCondition = _a.typeCondition, variableDefinitions = _a.variableDefinitions, directives = _a.directives, selectionSet = _a.selectionSet;
            // Note: fragment variable definitions are experimental and may be changed
            // or removed in the future.
            return "fragment " + name + wrap('(', join(variableDefinitions, ', '), ')') + " " +
                ("on " + typeCondition + " " + wrap('', join(directives, ' '), ' ')) +
                selectionSet;
        },
    },
    // Value
    IntValue: { leave: function (_a) {
            var value = _a.value;
            return value;
        } },
    FloatValue: { leave: function (_a) {
            var value = _a.value;
            return value;
        } },
    StringValue: {
        leave: function (_a) {
            var value = _a.value, isBlockString = _a.block;
            return (isBlockString ? printBlockString(value) : JSON.stringify(value));
        },
    },
    BooleanValue: { leave: function (_a) {
            var value = _a.value;
            return (value ? 'true' : 'false');
        } },
    NullValue: { leave: function () { return 'null'; } },
    EnumValue: { leave: function (_a) {
            var value = _a.value;
            return value;
        } },
    ListValue: { leave: function (_a) {
            var values = _a.values;
            return '[' + join(values, ', ') + ']';
        } },
    ObjectValue: { leave: function (_a) {
            var fields = _a.fields;
            return '{' + join(fields, ', ') + '}';
        } },
    ObjectField: { leave: function (_a) {
            var name = _a.name, value = _a.value;
            return name + ': ' + value;
        } },
    // Directive
    Directive: {
        leave: function (_a) {
            var name = _a.name, args = _a.arguments;
            return '@' + name + wrap('(', join(args, ', '), ')');
        },
    },
    // Type
    NamedType: { leave: function (_a) {
            var name = _a.name;
            return name;
        } },
    ListType: { leave: function (_a) {
            var type = _a.type;
            return '[' + type + ']';
        } },
    NonNullType: { leave: function (_a) {
            var type = _a.type;
            return type + '!';
        } },
    // Type System Definitions
    SchemaDefinition: {
        leave: function (_a) {
            var description = _a.description, directives = _a.directives, operationTypes = _a.operationTypes;
            return wrap('', description, '\n') + join(['schema', join(directives, ' '), block(operationTypes)], ' ');
        },
    },
    OperationTypeDefinition: {
        leave: function (_a) {
            var operation = _a.operation, type = _a.type;
            return operation + ': ' + type;
        },
    },
    ScalarTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, directives = _a.directives;
            return wrap('', description, '\n') + join(['scalar', name, join(directives, ' ')], ' ');
        },
    },
    ObjectTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, interfaces = _a.interfaces, directives = _a.directives, fields = _a.fields;
            return wrap('', description, '\n') +
                join(['type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' ');
        },
    },
    FieldDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, args = _a.arguments, type = _a.type, directives = _a.directives;
            return wrap('', description, '\n') +
                name +
                (hasMultilineItems(args)
                    ? wrap('(\n', indent(join(args, '\n')), '\n)')
                    : wrap('(', join(args, ', '), ')')) +
                ': ' +
                type +
                wrap(' ', join(directives, ' '));
        },
    },
    InputValueDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, type = _a.type, defaultValue = _a.defaultValue, directives = _a.directives;
            return wrap('', description, '\n') + join([name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')], ' ');
        },
    },
    InterfaceTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, interfaces = _a.interfaces, directives = _a.directives, fields = _a.fields;
            return wrap('', description, '\n') +
                join(['interface', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' ');
        },
    },
    UnionTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, directives = _a.directives, types = _a.types;
            return wrap('', description, '\n') + join(['union', name, join(directives, ' '), wrap('= ', join(types, ' | '))], ' ');
        },
    },
    EnumTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, directives = _a.directives, values = _a.values;
            return wrap('', description, '\n') + join(['enum', name, join(directives, ' '), block(values)], ' ');
        },
    },
    EnumValueDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, directives = _a.directives;
            return wrap('', description, '\n') + join([name, join(directives, ' ')], ' ');
        },
    },
    InputObjectTypeDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, directives = _a.directives, fields = _a.fields;
            return wrap('', description, '\n') + join(['input', name, join(directives, ' '), block(fields)], ' ');
        },
    },
    DirectiveDefinition: {
        leave: function (_a) {
            var description = _a.description, name = _a.name, args = _a.arguments, repeatable = _a.repeatable, locations = _a.locations;
            return wrap('', description, '\n') +
                'directive @' +
                name +
                (hasMultilineItems(args)
                    ? wrap('(\n', indent(join(args, '\n')), '\n)')
                    : wrap('(', join(args, ', '), ')')) +
                (repeatable ? ' repeatable' : '') +
                ' on ' +
                join(locations, ' | ');
        },
    },
    SchemaExtension: {
        leave: function (_a) {
            var directives = _a.directives, operationTypes = _a.operationTypes;
            return join(['extend schema', join(directives, ' '), block(operationTypes)], ' ');
        },
    },
    ScalarTypeExtension: {
        leave: function (_a) {
            var name = _a.name, directives = _a.directives;
            return join(['extend scalar', name, join(directives, ' ')], ' ');
        },
    },
    ObjectTypeExtension: {
        leave: function (_a) {
            var name = _a.name, interfaces = _a.interfaces, directives = _a.directives, fields = _a.fields;
            return join(['extend type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' ');
        },
    },
    InterfaceTypeExtension: {
        leave: function (_a) {
            var name = _a.name, interfaces = _a.interfaces, directives = _a.directives, fields = _a.fields;
            return join(['extend interface', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' ');
        },
    },
    UnionTypeExtension: {
        leave: function (_a) {
            var name = _a.name, directives = _a.directives, types = _a.types;
            return join(['extend union', name, join(directives, ' '), wrap('= ', join(types, ' | '))], ' ');
        },
    },
    EnumTypeExtension: {
        leave: function (_a) {
            var name = _a.name, directives = _a.directives, values = _a.values;
            return join(['extend enum', name, join(directives, ' '), block(values)], ' ');
        },
    },
    InputObjectTypeExtension: {
        leave: function (_a) {
            var name = _a.name, directives = _a.directives, fields = _a.fields;
            return join(['extend input', name, join(directives, ' '), block(fields)], ' ');
        },
    },
};
var printDocASTReducerWithComments = Object.keys(printDocASTReducer).reduce(function (prev, key) {
    var _a;
    return (__assign(__assign({}, prev), (_a = {}, _a[key] = {
        leave: addDescription(printDocASTReducer[key].leave),
    }, _a)));
}, {});
/**
 * Converts an AST into a string, using one set of reasonable
 * formatting rules.
 */
function printWithComments(ast) {
    return visit(ast, printDocASTReducerWithComments);
}
function isFieldDefinitionNode(node) {
    return node.kind === 'FieldDefinition';
}

function directiveAlreadyExists(directivesArr, otherDirective) {
    return !!directivesArr.find(function (directive) { return directive.name.value === otherDirective.name.value; });
}
function nameAlreadyExists(name, namesArr) {
    return namesArr.some(function (_a) {
        var value = _a.value;
        return value === name.value;
    });
}
function mergeArguments$1(a1, a2) {
    var e_1, _a;
    var result = __spreadArray([], __read(a2));
    var _loop_1 = function (argument) {
        var existingIndex = result.findIndex(function (a) { return a.name.value === argument.name.value; });
        if (existingIndex > -1) {
            var existingArg = result[existingIndex];
            if (existingArg.value.kind === 'ListValue') {
                var source = existingArg.value.values;
                var target = argument.value.values;
                // merge values of two lists
                existingArg.value.values = deduplicateLists(source, target, function (targetVal, source) {
                    var value = targetVal.value;
                    return !value || !source.some(function (sourceVal) { return sourceVal.value === value; });
                });
            }
            else {
                existingArg.value = argument.value;
            }
        }
        else {
            result.push(argument);
        }
    };
    try {
        for (var a1_1 = __values(a1), a1_1_1 = a1_1.next(); !a1_1_1.done; a1_1_1 = a1_1.next()) {
            var argument = a1_1_1.value;
            _loop_1(argument);
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (a1_1_1 && !a1_1_1.done && (_a = a1_1.return)) _a.call(a1_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    return result;
}
function deduplicateDirectives(directives) {
    return directives
        .map(function (directive, i, all) {
        var firstAt = all.findIndex(function (d) { return d.name.value === directive.name.value; });
        if (firstAt !== i) {
            var dup = all[firstAt];
            directive.arguments = mergeArguments$1(directive.arguments, dup.arguments);
            return null;
        }
        return directive;
    })
        .filter(isSome);
}
function mergeDirectives(d1, d2, config) {
    var e_2, _a;
    if (d1 === void 0) { d1 = []; }
    if (d2 === void 0) { d2 = []; }
    var reverseOrder = config && config.reverseDirectives;
    var asNext = reverseOrder ? d1 : d2;
    var asFirst = reverseOrder ? d2 : d1;
    var result = deduplicateDirectives(__spreadArray([], __read(asNext)));
    var _loop_2 = function (directive) {
        if (directiveAlreadyExists(result, directive)) {
            var existingDirectiveIndex = result.findIndex(function (d) { return d.name.value === directive.name.value; });
            var existingDirective = result[existingDirectiveIndex];
            result[existingDirectiveIndex].arguments = mergeArguments$1(directive.arguments || [], existingDirective.arguments || []);
        }
        else {
            result.push(directive);
        }
    };
    try {
        for (var asFirst_1 = __values(asFirst), asFirst_1_1 = asFirst_1.next(); !asFirst_1_1.done; asFirst_1_1 = asFirst_1.next()) {
            var directive = asFirst_1_1.value;
            _loop_2(directive);
        }
    }
    catch (e_2_1) { e_2 = { error: e_2_1 }; }
    finally {
        try {
            if (asFirst_1_1 && !asFirst_1_1.done && (_a = asFirst_1.return)) _a.call(asFirst_1);
        }
        finally { if (e_2) throw e_2.error; }
    }
    return result;
}
function validateInputs(node, existingNode) {
    var printedNode = print(node);
    var printedExistingNode = print(existingNode);
    // eslint-disable-next-line
    var leaveInputs = new RegExp('(directive @w*d*)|( on .*$)', 'g');
    var sameArguments = printedNode.replace(leaveInputs, '') === printedExistingNode.replace(leaveInputs, '');
    if (!sameArguments) {
        throw new Error("Unable to merge GraphQL directive \"" + node.name.value + "\". \nExisting directive:  \n\t" + printedExistingNode + " \nReceived directive: \n\t" + printedNode);
    }
}
function mergeDirective(node, existingNode) {
    if (existingNode) {
        validateInputs(node, existingNode);
        return __assign(__assign({}, node), { locations: __spreadArray(__spreadArray([], __read(existingNode.locations)), __read(node.locations.filter(function (name) { return !nameAlreadyExists(name, existingNode.locations); }))) });
    }
    return node;
}
function deduplicateLists(source, target, filterFn) {
    return source.concat(target.filter(function (val) { return filterFn(val, source); }));
}

function mergeEnumValues(first, second, config) {
    var e_1, _a, e_2, _b;
    if (config === null || config === void 0 ? void 0 : config.consistentEnumMerge) {
        var reversed = [];
        if (first) {
            reversed.push.apply(reversed, __spreadArray([], __read(first)));
        }
        first = second;
        second = reversed;
    }
    var enumValueMap = new Map();
    if (first) {
        try {
            for (var first_1 = __values(first), first_1_1 = first_1.next(); !first_1_1.done; first_1_1 = first_1.next()) {
                var firstValue = first_1_1.value;
                enumValueMap.set(firstValue.name.value, firstValue);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (first_1_1 && !first_1_1.done && (_a = first_1.return)) _a.call(first_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
    }
    if (second) {
        try {
            for (var second_1 = __values(second), second_1_1 = second_1.next(); !second_1_1.done; second_1_1 = second_1.next()) {
                var secondValue = second_1_1.value;
                var enumValue = secondValue.name.value;
                if (enumValueMap.has(enumValue)) {
                    var firstValue = enumValueMap.get(enumValue);
                    firstValue.description = secondValue.description || firstValue.description;
                    firstValue.directives = mergeDirectives(secondValue.directives, firstValue.directives);
                }
                else {
                    enumValueMap.set(enumValue, secondValue);
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (second_1_1 && !second_1_1.done && (_b = second_1.return)) _b.call(second_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
    }
    var result = __spreadArray([], __read(enumValueMap.values()));
    if (config && config.sort) {
        result.sort(compareNodes);
    }
    return result;
}

function mergeEnum(e1, e2, config) {
    if (e2) {
        return {
            name: e1.name,
            description: e1['description'] || e2['description'],
            kind: (config && config.convertExtensions) || e1.kind === 'EnumTypeDefinition' || e2.kind === 'EnumTypeDefinition'
                ? 'EnumTypeDefinition'
                : 'EnumTypeExtension',
            loc: e1.loc,
            directives: mergeDirectives(e1.directives, e2.directives, config),
            values: mergeEnumValues(e1.values, e2.values, config),
        };
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, e1), { kind: 'EnumTypeDefinition' }) : e1;
}

function isStringTypes(types) {
    return typeof types === 'string';
}
function isSourceTypes(types) {
    return types instanceof Source;
}
function extractType(type) {
    var visitedType = type;
    while (visitedType.kind === Kind.LIST_TYPE || visitedType.kind === 'NonNullType') {
        visitedType = visitedType.type;
    }
    return visitedType;
}
function isWrappingTypeNode(type) {
    return type.kind !== Kind.NAMED_TYPE;
}
function isListTypeNode(type) {
    return type.kind === Kind.LIST_TYPE;
}
function isNonNullTypeNode(type) {
    return type.kind === Kind.NON_NULL_TYPE;
}
function printTypeNode(type) {
    if (isListTypeNode(type)) {
        return "[" + printTypeNode(type.type) + "]";
    }
    if (isNonNullTypeNode(type)) {
        return printTypeNode(type.type) + "!";
    }
    return type.name.value;
}
var CompareVal;
(function (CompareVal) {
    CompareVal[CompareVal["A_SMALLER_THAN_B"] = -1] = "A_SMALLER_THAN_B";
    CompareVal[CompareVal["A_EQUALS_B"] = 0] = "A_EQUALS_B";
    CompareVal[CompareVal["A_GREATER_THAN_B"] = 1] = "A_GREATER_THAN_B";
})(CompareVal || (CompareVal = {}));
function defaultStringComparator(a, b) {
    if (a == null && b == null) {
        return CompareVal.A_EQUALS_B;
    }
    if (a == null) {
        return CompareVal.A_SMALLER_THAN_B;
    }
    if (b == null) {
        return CompareVal.A_GREATER_THAN_B;
    }
    if (a < b)
        return CompareVal.A_SMALLER_THAN_B;
    if (a > b)
        return CompareVal.A_GREATER_THAN_B;
    return CompareVal.A_EQUALS_B;
}

function fieldAlreadyExists(fieldsArr, otherField, config) {
    var result = fieldsArr.find(function (field) { return field.name.value === otherField.name.value; });
    if (result && !(config === null || config === void 0 ? void 0 : config.ignoreFieldConflicts)) {
        var t1 = extractType(result.type);
        var t2 = extractType(otherField.type);
        if (t1.name.value !== t2.name.value) {
            throw new Error("Field \"" + otherField.name.value + "\" already defined with a different type. Declared as \"" + t1.name.value + "\", but you tried to override with \"" + t2.name.value + "\"");
        }
    }
    return !!result;
}
function mergeFields(type, f1, f2, config) {
    var e_1, _a;
    var result = [];
    if (f2 != null) {
        result.push.apply(result, __spreadArray([], __read(f2)));
    }
    if (f1 != null) {
        var _loop_1 = function (field) {
            if (fieldAlreadyExists(result, field, config)) {
                var existing = result.find(function (f) { return f.name.value === field.name.value; });
                if (!(config === null || config === void 0 ? void 0 : config.ignoreFieldConflicts)) {
                    if (config === null || config === void 0 ? void 0 : config.throwOnConflict) {
                        preventConflicts(type, existing, field);
                    }
                    else {
                        preventConflicts(type, existing, field);
                    }
                    if (isNonNullTypeNode(field.type) && !isNonNullTypeNode(existing.type)) {
                        existing.type = field.type;
                    }
                }
                existing.arguments = mergeArguments(field['arguments'] || [], existing.arguments || [], config);
                existing.directives = mergeDirectives(field.directives, existing.directives, config);
                existing.description = field.description || existing.description;
            }
            else {
                result.push(field);
            }
        };
        try {
            for (var f1_1 = __values(f1), f1_1_1 = f1_1.next(); !f1_1_1.done; f1_1_1 = f1_1.next()) {
                var field = f1_1_1.value;
                _loop_1(field);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (f1_1_1 && !f1_1_1.done && (_a = f1_1.return)) _a.call(f1_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
    }
    if (config && config.sort) {
        result.sort(compareNodes);
    }
    if (config && config.exclusions) {
        var exclusions_1 = config.exclusions;
        return result.filter(function (field) { return !exclusions_1.includes(type.name.value + "." + field.name.value); });
    }
    return result;
}
function preventConflicts(type, a, b, ignoreNullability) {
    var aType = printTypeNode(a.type);
    var bType = printTypeNode(b.type);
    if (isNotEqual(aType, bType)) {
        if (safeChangeForFieldType(a.type, b.type) === false) {
            throw new Error("Field '" + type.name.value + "." + a.name.value + "' changed type from '" + aType + "' to '" + bType + "'");
        }
    }
}
function safeChangeForFieldType(oldType, newType, ignoreNullability) {
    // both are named
    if (!isWrappingTypeNode(oldType) && !isWrappingTypeNode(newType)) {
        return oldType.toString() === newType.toString();
    }
    // new is non-null
    if (isNonNullTypeNode(newType)) {
        var ofType = isNonNullTypeNode(oldType) ? oldType.type : oldType;
        return safeChangeForFieldType(ofType, newType.type);
    }
    // old is non-null
    if (isNonNullTypeNode(oldType)) {
        return safeChangeForFieldType(newType, oldType);
    }
    // old is list
    if (isListTypeNode(oldType)) {
        return ((isListTypeNode(newType) && safeChangeForFieldType(oldType.type, newType.type)) ||
            (isNonNullTypeNode(newType) && safeChangeForFieldType(oldType, newType['type'])));
    }
    return false;
}

function mergeInputType(node, existingNode, config) {
    if (existingNode) {
        try {
            return {
                name: node.name,
                description: node['description'] || existingNode['description'],
                kind: (config && config.convertExtensions) ||
                    node.kind === 'InputObjectTypeDefinition' ||
                    existingNode.kind === 'InputObjectTypeDefinition'
                    ? 'InputObjectTypeDefinition'
                    : 'InputObjectTypeExtension',
                loc: node.loc,
                fields: mergeFields(node, node.fields, existingNode.fields, config),
                directives: mergeDirectives(node.directives, existingNode.directives, config),
            };
        }
        catch (e) {
            throw new Error("Unable to merge GraphQL input type \"" + node.name.value + "\": " + e.message);
        }
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, node), { kind: 'InputObjectTypeDefinition' }) : node;
}

function mergeInterface(node, existingNode, config) {
    if (existingNode) {
        try {
            return {
                name: node.name,
                description: node['description'] || existingNode['description'],
                kind: (config && config.convertExtensions) ||
                    node.kind === 'InterfaceTypeDefinition' ||
                    existingNode.kind === 'InterfaceTypeDefinition'
                    ? 'InterfaceTypeDefinition'
                    : 'InterfaceTypeExtension',
                loc: node.loc,
                fields: mergeFields(node, node.fields, existingNode.fields, config),
                directives: mergeDirectives(node.directives, existingNode.directives, config),
            };
        }
        catch (e) {
            throw new Error("Unable to merge GraphQL interface \"" + node.name.value + "\": " + e.message);
        }
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, node), { kind: 'InterfaceTypeDefinition' }) : node;
}

function alreadyExists(arr, other) {
    return !!arr.find(function (i) { return i.name.value === other.name.value; });
}
function mergeNamedTypeArray(first, second, config) {
    if (first === void 0) { first = []; }
    if (second === void 0) { second = []; }
    if (config === void 0) { config = {}; }
    var result = __spreadArray(__spreadArray([], __read(second)), __read(first.filter(function (d) { return !alreadyExists(second, d); })));
    if (config && config.sort) {
        result.sort(compareNodes);
    }
    return result;
}

function mergeType(node, existingNode, config) {
    if (existingNode) {
        try {
            return {
                name: node.name,
                description: node['description'] || existingNode['description'],
                kind: (config && config.convertExtensions) ||
                    node.kind === 'ObjectTypeDefinition' ||
                    existingNode.kind === 'ObjectTypeDefinition'
                    ? 'ObjectTypeDefinition'
                    : 'ObjectTypeExtension',
                loc: node.loc,
                fields: mergeFields(node, node.fields, existingNode.fields, config),
                directives: mergeDirectives(node.directives, existingNode.directives, config),
                interfaces: mergeNamedTypeArray(node.interfaces, existingNode.interfaces, config),
            };
        }
        catch (e) {
            throw new Error("Unable to merge GraphQL type \"" + node.name.value + "\": " + e.message);
        }
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, node), { kind: 'ObjectTypeDefinition' }) : node;
}

function mergeScalar(node, existingNode, config) {
    if (existingNode) {
        return {
            name: node.name,
            description: node['description'] || existingNode['description'],
            kind: (config && config.convertExtensions) ||
                node.kind === 'ScalarTypeDefinition' ||
                existingNode.kind === 'ScalarTypeDefinition'
                ? 'ScalarTypeDefinition'
                : 'ScalarTypeExtension',
            loc: node.loc,
            directives: mergeDirectives(node.directives, existingNode.directives, config),
        };
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, node), { kind: 'ScalarTypeDefinition' }) : node;
}

function mergeUnion(first, second, config) {
    if (second) {
        return {
            name: first.name,
            description: first['description'] || second['description'],
            directives: mergeDirectives(first.directives, second.directives, config),
            kind: (config && config.convertExtensions) ||
                first.kind === 'UnionTypeDefinition' ||
                second.kind === 'UnionTypeDefinition'
                ? 'UnionTypeDefinition'
                : 'UnionTypeExtension',
            loc: first.loc,
            types: mergeNamedTypeArray(first.types, second.types, config),
        };
    }
    return config && config.convertExtensions
        ? __assign(__assign({}, first), { kind: 'UnionTypeDefinition' }) : first;
}

var DEFAULT_OPERATION_TYPE_NAME_MAP = {
    query: 'Query',
    mutation: 'Mutation',
    subscription: 'Subscription',
};
function mergeOperationTypes(opNodeList, existingOpNodeList) {
    if (opNodeList === void 0) { opNodeList = []; }
    if (existingOpNodeList === void 0) { existingOpNodeList = []; }
    var finalOpNodeList = [];
    var _loop_1 = function (opNodeType) {
        var opNode = opNodeList.find(function (n) { return n.operation === opNodeType; }) || existingOpNodeList.find(function (n) { return n.operation === opNodeType; });
        if (opNode) {
            finalOpNodeList.push(opNode);
        }
    };
    for (var opNodeType in DEFAULT_OPERATION_TYPE_NAME_MAP) {
        _loop_1(opNodeType);
    }
    return finalOpNodeList;
}
function mergeSchemaDefs(node, existingNode, config) {
    if (existingNode) {
        return {
            kind: node.kind === Kind.SCHEMA_DEFINITION ||
                existingNode.kind === Kind.SCHEMA_DEFINITION
                ? Kind.SCHEMA_DEFINITION
                : Kind.SCHEMA_EXTENSION,
            description: node['description'] || existingNode['description'],
            directives: mergeDirectives(node.directives, existingNode.directives, config),
            operationTypes: mergeOperationTypes(node.operationTypes, existingNode.operationTypes),
        };
    }
    return ((config === null || config === void 0 ? void 0 : config.convertExtensions)
        ? __assign(__assign({}, node), { kind: Kind.SCHEMA_EXTENSION }) : node);
}

var schemaDefSymbol = 'SCHEMA_DEF_SYMBOL';
function isNamedDefinitionNode(definitionNode) {
    return 'name' in definitionNode;
}
function mergeGraphQLNodes(nodes, config) {
    var e_1, _a;
    var _b, _c, _d;
    var mergedResultMap = {};
    try {
        for (var nodes_1 = __values(nodes), nodes_1_1 = nodes_1.next(); !nodes_1_1.done; nodes_1_1 = nodes_1.next()) {
            var nodeDefinition = nodes_1_1.value;
            if (isNamedDefinitionNode(nodeDefinition)) {
                var name_1 = (_b = nodeDefinition.name) === null || _b === void 0 ? void 0 : _b.value;
                if (config === null || config === void 0 ? void 0 : config.commentDescriptions) {
                    collectComment(nodeDefinition);
                }
                if (name_1 == null) {
                    continue;
                }
                if (((_c = config === null || config === void 0 ? void 0 : config.exclusions) === null || _c === void 0 ? void 0 : _c.includes(name_1 + '.*')) || ((_d = config === null || config === void 0 ? void 0 : config.exclusions) === null || _d === void 0 ? void 0 : _d.includes(name_1))) {
                    delete mergedResultMap[name_1];
                }
                else {
                    switch (nodeDefinition.kind) {
                        case Kind.OBJECT_TYPE_DEFINITION:
                        case Kind.OBJECT_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeType(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.ENUM_TYPE_DEFINITION:
                        case Kind.ENUM_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeEnum(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.UNION_TYPE_DEFINITION:
                        case Kind.UNION_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeUnion(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.SCALAR_TYPE_DEFINITION:
                        case Kind.SCALAR_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeScalar(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.INPUT_OBJECT_TYPE_DEFINITION:
                        case Kind.INPUT_OBJECT_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeInputType(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.INTERFACE_TYPE_DEFINITION:
                        case Kind.INTERFACE_TYPE_EXTENSION:
                            mergedResultMap[name_1] = mergeInterface(nodeDefinition, mergedResultMap[name_1], config);
                            break;
                        case Kind.DIRECTIVE_DEFINITION:
                            mergedResultMap[name_1] = mergeDirective(nodeDefinition, mergedResultMap[name_1]);
                            break;
                    }
                }
            }
            else if (nodeDefinition.kind === Kind.SCHEMA_DEFINITION || nodeDefinition.kind === Kind.SCHEMA_EXTENSION) {
                mergedResultMap[schemaDefSymbol] = mergeSchemaDefs(nodeDefinition, mergedResultMap[schemaDefSymbol], config);
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (nodes_1_1 && !nodes_1_1.done && (_a = nodes_1.return)) _a.call(nodes_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    return mergedResultMap;
}

function mergeTypeDefs(typeSource, config) {
    resetComments();
    var doc = {
        kind: Kind.DOCUMENT,
        definitions: mergeGraphQLTypes(typeSource, __assign({ useSchemaDefinition: true, forceSchemaDefinition: false, throwOnConflict: false, commentDescriptions: false }, config)),
    };
    var result;
    if (config && config.commentDescriptions) {
        result = printWithComments(doc);
    }
    else {
        result = doc;
    }
    resetComments();
    return result;
}
function visitTypeSources(typeSource, options, allNodes, visitedTypeSources) {
    var e_1, _a;
    if (allNodes === void 0) { allNodes = []; }
    if (visitedTypeSources === void 0) { visitedTypeSources = new Set(); }
    if (typeSource && !visitedTypeSources.has(typeSource)) {
        visitedTypeSources.add(typeSource);
        if (typeof typeSource === 'function') {
            visitTypeSources(typeSource(), options, allNodes, visitedTypeSources);
        }
        else if (Array.isArray(typeSource)) {
            try {
                for (var typeSource_1 = __values(typeSource), typeSource_1_1 = typeSource_1.next(); !typeSource_1_1.done; typeSource_1_1 = typeSource_1.next()) {
                    var type = typeSource_1_1.value;
                    visitTypeSources(type, options, allNodes, visitedTypeSources);
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (typeSource_1_1 && !typeSource_1_1.done && (_a = typeSource_1.return)) _a.call(typeSource_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
        }
        else if (isSchema(typeSource)) {
            var documentNode = getDocumentNodeFromSchema(typeSource, options);
            visitTypeSources(documentNode.definitions, options, allNodes, visitedTypeSources);
        }
        else if (isStringTypes(typeSource) || isSourceTypes(typeSource)) {
            var documentNode = parse(typeSource, options);
            visitTypeSources(documentNode.definitions, options, allNodes, visitedTypeSources);
        }
        else if (typeof typeSource === 'object' && isDefinitionNode(typeSource)) {
            allNodes.push(typeSource);
        }
        else if (isDocumentNode(typeSource)) {
            visitTypeSources(typeSource.definitions, options, allNodes, visitedTypeSources);
        }
        else {
            throw new Error("typeDefs must contain only strings, documents, schemas, or functions, got " + typeof typeSource);
        }
    }
    return allNodes;
}
function mergeGraphQLTypes(typeSource, config) {
    var _a, _b, _c;
    resetComments();
    var allNodes = visitTypeSources(typeSource, config);
    var mergedNodes = mergeGraphQLNodes(allNodes, config);
    if (config === null || config === void 0 ? void 0 : config.useSchemaDefinition) {
        // XXX: right now we don't handle multiple schema definitions
        var schemaDef = mergedNodes[schemaDefSymbol] || {
            kind: Kind.SCHEMA_DEFINITION,
            operationTypes: [],
        };
        var operationTypes = schemaDef.operationTypes;
        var _loop_1 = function (opTypeDefNodeType) {
            var opTypeDefNode = operationTypes.find(function (operationType) { return operationType.operation === opTypeDefNodeType; });
            if (!opTypeDefNode) {
                var possibleRootTypeName = DEFAULT_OPERATION_TYPE_NAME_MAP[opTypeDefNodeType];
                var existingPossibleRootType = mergedNodes[possibleRootTypeName];
                if (existingPossibleRootType != null && existingPossibleRootType.name != null) {
                    operationTypes.push({
                        kind: Kind.OPERATION_TYPE_DEFINITION,
                        type: {
                            kind: Kind.NAMED_TYPE,
                            name: existingPossibleRootType.name,
                        },
                        operation: opTypeDefNodeType,
                    });
                }
            }
        };
        for (var opTypeDefNodeType in DEFAULT_OPERATION_TYPE_NAME_MAP) {
            _loop_1(opTypeDefNodeType);
        }
        if (((_a = schemaDef === null || schemaDef === void 0 ? void 0 : schemaDef.operationTypes) === null || _a === void 0 ? void 0 : _a.length) != null && schemaDef.operationTypes.length > 0) {
            mergedNodes[schemaDefSymbol] = schemaDef;
        }
    }
    if ((config === null || config === void 0 ? void 0 : config.forceSchemaDefinition) && !((_c = (_b = mergedNodes[schemaDefSymbol]) === null || _b === void 0 ? void 0 : _b.operationTypes) === null || _c === void 0 ? void 0 : _c.length)) {
        mergedNodes[schemaDefSymbol] = {
            kind: Kind.SCHEMA_DEFINITION,
            operationTypes: [
                {
                    kind: Kind.OPERATION_TYPE_DEFINITION,
                    operation: 'query',
                    type: {
                        kind: Kind.NAMED_TYPE,
                        name: {
                            kind: Kind.NAME,
                            value: 'Query',
                        },
                    },
                },
            ],
        };
    }
    var mergedNodeDefinitions = Object.values(mergedNodes);
    if (config === null || config === void 0 ? void 0 : config.sort) {
        var sortFn_1 = typeof config.sort === 'function' ? config.sort : defaultStringComparator;
        mergedNodeDefinitions.sort(function (a, b) { var _a, _b; return sortFn_1((_a = a.name) === null || _a === void 0 ? void 0 : _a.value, (_b = b.name) === null || _b === void 0 ? void 0 : _b.value); });
    }
    return mergedNodeDefinitions;
}

function travelSchemaPossibleExtensions(schema, hooks) {
    var e_1, _a, e_2, _b, e_3, _c, e_4, _d, e_5, _e, e_6, _f, e_7, _g;
    hooks.onSchema(schema);
    var typesMap = schema.getTypeMap();
    try {
        for (var _h = __values(Object.entries(typesMap)), _j = _h.next(); !_j.done; _j = _h.next()) {
            var _k = __read(_j.value, 2), type = _k[1];
            var isPredefinedScalar = isScalarType(type) && isSpecifiedScalarType(type);
            var isIntrospection = isIntrospectionType(type);
            if (isPredefinedScalar || isIntrospection) {
                continue;
            }
            if (isObjectType(type)) {
                hooks.onObjectType(type);
                var fields = type.getFields();
                try {
                    for (var _l = (e_2 = void 0, __values(Object.entries(fields))), _m = _l.next(); !_m.done; _m = _l.next()) {
                        var _o = __read(_m.value, 2), field = _o[1];
                        hooks.onObjectField(type, field);
                        var args = field.args || [];
                        try {
                            for (var args_1 = (e_3 = void 0, __values(args)), args_1_1 = args_1.next(); !args_1_1.done; args_1_1 = args_1.next()) {
                                var arg = args_1_1.value;
                                hooks.onObjectFieldArg(type, field, arg);
                            }
                        }
                        catch (e_3_1) { e_3 = { error: e_3_1 }; }
                        finally {
                            try {
                                if (args_1_1 && !args_1_1.done && (_c = args_1.return)) _c.call(args_1);
                            }
                            finally { if (e_3) throw e_3.error; }
                        }
                    }
                }
                catch (e_2_1) { e_2 = { error: e_2_1 }; }
                finally {
                    try {
                        if (_m && !_m.done && (_b = _l.return)) _b.call(_l);
                    }
                    finally { if (e_2) throw e_2.error; }
                }
            }
            else if (isInterfaceType(type)) {
                hooks.onInterface(type);
                var fields = type.getFields();
                try {
                    for (var _p = (e_4 = void 0, __values(Object.entries(fields))), _q = _p.next(); !_q.done; _q = _p.next()) {
                        var _r = __read(_q.value, 2), field = _r[1];
                        hooks.onInterfaceField(type, field);
                        var args = field.args || [];
                        try {
                            for (var args_2 = (e_5 = void 0, __values(args)), args_2_1 = args_2.next(); !args_2_1.done; args_2_1 = args_2.next()) {
                                var arg = args_2_1.value;
                                hooks.onInterfaceFieldArg(type, field, arg);
                            }
                        }
                        catch (e_5_1) { e_5 = { error: e_5_1 }; }
                        finally {
                            try {
                                if (args_2_1 && !args_2_1.done && (_e = args_2.return)) _e.call(args_2);
                            }
                            finally { if (e_5) throw e_5.error; }
                        }
                    }
                }
                catch (e_4_1) { e_4 = { error: e_4_1 }; }
                finally {
                    try {
                        if (_q && !_q.done && (_d = _p.return)) _d.call(_p);
                    }
                    finally { if (e_4) throw e_4.error; }
                }
            }
            else if (isInputObjectType(type)) {
                hooks.onInputType(type);
                var fields = type.getFields();
                try {
                    for (var _s = (e_6 = void 0, __values(Object.entries(fields))), _t = _s.next(); !_t.done; _t = _s.next()) {
                        var _u = __read(_t.value, 2), field = _u[1];
                        hooks.onInputFieldType(type, field);
                    }
                }
                catch (e_6_1) { e_6 = { error: e_6_1 }; }
                finally {
                    try {
                        if (_t && !_t.done && (_f = _s.return)) _f.call(_s);
                    }
                    finally { if (e_6) throw e_6.error; }
                }
            }
            else if (isUnionType(type)) {
                hooks.onUnion(type);
            }
            else if (isScalarType(type)) {
                hooks.onScalar(type);
            }
            else if (isEnumType(type)) {
                hooks.onEnum(type);
                try {
                    for (var _v = (e_7 = void 0, __values(type.getValues())), _w = _v.next(); !_w.done; _w = _v.next()) {
                        var value = _w.value;
                        hooks.onEnumValue(type, value);
                    }
                }
                catch (e_7_1) { e_7 = { error: e_7_1 }; }
                finally {
                    try {
                        if (_w && !_w.done && (_g = _v.return)) _g.call(_v);
                    }
                    finally { if (e_7) throw e_7.error; }
                }
            }
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (_j && !_j.done && (_a = _h.return)) _a.call(_h);
        }
        finally { if (e_1) throw e_1.error; }
    }
}
function mergeExtensions(extensions) {
    return mergeDeep(extensions);
}
function applyExtensionObject(obj, extensions) {
    if (!obj) {
        return;
    }
    obj.extensions = mergeDeep([obj.extensions || {}, extensions || {}]);
}
function applyExtensions(schema, extensions) {
    var e_8, _a, e_9, _b, e_10, _c, e_11, _d, e_12, _e;
    applyExtensionObject(schema, extensions.schemaExtensions);
    try {
        for (var _f = __values(Object.entries(extensions.types || {})), _g = _f.next(); !_g.done; _g = _f.next()) {
            var _h = __read(_g.value, 2), typeName = _h[0], data = _h[1];
            var type = schema.getType(typeName);
            if (type) {
                applyExtensionObject(type, data.extensions);
                if (data.type === 'object' || data.type === 'interface') {
                    try {
                        for (var _j = (e_9 = void 0, __values(Object.entries(data.fields))), _k = _j.next(); !_k.done; _k = _j.next()) {
                            var _l = __read(_k.value, 2), fieldName = _l[0], fieldData = _l[1];
                            var field = type.getFields()[fieldName];
                            if (field) {
                                applyExtensionObject(field, fieldData.extensions);
                                var _loop_1 = function (arg, argData) {
                                    applyExtensionObject(field.args.find(function (a) { return a.name === arg; }), argData);
                                };
                                try {
                                    for (var _m = (e_10 = void 0, __values(Object.entries(fieldData.arguments))), _o = _m.next(); !_o.done; _o = _m.next()) {
                                        var _p = __read(_o.value, 2), arg = _p[0], argData = _p[1];
                                        _loop_1(arg, argData);
                                    }
                                }
                                catch (e_10_1) { e_10 = { error: e_10_1 }; }
                                finally {
                                    try {
                                        if (_o && !_o.done && (_c = _m.return)) _c.call(_m);
                                    }
                                    finally { if (e_10) throw e_10.error; }
                                }
                            }
                        }
                    }
                    catch (e_9_1) { e_9 = { error: e_9_1 }; }
                    finally {
                        try {
                            if (_k && !_k.done && (_b = _j.return)) _b.call(_j);
                        }
                        finally { if (e_9) throw e_9.error; }
                    }
                }
                else if (data.type === 'input') {
                    try {
                        for (var _q = (e_11 = void 0, __values(Object.entries(data.fields))), _r = _q.next(); !_r.done; _r = _q.next()) {
                            var _s = __read(_r.value, 2), fieldName = _s[0], fieldData = _s[1];
                            var field = type.getFields()[fieldName];
                            applyExtensionObject(field, fieldData.extensions);
                        }
                    }
                    catch (e_11_1) { e_11 = { error: e_11_1 }; }
                    finally {
                        try {
                            if (_r && !_r.done && (_d = _q.return)) _d.call(_q);
                        }
                        finally { if (e_11) throw e_11.error; }
                    }
                }
                else if (data.type === 'enum') {
                    try {
                        for (var _t = (e_12 = void 0, __values(Object.entries(data.values))), _u = _t.next(); !_u.done; _u = _t.next()) {
                            var _v = __read(_u.value, 2), valueName = _v[0], valueData = _v[1];
                            var value = type.getValue(valueName);
                            applyExtensionObject(value, valueData);
                        }
                    }
                    catch (e_12_1) { e_12 = { error: e_12_1 }; }
                    finally {
                        try {
                            if (_u && !_u.done && (_e = _t.return)) _e.call(_t);
                        }
                        finally { if (e_12) throw e_12.error; }
                    }
                }
            }
        }
    }
    catch (e_8_1) { e_8 = { error: e_8_1 }; }
    finally {
        try {
            if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
        }
        finally { if (e_8) throw e_8.error; }
    }
    return schema;
}
function extractExtensionsFromSchema(schema) {
    var result = {
        schemaExtensions: {},
        types: {},
    };
    travelSchemaPossibleExtensions(schema, {
        onSchema: function (schema) { return (result.schemaExtensions = schema.extensions || {}); },
        onObjectType: function (type) { return (result.types[type.name] = { fields: {}, type: 'object', extensions: type.extensions || {} }); },
        onObjectField: function (type, field) {
            return (result.types[type.name].fields[field.name] = {
                arguments: {},
                extensions: field.extensions || {},
            });
        },
        onObjectFieldArg: function (type, field, arg) {
            return (result.types[type.name].fields[field.name].arguments[arg.name] = arg.extensions || {});
        },
        onInterface: function (type) {
            return (result.types[type.name] = { fields: {}, type: 'interface', extensions: type.extensions || {} });
        },
        onInterfaceField: function (type, field) {
            return (result.types[type.name].fields[field.name] = {
                arguments: {},
                extensions: field.extensions || {},
            });
        },
        onInterfaceFieldArg: function (type, field, arg) {
            return (result.types[type.name].fields[field.name].arguments[arg.name] =
                arg.extensions || {});
        },
        onEnum: function (type) { return (result.types[type.name] = { values: {}, type: 'enum', extensions: type.extensions || {} }); },
        onEnumValue: function (type, value) {
            return (result.types[type.name].values[value.name] = value.extensions || {});
        },
        onScalar: function (type) { return (result.types[type.name] = { type: 'scalar', extensions: type.extensions || {} }); },
        onUnion: function (type) { return (result.types[type.name] = { type: 'union', extensions: type.extensions || {} }); },
        onInputType: function (type) { return (result.types[type.name] = { fields: {}, type: 'input', extensions: type.extensions || {} }); },
        onInputFieldType: function (type, field) {
            return (result.types[type.name].fields[field.name] = { extensions: field.extensions || {} });
        },
    });
    return result;
}

var defaultResolverValidationOptions = {
    requireResolversForArgs: 'ignore',
    requireResolversForNonScalar: 'ignore',
    requireResolversForAllFields: 'ignore',
    requireResolversForResolveType: 'ignore',
    requireResolversToMatchSchema: 'ignore',
};
/**
 * Synchronously merges multiple schemas, typeDefinitions and/or resolvers into a single schema.
 * @param config Configuration object
 */
function mergeSchemas(config) {
    var e_1, _a;
    var typeDefs = mergeTypeDefs([config.schemas, config.typeDefs || []], config);
    var extractedResolvers = [];
    var extractedExtensions = [];
    try {
        for (var _b = __values(config.schemas), _c = _b.next(); !_c.done; _c = _b.next()) {
            var schema = _c.value;
            extractedResolvers.push(getResolversFromSchema(schema));
            extractedExtensions.push(extractExtensionsFromSchema(schema));
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_1) throw e_1.error; }
    }
    extractedResolvers.push.apply(extractedResolvers, __spreadArray([], __read(ensureResolvers(config))));
    var resolvers = mergeResolvers(extractedResolvers, config);
    var extensions = mergeExtensions(extractedExtensions);
    return makeSchema({ resolvers: resolvers, typeDefs: typeDefs, extensions: extensions }, config);
}
/**
 * Asynchronously merges multiple schemas, typeDefinitions and/or resolvers into a single schema.
 * @param config Configuration object
 */
function mergeSchemasAsync(config) {
    return __awaiter(this, void 0, void 0, function () {
        var _a, typeDefs, resolvers, extensions;
        var _this = this;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, Promise.all([
                        mergeTypeDefs([config.schemas, config.typeDefs || []], config),
                        Promise.all(config.schemas.map(function (schema) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
                            return [2 /*return*/, getResolversFromSchema(schema)];
                        }); }); })).then(function (extractedResolvers) {
                            return mergeResolvers(__spreadArray(__spreadArray([], __read(extractedResolvers)), __read(ensureResolvers(config))), config);
                        }),
                        Promise.all(config.schemas.map(function (schema) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
                            return [2 /*return*/, extractExtensionsFromSchema(schema)];
                        }); }); })).then(function (extractedExtensions) {
                            return mergeExtensions(extractedExtensions);
                        }),
                    ])];
                case 1:
                    _a = __read.apply(void 0, [_b.sent(), 3]), typeDefs = _a[0], resolvers = _a[1], extensions = _a[2];
                    return [2 /*return*/, makeSchema({ resolvers: resolvers, typeDefs: typeDefs, extensions: extensions }, config)];
            }
        });
    });
}
function ensureResolvers(config) {
    return config.resolvers ? asArray(config.resolvers) : [];
}
function makeSchema(_a, config) {
    var resolvers = _a.resolvers, typeDefs = _a.typeDefs, extensions = _a.extensions;
    var schema = typeof typeDefs === 'string' ? buildSchema(typeDefs, config) : buildASTSchema(typeDefs, config);
    // add resolvers
    if (resolvers) {
        schema = addResolversToSchema({
            schema: schema,
            resolvers: resolvers,
            resolverValidationOptions: __assign(__assign({}, defaultResolverValidationOptions), (config.resolverValidationOptions || {})),
        });
    }
    // extensions
    applyExtensions(schema, extensions);
    return schema;
}

export { CompareVal, applyExtensions, collectComment, defaultStringComparator, extractExtensionsFromSchema, extractType, isListTypeNode, isNamedDefinitionNode, isNonNullTypeNode, isSourceTypes, isStringTypes, isWrappingTypeNode, mergeArguments, mergeDirective, mergeDirectives, mergeEnum, mergeEnumValues, mergeExtensions, mergeFields, mergeGraphQLNodes, mergeGraphQLTypes, mergeInputType, mergeInterface, mergeNamedTypeArray, mergeResolvers, mergeScalar, mergeSchemas, mergeSchemasAsync, mergeType, mergeTypeDefs, mergeUnion, printComment, printTypeNode, printWithComments, pushComment, resetComments, schemaDefSymbol, travelSchemaPossibleExtensions };
