UNPKG

@omnigraph/json-schema

Version:

This package generates GraphQL Schema from JSON Schema and sample JSON request and responses. You can define your root field endpoints like below in your GraphQL Config for example;

1,496 lines • 77.1 kB
import { getNamedType, GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLString, isNonNullType, } from 'graphql';
import { EnumTypeComposer, InputTypeComposer, InterfaceTypeComposer, isSomeInputTypeComposer, ListComposer, NonNullComposer, ObjectTypeComposer, ScalarTypeComposer, SchemaComposer, UnionTypeComposer, } from 'graphql-compose';
import { GraphQLBigInt, GraphQLByte, GraphQLDateTime, GraphQLEmailAddress, GraphQLIPv4, GraphQLIPv6, GraphQLJSON, GraphQLNegativeFloat, GraphQLNegativeInt, GraphQLNonEmptyString, GraphQLNonNegativeFloat, GraphQLNonNegativeInt, GraphQLNonPositiveFloat, GraphQLNonPositiveInt, GraphQLPositiveFloat, GraphQLPositiveInt, GraphQLTime, GraphQLTimestamp, GraphQLURL, GraphQLUUID, } from 'graphql-scalars';
import { visitJSONSchema } from 'json-machete';
import { sanitizeNameForGraphQL } from '@graphql-mesh/utils';
import { DictionaryDirective, DiscriminatorDirective, EnumDirective, ExampleDirective, FlattenDirective, LengthDirective, OneOfDirective, RegExpDirective, ResolveRootDirective, ResolveRootFieldDirective, TypeScriptDirective, } from './directives.js';
import { getJSONSchemaStringFormatScalarMap } from './getJSONSchemaStringFormatScalarMap.js';
import { getUnionTypeComposers } from './getUnionTypeComposers.js';
import { getValidTypeName } from './getValidTypeName.js';
import { GraphQLFile, GraphQLVoid } from './scalars.js';
const formatScalarMapWithoutAjv = {
    byte: GraphQLByte,
    binary: GraphQLFile,
    'date-time': GraphQLDateTime,
    time: GraphQLTime,
    email: GraphQLEmailAddress,
    ipv4: GraphQLIPv4,
    ipv6: GraphQLIPv6,
    uri: GraphQLURL,
    uuid: GraphQLUUID,
    'unix-time': GraphQLTimestamp,
    int64: GraphQLBigInt,
    int32: GraphQLInt,
    decimal: GraphQLFloat,
    float: GraphQLFloat,
};
const deepMergedInputTypeComposerFields = new WeakMap();
function deepMergeInputTypeComposerFields(existingInputTypeComposer, newInputTypeComposer) {
    let mergedInputTypes = deepMergedInputTypeComposerFields.get(existingInputTypeComposer);
    if (!mergedInputTypes) {
        mergedInputTypes = new WeakSet();
        deepMergedInputTypeComposerFields.set(existingInputTypeComposer, mergedInputTypes);
    }
    if (mergedInputTypes.has(newInputTypeComposer)) {
        return;
    }
    mergedInputTypes.add(newInputTypeComposer);
    const existingInputTypeComposerFields = existingInputTypeComposer.getFields();
    const newInputTypeComposerFields = newInputTypeComposer.getFields();
    for (const [newFieldKey, newFieldValue] of Object.entries(newInputTypeComposerFields)) {
        const existingFieldValue = existingInputTypeComposerFields[newFieldKey];
        if (!existingFieldValue) {
            existingInputTypeComposerFields[newFieldKey] = newFieldValue;
        }
        else {
            const existingFieldUnwrappedTC = typeof existingFieldValue.type?.getUnwrappedTC === 'function'
                ? existingFieldValue.type?.getUnwrappedTC()
                : undefined;
            const newFieldUnwrappedTC = typeof newFieldValue.type.getUnwrappedTC === 'function'
                ? newFieldValue.type.getUnwrappedTC()
                : undefined;
            if (existingFieldUnwrappedTC instanceof InputTypeComposer &&
                newFieldUnwrappedTC instanceof InputTypeComposer) {
                deepMergeInputTypeComposerFields(existingFieldUnwrappedTC, newFieldUnwrappedTC);
            }
            else {
                existingInputTypeComposerFields[newFieldKey] = newFieldValue;
            }
        }
    }
}
function deepMergeObjectTypeComposerFields(existingObjectTypeComposer, newObjectTypeComposer) {
    const existingObjectTypeComposerFields = existingObjectTypeComposer.getFields();
    const newObjectTypeComposerFields = newObjectTypeComposer.getFields();
    for (const [newFieldKey, newFieldValue] of Object.entries(newObjectTypeComposerFields)) {
        const existingFieldValue = existingObjectTypeComposerFields[newFieldKey];
        if (!existingFieldValue) {
            existingObjectTypeComposerFields[newFieldKey] = newFieldValue;
        }
        else {
            const existingFieldUnwrappedTC = typeof existingFieldValue.type?.getUnwrappedTC === 'function'
                ? existingFieldValue.type?.getUnwrappedTC()
                : undefined;
            const newFieldUnwrappedTC = typeof newFieldValue.type.getUnwrappedTC === 'function'
                ? newFieldValue.type.getUnwrappedTC()
                : undefined;
            const existingOTC = asObjectTypeComposer(existingFieldUnwrappedTC);
            const newOTC = asObjectTypeComposer(newFieldUnwrappedTC);
            if (existingOTC && newOTC) {
                deepMergeObjectTypeComposerFields(existingOTC, newOTC);
            }
            else {
                if (newFieldUnwrappedTC &&
                    existingFieldUnwrappedTC &&
                    !isUnspecificType(newFieldUnwrappedTC) &&
                    isUnspecificType(existingFieldUnwrappedTC)) {
                    continue;
                }
                existingObjectTypeComposerFields[newFieldKey] = newFieldValue;
            }
        }
    }
}
function asObjectTypeComposer(tc) {
    let current = tc;
    while (current instanceof NonNullComposer || current instanceof ListComposer) {
        current = current.ofType;
    }
    return current instanceof ObjectTypeComposer ? current : undefined;
}
function isJsonPrimitiveSchema(schema) {
    if (schema.properties ||
        schema.allOf ||
        schema.anyOf ||
        schema.oneOf ||
        schema.additionalProperties ||
        schema.items) {
        return false;
    }
    return (schema.type === 'string' ||
        schema.type === 'number' ||
        schema.type === 'integer' ||
        schema.type === 'boolean');
}
export function getComposerFromJSONSchema({ subgraphName, schema, logger, getScalarForFormat, }) {
    const schemaComposer = new SchemaComposer();
    const formatScalarMap = getJSONSchemaStringFormatScalarMap();
    const getDefaultScalarForFormat = (format) => formatScalarMapWithoutAjv[format] || formatScalarMap.get(format);
    const rootInputTypeNameComposerMap = {
        QueryInput: () => schemaComposer.Query,
        MutationInput: () => schemaComposer.Mutation,
        SubscriptionInput: () => schemaComposer.Subscription,
    };
    return visitJSONSchema(schema, {
        enter(subSchema, { path, visitedSubschemaResultMap }) {
            if (typeof subSchema === 'boolean' || subSchema.title === 'Any') {
                const typeComposer = schemaComposer.getAnyTC(GraphQLJSON);
                return subSchema
                    ? {
                        input: typeComposer,
                        output: typeComposer,
                    }
                    : undefined;
            }
            if (!subSchema) {
                throw new Error(`Something is wrong with ${path}`);
            }
            if (subSchema.type === 'array') {
                if (subSchema.items != null &&
                    typeof subSchema.items === 'object' &&
                    Object.keys(subSchema.items).length > 0) {
                    return {
                        // These are filled after enter
                        get input() {
                            const typeComposers = visitedSubschemaResultMap.get(subSchema.items);
                            return typeComposers.input.getTypePlural();
                        },
                        get output() {
                            const typeComposers = visitedSubschemaResultMap.get(subSchema.items);
                            return typeComposers.output.getTypePlural();
                        },
                        ...subSchema,
                    };
                }
                if (subSchema.contains) {
                    // Scalars cannot be in union type
                    const typeComposer = schemaComposer.getAnyTC(GraphQLJSON).getTypePlural();
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                // If it doesn't have any clue
                {
                    // const typeComposer = getGenericJSONScalar({
                    //   schemaComposer,
                    //   isInput: false,
                    //   subSchema,
                    //   validateWithJSONSchema,
                    // }).getTypePlural();
                    const typeComposer = schemaComposer.getAnyTC(GraphQLJSON).getTypePlural();
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
            }
            if (subSchema.pattern) {
                // A `$ref` with both `format` and `pattern` takes this branch (named from `title`),
                // while a nullable `anyOf`/`oneOf` wrapper of the same `$ref` takes the format
                // branch after collapse. Reuse the format scalar only when those names would
                // collide (e.g. title `UUID` + `format: uuid` → two types named `UUID`). Other
                // pairs such as title `endpoint` + `format: uri-template` keep `@regexp`.
                if (subSchema.format && subSchema.title) {
                    const formatScalar = getScalarForFormat?.(subSchema.format) || getDefaultScalarForFormat(subSchema.format);
                    if (formatScalar && sanitizeNameForGraphQL(subSchema.title) === formatScalar.name) {
                        const typeComposer = schemaComposer.getAnyTC(formatScalar);
                        return {
                            input: typeComposer,
                            output: typeComposer,
                            description: subSchema.description,
                            nullable: subSchema.nullable,
                            readOnly: subSchema.readOnly,
                            writeOnly: subSchema.writeOnly,
                            default: subSchema.default,
                            deprecated: subSchema.deprecated,
                        };
                    }
                }
                let typeScriptType;
                switch (subSchema.type) {
                    case 'number':
                        typeScriptType = 'number';
                        break;
                    case 'integer':
                        if (subSchema.format === 'int64') {
                            typeScriptType = 'bigint';
                        }
                        else {
                            typeScriptType = 'number';
                        }
                        break;
                    default:
                        typeScriptType = 'string';
                        break;
                }
                schemaComposer.addDirective(RegExpDirective);
                schemaComposer.addDirective(TypeScriptDirective);
                const typeComposer = schemaComposer.createScalarTC({
                    name: getValidTypeName({
                        schemaComposer,
                        isInput: false,
                        subSchema,
                    }),
                    directives: [
                        {
                            name: 'regexp',
                            args: {
                                subgraph: subgraphName,
                                pattern: subSchema.pattern,
                            },
                        },
                        {
                            name: 'typescript',
                            args: {
                                subgraph: subgraphName,
                                type: typeScriptType,
                            },
                        },
                    ],
                });
                return {
                    input: typeComposer,
                    output: typeComposer,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    deprecated: subSchema.deprecated,
                };
            }
            if (subSchema.const) {
                const scalarTypeName = getValidTypeName({
                    schemaComposer,
                    isInput: false,
                    subSchema,
                });
                schemaComposer.addDirective(EnumDirective);
                schemaComposer.addDirective(TypeScriptDirective);
                schemaComposer.addDirective(ExampleDirective);
                let enumValueName = sanitizeNameForGraphQL(subSchema.const.toString());
                if (enumValueName === 'true' || enumValueName === 'false') {
                    enumValueName = enumValueName.toUpperCase();
                }
                const typeComposer = schemaComposer.createEnumTC({
                    name: scalarTypeName,
                    values: {
                        [enumValueName]: {
                            directives: [
                                {
                                    name: 'enum',
                                    args: {
                                        subgraph: subgraphName,
                                        value: JSON.stringify(subSchema.const),
                                    },
                                },
                            ],
                        },
                    },
                    directives: [
                        {
                            name: 'typescript',
                            args: {
                                subgraph: subgraphName,
                                type: JSON.stringify(subSchema.const),
                            },
                        },
                        {
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: subSchema.const,
                            },
                        },
                    ],
                    extensions: {
                        default: subSchema.const,
                    },
                });
                return {
                    input: typeComposer,
                    output: typeComposer,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    deprecated: subSchema.deprecated,
                };
            }
            if (subSchema.enum && subSchema.type !== 'boolean') {
                const values = {};
                for (const value of subSchema.enum) {
                    let enumKey = sanitizeNameForGraphQL(value.toString());
                    if (enumKey === 'false' || enumKey === 'true' || enumKey === 'null') {
                        enumKey = enumKey.toUpperCase();
                    }
                    if (typeof enumKey === 'string' && enumKey.length === 0) {
                        enumKey = '_';
                    }
                    schemaComposer.addDirective(EnumDirective);
                    // Falsy values are ignored by GraphQL
                    const enumValue = value || value === 0 ? value : value?.toString();
                    const directives = [];
                    if (enumValue !== enumKey) {
                        directives.push({
                            name: 'enum',
                            args: {
                                subgraph: subgraphName,
                                value: JSON.stringify(enumValue),
                            },
                        });
                    }
                    values[enumKey] = {
                        directives,
                        value: enumValue,
                    };
                }
                const directives = [];
                if (subSchema.examples?.length) {
                    schemaComposer.addDirective(ExampleDirective);
                    for (const example of subSchema.examples) {
                        directives.push({
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: example,
                            },
                        });
                    }
                }
                const typeComposer = schemaComposer.createEnumTC({
                    name: getValidTypeName({
                        schemaComposer,
                        isInput: false,
                        subSchema,
                    }),
                    values,
                    description: subSchema.description,
                    directives,
                    extensions: {
                        default: subSchema.default,
                    },
                });
                return {
                    input: typeComposer,
                    output: typeComposer,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            // OpenAPI 3.1 / JSON Schema: `anyOf`/`oneOf` with a `{ type: 'null' }` branch means nullable.
            // Run before format/`switch (type)` so a remaining primitive (e.g. string+format) is handled normally.
            // Remaining object/$ref members are left as a 1-element union so `leave` can reuse that type
            // instead of cloning it as `Title2`. Keep this after `pattern` so pattern-only nullable
            // wrappers do not mint a second regexp scalar (Slack Bot_User_ID / #9637 CI).
            for (const unionKey of ['anyOf', 'oneOf']) {
                if (subSchema[unionKey] &&
                    !subSchema.properties &&
                    !subSchema.allOf &&
                    !subSchema.additionalProperties) {
                    const unionArr = subSchema[unionKey];
                    const nonNullUnion = unionArr.filter(s => s.type !== 'null');
                    if (nonNullUnion.length === unionArr.length) {
                        continue;
                    }
                    if (nonNullUnion.length === 0) {
                        const typeComposer = schemaComposer.getAnyTC(GraphQLVoid);
                        return {
                            input: typeComposer,
                            output: typeComposer,
                            nullable: true,
                            description: subSchema.description,
                            readOnly: subSchema.readOnly,
                            writeOnly: subSchema.writeOnly,
                            default: subSchema.default,
                            deprecated: subSchema.deprecated,
                        };
                    }
                    subSchema.nullable = true;
                    if (nonNullUnion.length === 1 && isJsonPrimitiveSchema(nonNullUnion[0])) {
                        Object.assign(subSchema, nonNullUnion[0]);
                        delete subSchema[unionKey];
                        // The referenced primitive may carry `nullable: false`; the `null` union
                        // member still permits null even when the field is required.
                        subSchema.nullable = true;
                    }
                    else {
                        subSchema[unionKey] = nonNullUnion;
                    }
                    break;
                }
            }
            if (Array.isArray(subSchema.type)) {
                const validTypes = subSchema.type.filter((typeName) => typeName !== 'null');
                if (validTypes.length === 1) {
                    subSchema.type = validTypes[0];
                    // continue with the single type
                }
                else {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLJSON);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
            }
            if (subSchema.format) {
                const formatScalar = getScalarForFormat?.(subSchema.format) || getDefaultScalarForFormat(subSchema.format);
                if (formatScalar) {
                    const typeComposer = schemaComposer.getAnyTC(formatScalar);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
            }
            if (subSchema.minimum === 0) {
                const typeComposer = schemaComposer.getAnyTC(subSchema.type === 'integer' ? GraphQLNonNegativeInt : GraphQLNonNegativeFloat);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchema.description,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            else if (subSchema.minimum > 0) {
                const typeComposer = schemaComposer.getAnyTC(subSchema.type === 'integer' ? GraphQLPositiveInt : GraphQLPositiveFloat);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchema.description,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            if (subSchema.maximum === 0) {
                const typeComposer = schemaComposer.getAnyTC(subSchema.type === 'integer' ? GraphQLNonPositiveInt : GraphQLNonPositiveFloat);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchema.description,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            else if (subSchema.maximum < 0) {
                const typeComposer = schemaComposer.getAnyTC(subSchema.type === 'integer' ? GraphQLNegativeInt : GraphQLNegativeFloat);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchema.description,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            if (subSchema.maximum > Number.MAX_SAFE_INTEGER ||
                subSchema.minimum < Number.MIN_SAFE_INTEGER) {
                const typeComposer = schemaComposer.getAnyTC(GraphQLBigInt);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchema.description,
                    nullable: subSchema.nullable,
                    readOnly: subSchema.readOnly,
                    writeOnly: subSchema.writeOnly,
                    default: subSchema.default,
                    deprecated: subSchema.deprecated,
                };
            }
            switch (subSchema.type) {
                case 'boolean': {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLBoolean);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                case 'null': {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLVoid);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                case 'integer': {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLInt);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                case 'number': {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLFloat);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                case 'string': {
                    if (subSchema.minLength === 1 && subSchema.maxLength == null) {
                        const tc = schemaComposer.getAnyTC(GraphQLNonEmptyString);
                        return {
                            input: tc,
                            output: tc,
                            description: subSchema.description,
                            nullable: subSchema.nullable,
                            readOnly: subSchema.readOnly,
                            writeOnly: subSchema.writeOnly,
                            default: subSchema.default,
                            deprecated: subSchema.deprecated,
                        };
                    }
                    if (subSchema.minLength != null || subSchema.maxLength != null) {
                        schemaComposer.addDirective(LengthDirective);
                        const lengthArgs = {
                            subgraph: subgraphName,
                        };
                        if (subSchema.minLength != null) {
                            lengthArgs.min = subSchema.minLength;
                        }
                        if (subSchema.maxLength != null) {
                            lengthArgs.max = subSchema.maxLength;
                        }
                        const typeComposer = schemaComposer.createScalarTC({
                            name: getValidTypeName({
                                schemaComposer,
                                isInput: false,
                                subSchema,
                            }),
                            description: subSchema.description,
                            directives: [
                                {
                                    name: 'length',
                                    args: lengthArgs,
                                },
                            ],
                        });
                        return {
                            input: typeComposer,
                            output: typeComposer,
                            description: subSchema.description,
                            nullable: subSchema.nullable,
                            readOnly: subSchema.readOnly,
                            writeOnly: subSchema.writeOnly,
                            default: subSchema.default,
                            deprecated: subSchema.deprecated,
                        };
                    }
                    const typeComposer = schemaComposer.getAnyTC(GraphQLString);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                case 'object': {
                    switch (subSchema.title) {
                        case '_schema':
                            return {
                                output: schemaComposer,
                                ...subSchema,
                            };
                        case 'Query':
                            return {
                                output: schemaComposer.Query,
                                ...subSchema,
                            };
                        case 'Mutation':
                            return {
                                output: schemaComposer.Mutation,
                                ...subSchema,
                            };
                        case 'Subscription': {
                            if (path === '/properties/subscription') {
                                return {
                                    output: schemaComposer.Subscription,
                                    ...subSchema,
                                };
                            }
                            subSchema.title = 'Subscription_';
                            break;
                        }
                    }
                }
            }
            if (subSchema.oneOf && !subSchema.properties) {
                schemaComposer.addDirective(OneOfDirective);
                const input = schemaComposer.createInputTC({
                    name: getValidTypeName({
                        schemaComposer,
                        isInput: true,
                        subSchema,
                    }),
                    fields: {},
                    directives: [
                        {
                            name: 'oneOf',
                            args: {
                                subgraph: subgraphName,
                            },
                        },
                    ],
                });
                const extensions = {};
                if (subSchema.$comment?.startsWith('statusCodeOneOfIndexMap:')) {
                    const statusCodeOneOfIndexMapStr = subSchema.$comment.replace('statusCodeOneOfIndexMap:', '');
                    const statusCodeOneOfIndexMap = JSON.parse(statusCodeOneOfIndexMapStr);
                    if (statusCodeOneOfIndexMap) {
                        extensions.statusCodeOneOfIndexMap = statusCodeOneOfIndexMap;
                    }
                }
                const output = schemaComposer.createUnionTC({
                    name: getValidTypeName({
                        schemaComposer,
                        isInput: false,
                        subSchema,
                    }),
                    description: subSchema.description,
                    types: [],
                    extensions,
                });
                return {
                    input,
                    output,
                    ...subSchema,
                };
            }
            if (subSchema.properties ||
                subSchema.allOf ||
                subSchema.anyOf ||
                subSchema.additionalProperties) {
                // Nullable wrapper around a single named object: don't mint Title2; leave() reuses the member.
                if (subSchema.nullable &&
                    !subSchema.properties &&
                    !subSchema.allOf &&
                    ((Array.isArray(subSchema.anyOf) && subSchema.anyOf.length === 1) ||
                        (Array.isArray(subSchema.oneOf) && subSchema.oneOf.length === 1))) {
                    return {
                        ...subSchema,
                    };
                }
                if (subSchema.title === 'Any') {
                    const typeComposer = schemaComposer.getAnyTC(GraphQLJSON);
                    return {
                        input: typeComposer,
                        output: typeComposer,
                        description: subSchema.description,
                        nullable: subSchema.nullable,
                        readOnly: subSchema.readOnly,
                        writeOnly: subSchema.writeOnly,
                        default: subSchema.default,
                        deprecated: subSchema.deprecated,
                    };
                }
                const config = {
                    name: getValidTypeName({
                        schemaComposer,
                        isInput: false,
                        subSchema,
                    }),
                    description: subSchema.description,
                    fields: {},
                    directives: [],
                    extensions: {
                        default: subSchema.default,
                    },
                };
                if (subSchema.examples?.length) {
                    schemaComposer.addDirective(ExampleDirective);
                    for (const example of subSchema.examples) {
                        config.directives.push({
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: example,
                            },
                        });
                    }
                }
                if (subSchema.discriminator?.propertyName) {
                    schemaComposer.addDirective(DiscriminatorDirective);
                }
                const directives = [];
                if (subSchema.examples?.length) {
                    schemaComposer.addDirective(ExampleDirective);
                    for (const example of subSchema.examples) {
                        directives.push({
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: example,
                            },
                        });
                    }
                }
                return {
                    input: schemaComposer.createInputTC({
                        name: getValidTypeName({
                            schemaComposer,
                            isInput: true,
                            subSchema,
                        }),
                        description: subSchema.description,
                        fields: {},
                        directives,
                        extensions: {
                            default: subSchema.default,
                        },
                    }),
                    output: subSchema.discriminator
                        ? schemaComposer.createInterfaceTC(config)
                        : schemaComposer.createObjectTC(config),
                    ...subSchema,
                    ...(subSchema.properties ? { properties: { ...subSchema.properties } } : {}),
                    ...(subSchema.allOf ? { allOf: [...subSchema.allOf] } : {}),
                    ...(subSchema.additionalProperties
                        ? {
                            additionalProperties: subSchema.additionalProperties === true
                                ? true
                                : { ...subSchema.additionalProperties },
                        }
                        : {}),
                    ...(subSchema.discriminatorMapping
                        ? { discriminatorMapping: { ...subSchema.discriminatorMapping } }
                        : {}),
                };
            }
            return subSchema;
        },
        leave(subSchemaAndTypeComposers, { path }) {
            // const validateWithJSONSchema = getValidateFnForSchemaPath(ajv, path, schema);
            for (const unionKey of ['anyOf', 'oneOf']) {
                const members = subSchemaAndTypeComposers[unionKey];
                if (subSchemaAndTypeComposers.nullable &&
                    Array.isArray(members) &&
                    members.length === 1 &&
                    members[0]?.output &&
                    !subSchemaAndTypeComposers.properties &&
                    !subSchemaAndTypeComposers.allOf) {
                    return {
                        ...members[0],
                        nullable: true,
                        description: subSchemaAndTypeComposers.description || members[0].description,
                        readOnly: subSchemaAndTypeComposers.readOnly ?? members[0].readOnly,
                        writeOnly: subSchemaAndTypeComposers.writeOnly ?? members[0].writeOnly,
                        default: subSchemaAndTypeComposers.default ?? members[0].default,
                        deprecated: subSchemaAndTypeComposers.deprecated ?? members[0].deprecated,
                    };
                }
            }
            const subSchemaOnly = {
                ...subSchemaAndTypeComposers,
                input: undefined,
                output: undefined,
            };
            if (subSchemaOnly.discriminator?.propertyName) {
                schemaComposer.addDirective(DiscriminatorDirective);
                const discriminatorArgs = {
                    subgraph: subgraphName,
                    field: subSchemaOnly.discriminator.propertyName,
                };
                if (subSchemaOnly.discriminatorMapping) {
                    const mappingByName = {};
                    for (const discriminatorValue in subSchemaOnly.discriminatorMapping) {
                        const discType = subSchemaOnly.discriminatorMapping[discriminatorValue];
                        mappingByName[discriminatorValue] = discType.output.getTypeName();
                    }
                    discriminatorArgs.mapping = Object.entries(mappingByName);
                }
                subSchemaAndTypeComposers.output.setDirectiveByName('discriminator', discriminatorArgs);
            }
            if (subSchemaAndTypeComposers.oneOf && !subSchemaAndTypeComposers.properties) {
                const isPlural = subSchemaAndTypeComposers.oneOf.some(({ output }) => output instanceof ListComposer);
                if (isPlural) {
                    const { input, output, flatten } = getUnionTypeComposers({
                        subgraphName,
                        schemaComposer,
                        typeComposersList: subSchemaAndTypeComposers.oneOf.map(({ input, output }) => ({
                            input: input.ofType || input,
                            output: output.ofType || output,
                        })),
                        subSchemaAndTypeComposers,
                        logger,
                    });
                    return {
                        input: input.getTypePlural(),
                        output: output instanceof ListComposer
                            ? output
                            : output.getTypePlural(),
                        nullable: subSchemaAndTypeComposers.nullable,
                        default: subSchemaAndTypeComposers.default,
                        readOnly: subSchemaAndTypeComposers.readOnly,
                        writeOnly: subSchemaAndTypeComposers.writeOnly,
                        flatten,
                        deprecated: subSchemaAndTypeComposers.deprecated,
                    };
                }
                return getUnionTypeComposers({
                    subgraphName,
                    schemaComposer,
                    typeComposersList: subSchemaAndTypeComposers.oneOf,
                    subSchemaAndTypeComposers,
                    logger,
                });
            }
            const fieldMap = {};
            const inputFieldMap = {};
            let isList = false;
            if (subSchemaAndTypeComposers.allOf) {
                let ableToUseGraphQLInputObjectType = true;
                for (const maybeTypeComposers of subSchemaAndTypeComposers.allOf) {
                    let { input: inputTypeComposer, output: outputTypeComposer } = maybeTypeComposers;
                    if (inputTypeComposer instanceof ListComposer) {
                        isList = true;
                        inputTypeComposer = inputTypeComposer.ofType;
                    }
                    if (outputTypeComposer instanceof ListComposer) {
                        isList = true;
                        outputTypeComposer = outputTypeComposer.ofType;
                    }
                    if (inputTypeComposer instanceof ScalarTypeComposer ||
                        inputTypeComposer instanceof EnumTypeComposer) {
                        ableToUseGraphQLInputObjectType = false;
                    }
                    else {
                        const inputTypeElemFieldMap = inputTypeComposer.getFields();
                        for (const fieldName in inputTypeElemFieldMap) {
                            const newInputField = inputTypeElemFieldMap[fieldName];
                            const existingInputField = inputFieldMap[fieldName];
                            if (!existingInputField) {
                                inputFieldMap[fieldName] = newInputField;
                            }
                            else {
                                /*
                                  If the new field collides with an existing field:
                
                                    - If both the existing and the new field have an input type composer, combine their subfields
                                    - Otherwise, replace the existing field with the new one
                                */
                                const existingInputFieldUnwrappedTC = typeof existingInputField.type?.getUnwrappedTC === 'function'
                                    ? existingInputField.type.getUnwrappedTC()
                                    : undefined;
                                const newInputFieldUnwrappedTC = typeof newInputField.type?.getUnwrappedTC === 'function'
                                    ? newInputField.type.getUnwrappedTC()
                                    : undefined;
                                if (existingInputFieldUnwrappedTC instanceof InputTypeComposer &&
                                    newInputFieldUnwrappedTC instanceof InputTypeComposer) {
                                    deepMergeInputTypeComposerFields(existingInputFieldUnwrappedTC, newInputFieldUnwrappedTC);
                                }
                                else {
                                    inputFieldMap[fieldName] = newInputField;
                                }
                            }
                        }
                    }
                    if (isSomeInputTypeComposer(outputTypeComposer)) {
                        schemaComposer.addDirective(ResolveRootDirective);
                        fieldMap[outputTypeComposer.getTypeName()] = {
                            type: outputTypeComposer,
                            directives: [
                                {
                                    name: 'resolveRoot',
                                    args: {
                                        subgraph: subgraphName,
                                    },
                                },
                            ],
                        };
                    }
                    else if (outputTypeComposer instanceof UnionTypeComposer) {
                        const outputTCElems = outputTypeComposer.getTypes();
                        for (const outputTCElem of outputTCElems) {
                            const outputTypeElemFieldMap = outputTCElem.getFields();
                            for (const fieldName in outputTypeElemFieldMap) {
                                const field = outputTypeElemFieldMap[fieldName];
                                fieldMap[fieldName] = field;
                            }
                        }
                    }
                    else {
                        if (outputTypeComposer instanceof InterfaceTypeComposer) {
                            subSchemaAndTypeComposers.output.addInterface(outputTypeComposer);
                            schemaComposer.addSchemaMustHaveType(subSchemaAndTypeComposers.output);
                        }
                        const typeElemFieldMap = outputTypeComposer.getFields();
                        for (const fieldName in typeElemFieldMap) {
                            const newField = typeElemFieldMap[fieldName];
                            const existingField = fieldMap[fieldName];
                            if (!existingField) {
                                fieldMap[fieldName] = newField;
                            }
                            else {
                                /*
                                  If the new field collides with an existing field:
                
                                    - If both the existing and the new field have an object type composer, combine their subfields
                                    - Otherwise, replace the existing field with the new one
                                */
                                const existingFieldUnwrappedTC = typeof existingField.type?.getUnwrappedTC === 'function'
                                    ? existingField.type.getUnwrappedTC()
                                    : undefined;
                                const newFieldUnwrappedTC = typeof newField.type?.getUnwrappedTC === 'function'
                                    ? newField.type.getUnwrappedTC()
                                    : undefined;
                                const existingOTC = asObjectTypeComposer(existingFieldUnwrappedTC);
                                const newOTC = asObjectTypeComposer(newFieldUnwrappedTC);
                                if (existingOTC && newOTC) {
                                    deepMergeObjectTypeComposerFields(existingOTC, newOTC);
                                }
                                else {
                                    if (newFieldUnwrappedTC &&
                                        existingFieldUnwrappedTC &&
                                        !isUnspecificType(newFieldUnwrappedTC) &&
                                        isUnspecificType(existingFieldUnwrappedTC)) {
                                        continue;
                                    }
                                    fieldMap[fieldName] = newField;
                                }
                            }
                        }
                    }
                }
                if (subSchemaAndTypeComposers.examples?.length) {
                    schemaComposer.addDirective(ExampleDirective);
                    const directives = subSchemaAndTypeComposers.output.getDirectives() || [];
                    for (const example of subSchemaAndTypeComposers.examples) {
                        directives.push({
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: example,
                            },
                        });
                    }
                    subSchemaAndTypeComposers.output.setDirectives(directives);
                }
                subSchemaAndTypeComposers.output.addFields(fieldMap);
                subSchemaAndTypeComposers.output.setExtensions({
                    // validateWithJSONSchema,
                    default: subSchemaAndTypeComposers.default,
                });
                if (ableToUseGraphQLInputObjectType) {
                    subSchemaAndTypeComposers.input.addFields(inputFieldMap);
                    if (subSchemaAndTypeComposers.examples?.length) {
                        schemaComposer.addDirective(ExampleDirective);
                        const directives = subSchemaAndTypeComposers.input.getDirectives() || [];
                        for (const example of subSchemaAndTypeComposers.examples) {
                            directives.push({
                                name: 'example',
                                args: {
                                    subgraph: subgraphName,
                                    value: example,
                                },
                            });
                        }
                        subSchemaAndTypeComposers.input.setDirectives(directives);
                    }
                    subSchemaAndTypeComposers.input.setExtensions({
                        default: subSchemaAndTypeComposers.default,
                    });
                }
                else {
                    subSchemaAndTypeComposers.input = schemaComposer.getAnyTC(GraphQLJSON);
                }
            }
            if (subSchemaAndTypeComposers.anyOf) {
                // It should not have `required` because it is `anyOf` not `allOf`
                let ableToUseGraphQLInputObjectType = true;
                for (const typeComposers of subSchemaAndTypeComposers.anyOf) {
                    let { input: inputTypeComposer, output: outputTypeComposer } = typeComposers;
                    if (inputTypeComposer instanceof ListComposer ||
                        outputTypeComposer instanceof ListComposer) {
                        isList = true;
                        inputTypeComposer = inputTypeComposer.ofType;
                        outputTypeComposer = outputTypeComposer.ofType;
                    }
                    if (inputTypeComposer instanceof ScalarTypeComposer ||
                        inputTypeComposer instanceof EnumTypeComposer) {
                        ableToUseGraphQLInputObjectType = false;
                    }
                    else {
                        const inputTypeElemFieldMap = inputTypeComposer.getFields();
                        for (const fieldName in inputTypeElemFieldMap) {
                            // In case of conflict set it to JSON
                            // TODO: But instead we can convert that field into a oneOf of all possible types
                            if (inputFieldMap[fieldName]) {
                                let existingType = inputFieldMap[fieldName].type;
                                if (typeof existingType === 'function') {
                                    existingType = existingType();
                                }
                                let newType = inputTypeElemFieldMap[fieldName].type;
                                if (typeof newType === 'function') {
                                    newType = newType();
                                }
                                const newTypeName = newType.getTypeName().replace('!', '');
                                const existingTypeName = existingType.getTypeName().replace('!', '');
                                if (existingTypeName !== newTypeName) {
                                    if (newTypeName !== 'JSON') {
                                        inputFieldMap[fieldName] = {
                                            type: schemaComposer.getAnyTC(GraphQLJSON),
                                        };
                                    }
                                    if (existingTypeName === 'JSON') {
                                        const field = inputTypeElemFieldMap[fieldName];
                                        inputFieldMap[fieldName] = isNonNullType(field.type.getType())
                                            ? {
                                                ...field,
                                                type: () => field.type.ofType,
                                            }
                                            : field;
                                    }
                                }
                            }
                            else {
                                const field = inputTypeElemFieldMap[fieldName];
                                inputFieldMap[fieldName] = isNonNullType(field.type.getType())
                                    ? {
                                        ...field,
                                        type: () => field.type.ofType,
                                    }
                                    : field;
                            }
                        }
                    }
                    if (outputTypeComposer instanceof ScalarTypeComposer) {
                        const typeName = outputTypeComposer.getTypeName();
                        // In case of conflict set it to JSON
                        // TODO: But instead we can convert that field into a union of all possible types
                        if (fieldMap[typeName]) {
                            const existingTypeName = fieldMap[typeName]?.type?.getTypeName();
                            if (existingTypeName === 'JSON') {
                                schemaComposer.addDirective(ResolveRootDirective);
                                fieldMap[typeName] = {
                                    type: outputTypeComposer,
                                    directives: [
                                        {
                                            name: 'resolveRoot',
                                            args: {
                                                subgraph: subgraphName,
                                            },
                                        },
                                    ],
                                };
                            }
                            if (typeName !== 'JSON' && existingTypeName !== typeName) {
                                fieldMap[typeName] = {
                                    type: schemaComposer.getAnyTC(GraphQLJSON),
                                };
                            }
                        }
                        else {
                            schemaComposer.addDirective(ResolveRootDirective);
                            fieldMap[typeName] = {
                                type: outputTypeComposer,
                                directives: [
                                    {
                                        name: 'resolveRoot',
                                        args: {
                                            subgraph: subgraphName,
                                        },
                                    },
                                ],
                            };
                        }
                    }
                    else if (outputTypeComposer instanceof ObjectTypeComposer ||
                        outputTypeComposer instanceof InterfaceTypeComposer) {
                        const typeElemFieldMap = outputTypeComposer.getFields();
                        for (const fieldName in typeElemFieldMap) {
                            // In case of conflict set it to JSON
                            // TODO: But instead we can convert that field into a union of all possible types
                            const field = typeElemFieldMap[fieldName];
                            const existingField = fieldMap[fieldName];
                            fieldMap[fieldName] = {
                                ...field,
                                type: () => {
                                    if (!field.type) {
                                    }
                                    const fieldType = field.type.getType();
                                    const namedType = getNamedType(fieldType);
                                    if (existingField) {
                                        const existingFieldType = existingField.type();
                                        const existingNamedType = getNamedType(existingFieldType);
                                        const existingTypeName = existingNamedType.name;
                                        const newTypeName = namedType.name;
                                        if (existingTypeName !== 'JSON' && existingNamedType.name !== namedType.name) {
                                            return schemaComposer.getAnyTC(GraphQLJSON);
                                        }
                                        if (newTypeName === 'JSON') {
                                            return existingFieldType;
                                        }
                                    }
                                    return field.type.getType();
                                },
                            };
                        }
                    }
                }
                let outputTypeComposer = subSchemaAndTypeComposers.output;
                if ('ofType' in outputTypeComposer) {
                    outputTypeComposer = outputTypeComposer.ofType;
                }
                outputTypeComposer.addFields(fieldMap);
                if (subSchemaAndTypeComposers.examples?.length) {
                    schemaComposer.addDirective(ExampleDirective);
                    const directives = outputTypeComposer.getDirectives() || [];
                    for (const example of subSchemaAndTypeComposers.examples) {
                        directives.push({
                            name: 'example',
                            args: {
                                subgraph: subgraphName,
                                value: example,
                            },
                        });
                    }
                    outputTypeComposer.setDirectives(directives);
                }
                outputTypeComposer.setExtensions({
                    // validateWithJSONSchema,
                    default: subSchemaAndTypeComposers.default,
                });
                let inputTypeComposer = subSchemaAndTypeComposers.input;
                if ('ofType' in inputTypeComposer) {
                    inputTypeComposer = inputTypeComposer.ofType;
                }
                if (ableToUseGraphQLInputObjectType) {
                    inputTypeComposer.addFields(inputFieldMap);
                    if (subSchemaAndTypeComposers.examples?.length) {
                        schemaComposer.addDirective(ExampleDirective);
                        const directives = inputTypeComposer.getDirectives() || [];
                        for (const example of subSchemaAndTypeComposers.examples) {
                            directives.push({
                                name: 'example',
                                args: {
                                    subgraph: subgraphName,
                                    value: example,
                                },
                            });
                        }
                        inputTypeComposer.setDirectives(directives);
                    }
                    inputTypeComposer.setExtensions({
                        default: subSchemaAndTypeComposers.default,
                    });
                }
                else {
                    subSchemaAndTypeComposers.input = schemaComposer.getAnyTC(GraphQLJSON);
                }
            }
            switch (subSchemaAndTypeComposers.type) {
                case 'object':
                    if (subSchemaAndTypeComposers.properties) {
                        for (const propertyName in subSchemaAndTypeComposers.properties) {
                            // TODO: needs to be fixed
                            if (propertyName === 'additionalProperties') {
                                continue;
                            }
                            const fieldName = sanitizeNameForGraphQL(propertyName);
                            const fieldDirectives = [];
                            if (propertyName !== fieldName) {
                                schemaComposer.addDirective(ResolveRootFieldDirective);
                                fieldDirectives.push({
                                    name: 'resolveRootField',
                                    args: {
                                        subgraph: subgraphName,
                                        field: propertyName,
                                    },
                                });
                            }
                            if (subSchemaAndTypeComposers.properties[propertyName].flatten) {
                                schemaComposer.addDirective(FlattenDirective);
                                fieldDirectives.push({
                                    name: 'flatten',
                                    args: {
                                        subgraph: subgraphName,
                                    },
                                });
                            }
                            fieldMap[fieldName] = {
                                type: () => {
                                    const typeComposers = subSchemaAndTypeComposers.properties[propertyName];
                                    let nullable = true;
                                    if (subSchemaAndTypeComposers.required?.includes(propertyName)) {
                                        nullable = false;
                                    }
                                    // Nullable has more priority
                                    if (typeComposers.nullable === false) {
                                        nullable = false;
                                    }
                                    if (typeComposers.nullable === true) {
                                        nullable = true;
                                    }
                                    if (subSchemaAndTypeComposers.properties[propertyName].writeOnly) {
                                        nullable = true;
                                    }
                                    return !nullable ? typeComposers.output.getTypeNonNull() : typeComposers.output;
                                },
                                // Make sure you get the right property
                                directives: fieldDirectives,
                                extensions: {
                                    nullable: subSchemaAndTypeComposers.properties?.[propertyName]?.nullable,
                                },
                                description: subSchemaAndTypeComposers.properties[propertyName].description ||
                                    subSchemaAndTypeComposers.properties[propertyName].output?.description,
                                deprecationReason: subSchemaAndTypeComposers.properties[propertyName].deprecated
                                    ? 'deprecated'
                                    : undefined,
                            };
                            const directives = [];
                            if (fieldName !== propertyName) {
                                schemaComposer.addDirective(ResolveRootFieldDirective);
                                directives.push({
                                    name: 'resolveRootField',
                                    args: {
                                        subgraph: subgraphName,
                                        field: propertyName,
                                    },
                                });
                            }
                            inputFieldMap[fieldName] = {
                                type: () => {
                                    const typeComposers = subSchemaAndTypeComposers.properties[propertyName];
                                    let nullable = true;
                                    if (subSchemaAndTypeComposers.required?.includes(propertyName)) {
                                        nullable = false;
                                    }
                                    // Nullable has more priority
                                    if (typeComposers.nullable === false) {
                                        nullable = false;
                                    }
                                    if (typeComposers.nullable === true) {
                                        nullable = true;
                                    }
                                    if (subSchemaAndTypeComposers.properties[propertyName].readOnly) {
                                        nullable = true;
                                    }
                                    return !nullable ? typeComposers.input?.getTypeNonNull() : typeComposers.input;
                                },
                                extensions: {
                                    nullable: subSchemaAndTypeComposers.properties?.[propertyName]?.nullable,
                                },
                                directives,
                                description: subSchemaAndTypeComposers.properties[propertyName].description ||
                                    subSchemaAndTypeComposers.properties[propertyName].input?.description,
                                defaultValue: subSchemaAndTypeComposers.properties[propertyName]?.default ||
                                    subSchemaAndTypeComposers.properties[propertyName]?.extensions?.default ||
                                    subSchemaAndTypeComposers.properties[propertyName]?.input?.default,
                                // deprecationReason: subSchemaAndTypeComposers.properties[propertyName].deprecated ? 'deprecated' : undefined,
                            };
                        }
                    }
                    if (subSchemaAndTypeComposers.additionalProperties) {
                        // Take a look later
                        if (Object.keys(fieldMap).length > 0 &&
                            typeof subSchemaAndTypeComposers.additionalProperties === 'object' &&
                            subSchemaAndTypeComposers.additionalProperties.output instanceof ObjectTypeComposer) {
                            const containerOutputTC = schemaComposer.createObjectTC({
                                name: `${subSchemaAndTypeComposers.additionalProperties.output.getTypeName()}_entry`,
                                fields: {
                                    key: {
                                        type: 'ID!',
                                    },
                                    value: {
                                        type: subSchemaAndTypeComposers.additionalProperties.output,
                                    },
                                },
                            });
                            schemaComposer.addDirective(DictionaryDirective);
                            fieldMap.additionalProperties = {
                                type: containerOutputTC.List,
                                directives: [
                                    {
                                        name: 'dictionary',
                                        args: {
                                            subgraph: subgraphName,
                                        },
                                    },
                                ],
                            };
                        }
                        else if (Object.keys(fieldMap).length > 0) {
                            schemaComposer.addDirective(ResolveRootDirective);
                            fieldMap.additionalProperties = {
                                type: GraphQLJSON,
                                directives: [
                                    {
                                        name: 'resolveRoot',
                                        args: {
                                            subgraph: subgraphName,
                                        },
                                    },
                                ],
                            };
                        }
                        else {
                            const typeComposer = schemaComposer.getAnyTC(GraphQLJSON);
                            schemaComposer.delete(subSchemaAndTypeComposers.input?.getTypeName?.());
                            schemaComposer.delete(subSchemaAndTypeComposers.output?.getTypeName?.());
                            return {
                                input: typeComposer,
                                output: typeComposer,
                                description: subSchemaAndTypeComposers.description,
                                nullable: subSchemaAndTypeComposers.nullable,
                                default: subSchemaAndTypeComposers.default,
                                readOnly: subSchemaAndTypeComposers.readOnly,
                                writeOnly: subSchemaAndTypeComposers.writeOnly,
                                deprecated: subSchemaAndTypeComposers.deprecated,
                            };
                        }
                    }
                    if (subSchemaAndTypeComposers.title in rootInputTypeNameComposerMap) {
                        const typeComposer = rootInputTypeNameComposerMap[subSchemaAndTypeComposers.title]();
                        for (const fieldName in inputFieldMap) {
                            let inputTC = inputFieldMap[fieldName].type();
                            if ('ofType' in inputTC) {
                                inputTC = inputTC.ofType;
                            }
                            typeComposer.addFieldArgs(fieldName, inputTC.getFields());
                        }
                        return {
                            output: typeComposer,
                        };
                    }
                    let output = subSchemaAndTypeComposers.output;
                    if (Object.keys(fieldMap).length === 0) {
                        output = schemaComposer.getAnyTC(GraphQLJSON);
                    }
                    else if ('addFields' in output) {
                        if (subSchemaOnly.discriminatorMapping) {
                            for (const discriminatorValue in subSchemaOnly.discriminatorMapping) {
                                const discType = subSchemaOnly.discriminatorMapping[discriminatorValue];
                                discType.output.addFields(fieldMap);
                            }
                        }
                        output.addFields(fieldMap);
                        // TODO: Improve this later
                        for (const requiredFieldName of subSchemaAndTypeComposers.required || []) {
                            const sanitizedFieldName = sanitizeNameForGraphQL(requiredFieldName);
                            const fieldObj = output.getField(sanitizedFieldName);
                            if (!isNonNullType(fieldObj.type.getType()) && !fieldObj.extensions?.nullable) {
                                output.makeFieldNonNull(requiredFieldName);
                            }
                        }
                    }
                    let input = subSchemaAndTypeComposers.input;
                    if (Object.keys(inputFieldMap).length === 0) {
                        input = schemaComposer.getAnyTC(GraphQLJSON);
                    }
                    else if (input != null && 'addFields' in input) {
                        input.addFields(inputFieldMap);
                        // TODO: Improve this later
                        for (const requiredFieldName of subSchemaAndTypeComposers.required || []) {
                            const sanitizedFieldName = sanitizeNameForGraphQL(requiredFieldName);
                            const field = input.getField(sanitizedFieldName);
                            if (!isNonNullType(field.type.getType()) && !field.extensions?.nullable) {
                                input.makeFieldNonNull(requiredFieldName);
                            }
                        }
                    }
                    if (isList) {
                        input = input.List;
                        output = output.List;
                    }
                    return {
                        input,
                        output,
                        nullable: subSchemaAndTypeComposers.nullable,
                        default: subSchemaAndTypeComposers.default,
                        readOnly: subSchemaAndTypeComposers.readOnly,
                        writeOnly: subSchemaAndTypeComposers.writeOnly,
                        deprecated: subSchemaAndTypeComposers.deprecated,
                    };
            }
            if (subSchemaAndTypeComposers.input || subSchemaAndTypeComposers.output) {
                return {
                    input: subSchemaAndTypeComposers.input,
                    output: subSchemaAndTypeComposers.output,
                    description: subSchemaAndTypeComposers.description,
                    nullable: subSchemaAndTypeComposers.nullable,
                    default: subSchemaAndTypeComposers.default,
                    readOnly: subSchemaAndTypeComposers.readOnly,
                    writeOnly: subSchemaAndTypeComposers.writeOnly,
                    deprecated: subSchemaAndTypeComposers.deprecated,
                };
            }
            else {
                logger.debug(`GraphQL Type cannot be created for this JSON Schema definition;`, {
                    subSchema: subSchemaOnly,
                    path,
                });
                const typeComposer = schemaComposer.getAnyTC(GraphQLJSON);
                return {
                    input: typeComposer,
                    output: typeComposer,
                    description: subSchemaAndTypeComposers.description,
                    nullable: subSchemaAndTypeComposers.nullable,
                    readOnly: subSchemaAndTypeComposers.readOnly,
                    writeOnly: subSchemaAndTypeComposers.writeOnly,
                    default: subSchemaAndTypeComposers.default,
                    deprecated: subSchemaAndTypeComposers.deprecated,
                };
            }
        },
    });
}
const specifiedTypeNames = ['String', 'Int', 'Float', 'Boolean', 'ID', 'JSON', 'Void'];
function isUnspecificType(typeComposer) {
    const tc = typeComposer.getTypeName();
    return !specifiedTypeNames.includes(tc);
}