"use strict";
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaObjectFactory = void 0;
const common_1 = require("@nestjs/common");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const lodash_1 = require("lodash");
const constants_1 = require("../constants");
const helpers_1 = require("../decorators/helpers");
const api_extra_models_explorer_1 = require("../explorers/api-extra-models.explorer");
const utils_1 = require("../utils");
const enum_utils_1 = require("../utils/enum.utils");
const is_body_parameter_util_1 = require("../utils/is-body-parameter.util");
const is_built_in_type_util_1 = require("../utils/is-built-in-type.util");
const is_date_ctor_util_1 = require("../utils/is-date-ctor.util");
class SchemaObjectFactory {
    constructor(modelPropertiesAccessor, swaggerTypesMapper) {
        this.modelPropertiesAccessor = modelPropertiesAccessor;
        this.swaggerTypesMapper = swaggerTypesMapper;
    }
    createFromModel(parameters, schemas) {
        const parameterObjects = parameters.map((param) => {
            if (this.isLazyTypeFunc(param.type)) {
                [param.type, param.isArray] = (0, helpers_1.getTypeIsArrayTuple)(param.type(), undefined);
            }
            if (!(0, is_body_parameter_util_1.isBodyParameter)(param) && param.enumName) {
                return this.createEnumParam(param, schemas);
            }
            if (this.isPrimitiveType(param.type)) {
                return param;
            }
            if (this.isArrayCtor(param.type)) {
                return this.mapArrayCtorParam(param);
            }
            if (!(0, is_body_parameter_util_1.isBodyParameter)(param)) {
                return this.createQueryOrParamSchema(param, schemas);
            }
            return this.getCustomType(param, schemas);
        });
        return (0, lodash_1.flatten)(parameterObjects);
    }
    getCustomType(param, schemas) {
        const modelName = this.exploreModelSchema(param.type, schemas);
        const name = param.name || modelName;
        const schema = Object.assign(Object.assign({}, (param.schema || {})), { $ref: (0, utils_1.getSchemaPath)(modelName) });
        const isArray = param.isArray;
        param = (0, lodash_1.omit)(param, 'isArray');
        if (isArray) {
            return Object.assign(Object.assign({}, param), { name, schema: {
                    type: 'array',
                    items: schema
                } });
        }
        return Object.assign(Object.assign({}, param), { name,
            schema });
    }
    createQueryOrParamSchema(param, schemas) {
        if ((0, is_date_ctor_util_1.isDateCtor)(param.type)) {
            return Object.assign(Object.assign({ format: 'date-time' }, param), { type: 'string' });
        }
        if (this.isBigInt(param.type)) {
            return Object.assign(Object.assign({ format: 'int64' }, param), { type: 'integer' });
        }
        if ((0, lodash_1.isFunction)(param.type)) {
            if (param.name) {
                const customType = this.getCustomType(param, schemas);
                const schemaOptionsKeys = [
                    ...this.swaggerTypesMapper.getSchemaOptionsKeys(),
                    'allOf'
                ];
                const schemaOptionsFromParam = {};
                for (const key of schemaOptionsKeys) {
                    if (key === 'type' || key === 'items') {
                        continue;
                    }
                    if (key in customType && !(key in (customType.schema || {}))) {
                        schemaOptionsFromParam[key] = customType[key];
                        delete customType[key];
                    }
                }
                if (Object.keys(schemaOptionsFromParam).length > 0) {
                    const existingSchema = (customType.schema || {});
                    if ('$ref' in existingSchema) {
                        const { $ref, allOf: existingAllOf } = existingSchema, restSchema = __rest(existingSchema, ["$ref", "allOf"]);
                        const { allOf: paramAllOf } = schemaOptionsFromParam, restParamOptions = __rest(schemaOptionsFromParam, ["allOf"]);
                        const mergedAllOf = [
                            ...(Array.isArray(existingAllOf) ? existingAllOf : []),
                            ...(Array.isArray(paramAllOf) ? paramAllOf : []),
                            { $ref: $ref }
                        ];
                        customType.schema = Object.assign(Object.assign(Object.assign({}, restSchema), restParamOptions), { allOf: mergedAllOf });
                    }
                    else {
                        const mergedSchema = Object.assign(Object.assign({}, existingSchema), schemaOptionsFromParam);
                        const existingAllOf = existingSchema.allOf;
                        const paramAllOf = schemaOptionsFromParam.allOf;
                        if (Array.isArray(existingAllOf) && Array.isArray(paramAllOf)) {
                            mergedSchema.allOf = [...existingAllOf, ...paramAllOf];
                        }
                        customType.schema = mergedSchema;
                    }
                }
                return customType;
            }
            const propertiesWithType = this.extractPropertiesFromType(param.type, schemas);
            if (!propertiesWithType) {
                return param;
            }
            return propertiesWithType.map((property) => {
                const keysToOmit = [
                    'isArray',
                    'enumName',
                    'enumSchema',
                    'selfRequired'
                ];
                const parameterObject = Object.assign(Object.assign({}, (0, lodash_1.omit)(property, keysToOmit)), { in: 'query', required: 'selfRequired' in property
                        ? property.selfRequired
                        : typeof property.required === 'boolean'
                            ? property.required
                            : true });
                const keysToMoveToSchema = [
                    ...this.swaggerTypesMapper.getSchemaOptionsKeys(),
                    'allOf'
                ];
                return keysToMoveToSchema.reduce((acc, key) => {
                    if (key in property) {
                        acc.schema = Object.assign(Object.assign({}, acc.schema), { [key]: property[key] });
                        delete acc[key];
                    }
                    return acc;
                }, parameterObject);
            });
        }
        if (this.isConstEnumObject(param.type)) {
            const enumValues = (0, enum_utils_1.getEnumValues)(param.type);
            const enumType = (0, enum_utils_1.getEnumType)(enumValues);
            return Object.assign(Object.assign({}, param), { schema: {
                    type: enumType,
                    enum: enumValues
                }, selfRequired: param.required });
        }
        if (this.isObjectLiteral(param.type)) {
            const schemaFromObjectLiteral = this.createFromObjectLiteral(param.name, param.type, schemas);
            if (param.isArray) {
                return Object.assign(Object.assign({}, param), { schema: {
                        type: 'array',
                        items: (0, lodash_1.omit)(schemaFromObjectLiteral, 'name')
                    }, selfRequired: param.required });
            }
            return Object.assign(Object.assign({}, param), { schema: {
                    type: schemaFromObjectLiteral.type,
                    properties: schemaFromObjectLiteral.properties,
                    required: schemaFromObjectLiteral.required
                }, selfRequired: param.required });
        }
        return param;
    }
    extractPropertiesFromType(type, schemas, pendingSchemasRefs = []) {
        const { prototype } = type;
        if (!prototype) {
            return;
        }
        const extraModels = (0, api_extra_models_explorer_1.exploreGlobalApiExtraModelsMetadata)(type);
        extraModels.forEach((item) => this.exploreModelSchema(item, schemas, pendingSchemasRefs));
        this.modelPropertiesAccessor.applyMetadataFactory(prototype);
        const modelProperties = this.modelPropertiesAccessor.getModelProperties(prototype);
        const propertiesWithType = modelProperties.map((key) => {
            let property;
            try {
                property = this.mergePropertyWithMetadata(key, prototype, schemas, pendingSchemasRefs);
            }
            catch (err) {
                if (err instanceof Error) {
                    const className = (type === null || type === void 0 ? void 0 : type.name) || 'UnknownType';
                    const prefix = `[${className}] `;
                    if (!err.message.startsWith(prefix)) {
                        err.message = `${prefix}${err.message}`;
                    }
                }
                throw err;
            }
            const schemaCombinators = ['oneOf', 'anyOf', 'allOf'];
            const declaredSchemaCombinator = schemaCombinators.find((combinator) => combinator in property);
            if (declaredSchemaCombinator) {
                const reflectedPropertyMetadata = Reflect.getMetadata(constants_1.DECORATORS.API_MODEL_PROPERTIES, prototype, key);
                const hasDeclaredSchemaCombinator = schemaCombinators.some((combinator) => combinator in (reflectedPropertyMetadata || {}));
                const schemaObjectMetadata = property;
                if ((schemaObjectMetadata === null || schemaObjectMetadata === void 0 ? void 0 : schemaObjectMetadata.type) === 'array' ||
                    schemaObjectMetadata.isArray) {
                    schemaObjectMetadata.items = {};
                    schemaObjectMetadata.items[declaredSchemaCombinator] =
                        property[declaredSchemaCombinator];
                    delete property[declaredSchemaCombinator];
                }
                else if (hasDeclaredSchemaCombinator) {
                    delete schemaObjectMetadata.type;
                }
            }
            return property;
        });
        return propertiesWithType;
    }
    exploreModelSchema(type, schemas, pendingSchemasRefs = []) {
        if (this.isLazyTypeFunc(type)) {
            type = type();
        }
        const propertiesWithType = this.extractPropertiesFromType(type, schemas, pendingSchemasRefs);
        if (!propertiesWithType) {
            return '';
        }
        const extensionProperties = Reflect.getMetadata(constants_1.DECORATORS.API_EXTENSION, type) || {};
        const { schemaName, schemaProperties } = this.getSchemaMetadata(type);
        const typeDefinition = Object.assign(Object.assign({ type: 'object', properties: (0, lodash_1.mapValues)((0, lodash_1.keyBy)(propertiesWithType, 'name'), (property) => {
                const keysToOmit = [
                    'name',
                    'isArray',
                    'enumName',
                    'enumSchema',
                    'selfRequired'
                ];
                if ('required' in property && Array.isArray(property.required)) {
                    return (0, lodash_1.omit)(property, keysToOmit);
                }
                return (0, lodash_1.omit)(property, [...keysToOmit, 'required']);
            }) }, extensionProperties), schemaProperties);
        const typeDefinitionRequiredFields = propertiesWithType
            .filter((property) => 'selfRequired' in property
            ? property.selfRequired != false
            : property.required != false && !Array.isArray(property.required))
            .map((property) => property.name);
        if (typeDefinitionRequiredFields.length > 0) {
            typeDefinition['required'] = typeDefinitionRequiredFields;
        }
        if (schemas[schemaName] && !(0, lodash_1.isEqual)(schemas[schemaName], typeDefinition)) {
            common_1.Logger.warn(`Duplicate DTO detected: "${schemaName}" is defined multiple times with different schemas.\n` +
                `Consider using unique class names or applying @ApiExtraModels() decorator with custom schema names.\n` +
                `Note: This will throw an error in the next major version.`);
        }
        schemas[schemaName] = typeDefinition;
        return schemaName;
    }
    getSchemaMetadata(type) {
        var _a, _b;
        const schemas = (_a = Reflect.getOwnMetadata(constants_1.DECORATORS.API_SCHEMA, type)) !== null && _a !== void 0 ? _a : [];
        const _c = (_b = schemas[schemas.length - 1]) !== null && _b !== void 0 ? _b : {}, { name } = _c, schemaProperties = __rest(_c, ["name"]);
        return { schemaName: name !== null && name !== void 0 ? name : type.name, schemaProperties };
    }
    mergePropertyWithMetadata(key, prototype, schemas, pendingSchemaRefs, metadata) {
        if (!metadata) {
            metadata =
                (0, lodash_1.omit)(Reflect.getMetadata(constants_1.DECORATORS.API_MODEL_PROPERTIES, prototype, key), 'link') || {};
        }
        if (this.isLazyTypeFunc(metadata.type)) {
            metadata.type = metadata.type();
            [metadata.type, metadata.isArray] = (0, helpers_1.getTypeIsArrayTuple)(metadata.type, metadata.isArray);
        }
        if (Array.isArray(metadata.type)) {
            return this.createFromNestedArray(key, metadata, schemas, pendingSchemaRefs);
        }
        return this.createSchemaMetadata(key, metadata, schemas, pendingSchemaRefs);
    }
    createEnumParam(param, schemas) {
        var _a, _b, _c, _d, _e;
        const enumName = param.enumName;
        const $ref = (0, utils_1.getSchemaPath)(enumName);
        if (!(enumName in schemas)) {
            const _enum = param.enum
                ? param.enum
                : param.schema
                    ? param.schema['items']
                        ? param.schema['items']['enum']
                        : param.schema['enum']
                    : param.isArray && param.items
                        ? param.items.enum
                        : undefined;
            schemas[enumName] = Object.assign(Object.assign({ type: (_d = (param.isArray
                    ? (_b = (_a = param.schema) === null || _a === void 0 ? void 0 : _a['items']) === null || _b === void 0 ? void 0 : _b['type']
                    : (_c = param.schema) === null || _c === void 0 ? void 0 : _c['type'])) !== null && _d !== void 0 ? _d : 'string', enum: _enum }, param.enumSchema), (param['x-enumNames'] ? { 'x-enumNames': param['x-enumNames'] } : {}));
        }
        else {
            if (param.enumSchema) {
                schemas[enumName] = Object.assign(Object.assign({}, schemas[enumName]), param.enumSchema);
            }
        }
        const newSchema = param.isArray || ((_e = param.schema) === null || _e === void 0 ? void 0 : _e['items'])
            ? { type: 'array', items: { $ref } }
            : { $ref };
        return (0, lodash_1.omit)(Object.assign(Object.assign({}, param), { schema: newSchema }), ['isArray', 'items', 'enumName', 'enum', 'x-enumNames', 'enumSchema']);
    }
    createEnumSchemaType(key, metadata, schemas) {
        var _a, _b, _c;
        if (!('enumName' in metadata) || !metadata.enumName) {
            return Object.assign(Object.assign({}, metadata), { name: metadata.name || key });
        }
        const enumName = metadata.enumName;
        const $ref = (0, utils_1.getSchemaPath)(enumName);
        const enumType = (_a = (metadata.isArray ? metadata.items['type'] : metadata.type)) !== null && _a !== void 0 ? _a : 'string';
        if (!schemas[enumName]) {
            schemas[enumName] = Object.assign(Object.assign({ type: enumType }, metadata.enumSchema), { enum: metadata.isArray && metadata.items
                    ? metadata.items['enum']
                    : metadata.enum, description: (_b = metadata.description) !== null && _b !== void 0 ? _b : undefined, 'x-enumNames': (_c = metadata['x-enumNames']) !== null && _c !== void 0 ? _c : undefined });
        }
        else {
            if (metadata.enumSchema) {
                schemas[enumName] = Object.assign(Object.assign({}, schemas[enumName]), metadata.enumSchema);
            }
            if (metadata['x-enumNames']) {
                schemas[enumName]['x-enumNames'] = metadata['x-enumNames'];
            }
        }
        const _schemaObject = Object.assign(Object.assign({}, metadata), { name: metadata.name || key, type: metadata.isArray ? 'array' : 'string' });
        const existingCombinator = ['oneOf', 'anyOf'].find((key) => key in metadata && Array.isArray(metadata[key]));
        const refHost = metadata.isArray
            ? { items: { $ref } }
            : existingCombinator
                ? { [existingCombinator]: [...metadata[existingCombinator], { $ref }] }
                : { allOf: [{ $ref }] };
        const paramObject = Object.assign(Object.assign({}, _schemaObject), refHost);
        const pathsToOmit = ['enum', 'enumName', 'enumSchema', 'x-enumNames'];
        if (!metadata.isArray) {
            pathsToOmit.push('type');
        }
        return (0, lodash_1.omit)(paramObject, pathsToOmit);
    }
    createNotBuiltInTypeReference(key, metadata, trueMetadataType, schemas, pendingSchemaRefs) {
        if ((0, shared_utils_1.isUndefined)(trueMetadataType)) {
            const errorIn = (pendingSchemaRefs === null || pendingSchemaRefs === void 0 ? void 0 : pendingSchemaRefs.length) > 0
                ? `in "${pendingSchemaRefs[pendingSchemaRefs.length - 1]}" `
                : '';
            throw new Error(`A circular dependency has been detected ${errorIn}(property key: "${key}"). To resolve this, use a lazy resolver for the property type ("type: () => ClassType") on each side of the relationship, or break the cycle by introducing a reference via @ApiExtraModels.`);
        }
        let { schemaName: schemaObjectName } = this.getSchemaMetadata(trueMetadataType);
        if (!(schemaObjectName in schemas) &&
            !pendingSchemaRefs.includes(schemaObjectName)) {
            schemaObjectName = this.exploreModelSchema(trueMetadataType, schemas, [...pendingSchemaRefs, schemaObjectName]);
        }
        const $ref = (0, utils_1.getSchemaPath)(schemaObjectName);
        if (metadata.isArray) {
            return this.transformToArraySchemaProperty(metadata, key, { $ref });
        }
        const keysToRemove = ['type', 'isArray', 'required', 'name'];
        const validMetadataObject = (0, lodash_1.omit)(metadata, keysToRemove);
        const extraMetadataKeys = Object.keys(validMetadataObject);
        if (extraMetadataKeys.length > 0) {
            return Object.assign(Object.assign(Object.assign({ name: metadata.name || key, required: metadata.required }, validMetadataObject), (validMetadataObject['nullable'] ? { type: 'object' } : {})), { allOf: [{ $ref }] });
        }
        return {
            name: metadata.name || key,
            required: metadata.required,
            $ref
        };
    }
    transformToArraySchemaProperty(metadata, key, type) {
        const keysToRemove = ['type', 'enum'];
        const [movedProperties, keysToMove] = this.extractPropertyModifiers(metadata);
        const schemaHost = Object.assign(Object.assign({}, (0, lodash_1.omit)(metadata, [...keysToRemove, ...keysToMove])), { name: metadata.name || key, type: 'array', items: metadata.items
                ? Object.assign(Object.assign({}, metadata.items), movedProperties) : (0, lodash_1.isString)(type)
                ? Object.assign({ type }, movedProperties) : Object.assign(Object.assign({}, type), movedProperties) });
        schemaHost.items = (0, lodash_1.omitBy)(schemaHost.items, shared_utils_1.isUndefined);
        return schemaHost;
    }
    mapArrayCtorParam(param) {
        return Object.assign(Object.assign({}, (0, lodash_1.omit)(param, 'type')), { schema: {
                type: 'array',
                items: {
                    type: 'string'
                }
            } });
    }
    createFromObjectLiteral(key, literalObj, schemas) {
        const objLiteralKeys = Object.keys(literalObj);
        const properties = {};
        const required = [];
        objLiteralKeys.forEach((key) => {
            var _a, _b;
            const propertyCompilerMetadata = literalObj[key];
            if ((0, enum_utils_1.isEnumArray)(propertyCompilerMetadata)) {
                propertyCompilerMetadata.type = 'array';
                const enumValues = (0, enum_utils_1.getEnumValues)(propertyCompilerMetadata.enum);
                propertyCompilerMetadata.items = {
                    type: (_b = (_a = propertyCompilerMetadata.items) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : (0, enum_utils_1.getEnumType)(enumValues),
                    enum: enumValues
                };
                delete propertyCompilerMetadata.enum;
            }
            else if (propertyCompilerMetadata.enum) {
                const enumValues = (0, enum_utils_1.getEnumValues)(propertyCompilerMetadata.enum);
                propertyCompilerMetadata.enum = enumValues;
                if (!propertyCompilerMetadata.type) {
                    propertyCompilerMetadata.type = (0, enum_utils_1.getEnumType)(enumValues);
                }
            }
            const propertyMetadata = this.mergePropertyWithMetadata(key, Object, schemas, [], propertyCompilerMetadata);
            if ('required' in propertyMetadata && propertyMetadata.required) {
                required.push(key);
            }
            const keysToRemove = ['isArray', 'name', 'required'];
            const validMetadataObject = (0, lodash_1.omit)(propertyMetadata, keysToRemove);
            properties[key] = validMetadataObject;
        });
        const schema = {
            name: key,
            type: 'object',
            properties,
            required
        };
        return schema;
    }
    createFromNestedArray(key, metadata, schemas, pendingSchemaRefs) {
        const recurse = (type) => {
            if (!Array.isArray(type)) {
                const schemaMetadata = this.createSchemaMetadata(key, metadata, schemas, pendingSchemaRefs, type);
                return (0, lodash_1.omit)(schemaMetadata, ['isArray', 'name']);
            }
            return {
                name: key,
                type: 'array',
                items: recurse(type[0])
            };
        };
        return recurse(metadata.type);
    }
    createSchemaMetadata(key, metadata, schemas, pendingSchemaRefs, nestedArrayType) {
        const typeRef = nestedArrayType || metadata.type;
        if (metadata.enum && typeRef === Object) {
            const enumValues = (0, enum_utils_1.getEnumValues)(metadata.enum);
            const enumType = (0, enum_utils_1.getEnumType)(enumValues);
            if (metadata.isArray) {
                return this.transformToArraySchemaProperty(Object.assign(Object.assign({}, metadata), { items: {
                        type: enumType,
                        enum: enumValues
                    } }), key, { type: enumType, enum: enumValues });
            }
            return this.createSchemaMetadata(key, Object.assign(Object.assign({}, metadata), { type: enumType, enum: enumValues }), schemas, pendingSchemaRefs, enumType);
        }
        if (this.isConstEnumObject(typeRef)) {
            const enumValues = (0, enum_utils_1.getEnumValues)(typeRef);
            const enumType = (0, enum_utils_1.getEnumType)(enumValues);
            const syntheticMetadata = Object.assign(Object.assign({}, metadata), { type: enumType, enum: enumValues });
            return this.createSchemaMetadata(key, syntheticMetadata, schemas, pendingSchemaRefs, enumType);
        }
        if (this.isObjectLiteral(typeRef)) {
            const schemaFromObjectLiteral = this.createFromObjectLiteral(key, typeRef, schemas);
            if (metadata.isArray) {
                return {
                    name: schemaFromObjectLiteral.name,
                    type: 'array',
                    items: (0, lodash_1.omit)(schemaFromObjectLiteral, 'name'),
                    selfRequired: metadata.required
                };
            }
            return Object.assign(Object.assign({}, schemaFromObjectLiteral), { selfRequired: metadata.required });
        }
        if ((0, lodash_1.isString)(typeRef)) {
            if ((0, enum_utils_1.isEnumMetadata)(metadata)) {
                return this.createEnumSchemaType(key, metadata, schemas);
            }
            if (metadata.isArray) {
                return this.transformToArraySchemaProperty(metadata, key, typeRef);
            }
            return Object.assign(Object.assign({}, metadata), { name: metadata.name || key });
        }
        if ((0, is_date_ctor_util_1.isDateCtor)(typeRef)) {
            if (metadata.isArray) {
                return this.transformToArraySchemaProperty(metadata, key, {
                    format: metadata.format || 'date-time',
                    type: 'string'
                });
            }
            return Object.assign(Object.assign({ format: 'date-time' }, metadata), { type: 'string', name: metadata.name || key });
        }
        if (this.isBigInt(typeRef)) {
            return Object.assign(Object.assign({ format: 'int64' }, metadata), { type: 'integer', name: metadata.name || key });
        }
        if (!(0, is_built_in_type_util_1.isBuiltInType)(typeRef)) {
            return this.createNotBuiltInTypeReference(key, metadata, typeRef, schemas, pendingSchemaRefs);
        }
        const typeName = this.getTypeName(typeRef);
        const itemType = this.swaggerTypesMapper.mapTypeToOpenAPIType(typeName);
        if (metadata.isArray) {
            return this.transformToArraySchemaProperty(metadata, key, {
                type: itemType
            });
        }
        else if (itemType === 'array') {
            const defaultOnArray = 'string';
            const hasSchemaCombinator = ['oneOf', 'anyOf', 'allOf'].some((combinator) => combinator in metadata);
            if (hasSchemaCombinator) {
                return Object.assign(Object.assign({}, metadata), { type: undefined, name: metadata.name || key });
            }
            return this.transformToArraySchemaProperty(metadata, key, {
                type: defaultOnArray
            });
        }
        return Object.assign(Object.assign({}, metadata), { name: metadata.name || key, type: itemType });
    }
    isArrayCtor(type) {
        return type === Array;
    }
    isPrimitiveType(type) {
        return ((0, lodash_1.isFunction)(type) &&
            [String, Boolean, Number].some((item) => item === type));
    }
    isLazyTypeFunc(type) {
        return (0, lodash_1.isFunction)(type) && type.name == 'type';
    }
    getTypeName(type) {
        return type && (0, lodash_1.isFunction)(type) ? type.name : type;
    }
    isObjectLiteral(obj) {
        if (typeof obj !== 'object' || !obj) {
            return false;
        }
        const hasOwnProp = Object.prototype.hasOwnProperty;
        let objPrototype = obj;
        while (Object.getPrototypeOf((objPrototype = Object.getPrototypeOf(objPrototype))) !== null)
            ;
        for (const prop in obj) {
            if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(objPrototype, prop)) {
                return false;
            }
        }
        return Object.getPrototypeOf(obj) === objPrototype;
    }
    isBigInt(type) {
        return type === BigInt;
    }
    isConstEnumObject(obj) {
        if (typeof obj !== 'object' || !obj || Array.isArray(obj)) {
            return false;
        }
        const values = Object.values(obj);
        if (values.length === 0) {
            return false;
        }
        return values.every((value) => typeof value === 'string' || typeof value === 'number');
    }
    extractPropertyModifiers(metadata) {
        const modifierKeys = [
            'format',
            'maximum',
            'maxLength',
            'minimum',
            'minLength',
            'pattern'
        ];
        return [(0, lodash_1.pick)(metadata, modifierKeys), modifierKeys];
    }
}
exports.SchemaObjectFactory = SchemaObjectFactory;
