UNPKG

class-validator

Version:

Decorator-based property validation for classes.

985 lines (967 loc) 490 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ClassValidator = {})); })(this, (function (exports) { 'use strict'; /** * This metadata contains validation rules. */ var ValidationMetadata = /** @class */ (function () { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- function ValidationMetadata(args) { /** * Validation groups used for this validation. */ this.groups = []; /** * Specifies if validated value is an array and each of its item must be validated. */ this.each = false; /* * A transient set of data passed through to the validation result for response mapping */ this.context = undefined; this.type = args.type; this.target = args.target; this.propertyName = args.propertyName; this.constraints = args.constraints; this.constraintCls = args.constraintCls; this.validationTypeOptions = args.validationTypeOptions; if (args.validationOptions) { this.message = args.validationOptions.message; this.groups = args.validationOptions.groups; this.always = args.validationOptions.always; this.each = args.validationOptions.each; this.context = args.validationOptions.context; } } return ValidationMetadata; }()); /** * Used to transform validation schemas to validation metadatas. */ var ValidationSchemaToMetadataTransformer = /** @class */ (function () { function ValidationSchemaToMetadataTransformer() { } ValidationSchemaToMetadataTransformer.prototype.transform = function (schema) { var metadatas = []; Object.keys(schema.properties).forEach(function (property) { schema.properties[property].forEach(function (validation) { var validationOptions = { message: validation.message, groups: validation.groups, always: validation.always, each: validation.each, }; var args = { type: validation.type, target: schema.name, propertyName: property, constraints: validation.constraints, validationTypeOptions: validation.options, validationOptions: validationOptions, }; metadatas.push(new ValidationMetadata(args)); }); }); return metadatas; }; return ValidationSchemaToMetadataTransformer; }()); /** * Convert Map, Set to Array */ function convertToArray(val) { if (val instanceof Map) { return Array.from(val.values()); } return Array.isArray(val) ? val : Array.from(val); } /** * This function returns the global object across Node and browsers. * * Note: `globalThis` is the standardized approach however it has been added to * Node.js in version 12. We need to include this snippet until Node 12 EOL. */ function getGlobal() { if (typeof globalThis !== 'undefined') { return globalThis; } if (typeof global !== 'undefined') { return global; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Cannot find name 'window'. if (typeof window !== 'undefined') { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Cannot find name 'window'. return window; } // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Cannot find name 'self'. if (typeof self !== 'undefined') { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: Cannot find name 'self'. return self; } } // https://github.com/TylorS/typed-is-promise/blob/abf1514e1b6961adfc75765476b0debb96b2c3ae/src/index.ts function isPromise(p) { return p !== null && typeof p === 'object' && typeof p.then === 'function'; } /** * Storage all metadatas. */ var MetadataStorage = /** @class */ (function () { function MetadataStorage() { // ------------------------------------------------------------------------- // Private properties // ------------------------------------------------------------------------- this.validationMetadatas = []; this.constraintMetadatas = []; } Object.defineProperty(MetadataStorage.prototype, "hasValidationMetaData", { get: function () { return !!this.validationMetadatas.length; }, enumerable: false, configurable: true }); // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Adds a new validation metadata. */ MetadataStorage.prototype.addValidationSchema = function (schema) { var _this = this; var validationMetadatas = new ValidationSchemaToMetadataTransformer().transform(schema); validationMetadatas.forEach(function (validationMetadata) { return _this.addValidationMetadata(validationMetadata); }); }; /** * Adds a new validation metadata. */ MetadataStorage.prototype.addValidationMetadata = function (metadata) { this.validationMetadatas.push(metadata); }; /** * Adds a new constraint metadata. */ MetadataStorage.prototype.addConstraintMetadata = function (metadata) { this.constraintMetadatas.push(metadata); }; /** * Groups metadata by their property names. */ MetadataStorage.prototype.groupByPropertyName = function (metadata) { var grouped = {}; metadata.forEach(function (metadata) { if (!grouped[metadata.propertyName]) grouped[metadata.propertyName] = []; grouped[metadata.propertyName].push(metadata); }); return grouped; }; /** * Gets all validation metadatas for the given object with the given groups. */ MetadataStorage.prototype.getTargetValidationMetadatas = function (targetConstructor, targetSchema, always, strictGroups, groups) { var includeMetadataBecauseOfAlwaysOption = function (metadata) { // `metadata.always` overrides global default. if (typeof metadata.always !== 'undefined') return metadata.always; // `metadata.groups` overrides global default. if (metadata.groups && metadata.groups.length) return false; // Use global default. return always; }; var excludeMetadataBecauseOfStrictGroupsOption = function (metadata) { if (strictGroups) { // Validation is not using groups. if (!groups || !groups.length) { // `metadata.groups` has at least one group. if (metadata.groups && metadata.groups.length) return true; } } return false; }; // get directly related to a target metadatas var originalMetadatas = this.validationMetadatas.filter(function (metadata) { if (metadata.target !== targetConstructor && metadata.target !== targetSchema) return false; if (includeMetadataBecauseOfAlwaysOption(metadata)) return true; if (excludeMetadataBecauseOfStrictGroupsOption(metadata)) return false; if (groups && groups.length > 0) return metadata.groups && !!metadata.groups.find(function (group) { return groups.indexOf(group) !== -1; }); return true; }); // get metadatas for inherited classes var inheritedMetadatas = this.validationMetadatas.filter(function (metadata) { // if target is a string it's means we validate against a schema, and there is no inheritance support for schemas if (typeof metadata.target === 'string') return false; if (metadata.target === targetConstructor) return false; if (metadata.target instanceof Function && !(targetConstructor.prototype instanceof metadata.target)) return false; if (includeMetadataBecauseOfAlwaysOption(metadata)) return true; if (excludeMetadataBecauseOfStrictGroupsOption(metadata)) return false; if (groups && groups.length > 0) return metadata.groups && !!metadata.groups.find(function (group) { return groups.indexOf(group) !== -1; }); return true; }); // filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas var uniqueInheritedMetadatas = inheritedMetadatas.filter(function (inheritedMetadata) { return !originalMetadatas.find(function (originalMetadata) { return (originalMetadata.propertyName === inheritedMetadata.propertyName && originalMetadata.type === inheritedMetadata.type); }); }); return originalMetadatas.concat(uniqueInheritedMetadatas); }; /** * Gets all validator constraints for the given object. */ MetadataStorage.prototype.getTargetValidatorConstraints = function (target) { return this.constraintMetadatas.filter(function (metadata) { return metadata.target === target; }); }; return MetadataStorage; }()); /** * Gets metadata storage. * Metadata storage follows the best practices and stores metadata in a global variable. */ function getMetadataStorage() { var global = getGlobal(); if (!global.classValidatorMetadataStorage) { global.classValidatorMetadataStorage = new MetadataStorage(); } return global.classValidatorMetadataStorage; } /** * Validation error description. */ var ValidationError = /** @class */ (function () { function ValidationError() { } /** * * @param shouldDecorate decorate the message with ANSI formatter escape codes for better readability * @param hasParent true when the error is a child of an another one * @param parentPath path as string to the parent of this property */ ValidationError.prototype.toString = function (shouldDecorate, hasParent, parentPath) { var _this = this; if (shouldDecorate === void 0) { shouldDecorate = false; } if (hasParent === void 0) { hasParent = false; } if (parentPath === void 0) { parentPath = ""; } var boldStart = shouldDecorate ? "\u001B[1m" : ""; var boldEnd = shouldDecorate ? "\u001B[22m" : ""; var propConstraintFailed = function (propertyName) { return " - property ".concat(boldStart).concat(parentPath).concat(propertyName).concat(boldEnd, " has failed the following constraints: ").concat(boldStart).concat(Object.keys(_this.constraints).join(", ")).concat(boldEnd, " \n"); }; if (!hasParent) { return ("An instance of ".concat(boldStart).concat(this.target ? this.target.constructor.name : 'an object').concat(boldEnd, " has failed the validation:\n") + (this.constraints ? propConstraintFailed(this.property) : "") + (this.children ? this.children.map(function (childError) { return childError.toString(shouldDecorate, true, _this.property); }).join("") : "")); } else { // we format numbers as array indexes for better readability. var formattedProperty_1 = Number.isInteger(+this.property) ? "[".concat(this.property, "]") : "".concat(parentPath ? "." : "").concat(this.property); if (this.constraints) { return propConstraintFailed(formattedProperty_1); } else { return this.children ? this.children .map(function (childError) { return childError.toString(shouldDecorate, true, "".concat(parentPath).concat(formattedProperty_1)); }) .join("") : ""; } } }; return ValidationError; }()); /** * Validation types. */ var ValidationTypes = /** @class */ (function () { function ValidationTypes() { } /** * Checks if validation type is valid. */ ValidationTypes.isValid = function (type) { var _this = this; return (type !== 'isValid' && type !== 'getMessage' && Object.keys(this) .map(function (key) { return _this[key]; }) .indexOf(type) !== -1); }; /* system */ ValidationTypes.CUSTOM_VALIDATION = 'customValidation'; // done ValidationTypes.NESTED_VALIDATION = 'nestedValidation'; // done ValidationTypes.PROMISE_VALIDATION = 'promiseValidation'; // done ValidationTypes.CONDITIONAL_VALIDATION = 'conditionalValidation'; // done ValidationTypes.WHITELIST = 'whitelistValidation'; // done ValidationTypes.IS_DEFINED = 'isDefined'; // done return ValidationTypes; }()); /** * Convert the constraint to a string to be shown in an error */ function constraintToString(constraint) { if (Array.isArray(constraint)) { return constraint.join(', '); } return "".concat(constraint); } var ValidationUtils = /** @class */ (function () { function ValidationUtils() { } ValidationUtils.replaceMessageSpecialTokens = function (message, validationArguments) { var messageString; if (message instanceof Function) { messageString = message(validationArguments); } else if (typeof message === 'string') { messageString = message; } if (messageString && Array.isArray(validationArguments.constraints)) { validationArguments.constraints.forEach(function (constraint, index) { messageString = messageString.replace(new RegExp("\\$constraint".concat(index + 1), 'g'), constraintToString(constraint)); }); } if (messageString && validationArguments.value !== undefined && validationArguments.value !== null && typeof validationArguments.value === 'string') messageString = messageString.replace(/\$value/g, validationArguments.value); if (messageString) messageString = messageString.replace(/\$property/g, validationArguments.property); if (messageString) messageString = messageString.replace(/\$target/g, validationArguments.targetName); return messageString; }; return ValidationUtils; }()); /** * Executes validation over given object. */ var ValidationExecutor = /** @class */ (function () { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- function ValidationExecutor(validator, validatorOptions) { this.validator = validator; this.validatorOptions = validatorOptions; // ------------------------------------------------------------------------- // Properties // ------------------------------------------------------------------------- this.awaitingPromises = []; this.ignoreAsyncValidations = false; // ------------------------------------------------------------------------- // Private Properties // ------------------------------------------------------------------------- this.metadataStorage = getMetadataStorage(); } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- ValidationExecutor.prototype.execute = function (object, targetSchema, validationErrors) { var _this = this; var _a; /** * If there is no metadata registered it means possibly the dependencies are not flatterned and * more than one instance is used. * * TODO: This needs proper handling, forcing to use the same container or some other proper solution. */ if (!this.metadataStorage.hasValidationMetaData && ((_a = this.validatorOptions) === null || _a === void 0 ? void 0 : _a.enableDebugMessages) === true) { console.warn("No metadata found. There is more than once class-validator version installed probably. You need to flatten your dependencies."); } var groups = this.validatorOptions ? this.validatorOptions.groups : undefined; var strictGroups = (this.validatorOptions && this.validatorOptions.strictGroups) || false; var always = (this.validatorOptions && this.validatorOptions.always) || false; var targetMetadatas = this.metadataStorage.getTargetValidationMetadatas(object.constructor, targetSchema, always, strictGroups, groups); var groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas); if (this.validatorOptions && this.validatorOptions.forbidUnknownValues && !targetMetadatas.length) { var validationError = new ValidationError(); if (!this.validatorOptions || !this.validatorOptions.validationError || this.validatorOptions.validationError.target === undefined || this.validatorOptions.validationError.target === true) validationError.target = object; validationError.value = undefined; validationError.property = undefined; validationError.children = []; validationError.constraints = { unknownValue: 'an unknown value was passed to the validate function' }; validationErrors.push(validationError); return; } if (this.validatorOptions && this.validatorOptions.whitelist) this.whitelist(object, groupedMetadatas, validationErrors); // General validation Object.keys(groupedMetadatas).forEach(function (propertyName) { var value = object[propertyName]; var definedMetadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type === ValidationTypes.IS_DEFINED; }); var metadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type !== ValidationTypes.IS_DEFINED && metadata.type !== ValidationTypes.WHITELIST; }); if (value instanceof Promise && metadatas.find(function (metadata) { return metadata.type === ValidationTypes.PROMISE_VALIDATION; })) { _this.awaitingPromises.push(value.then(function (resolvedValue) { _this.performValidations(object, resolvedValue, propertyName, definedMetadatas, metadatas, validationErrors); })); } else { _this.performValidations(object, value, propertyName, definedMetadatas, metadatas, validationErrors); } }); }; ValidationExecutor.prototype.whitelist = function (object, groupedMetadatas, validationErrors) { var _this = this; var notAllowedProperties = []; Object.keys(object).forEach(function (propertyName) { // does this property have no metadata? if (!groupedMetadatas[propertyName] || groupedMetadatas[propertyName].length === 0) notAllowedProperties.push(propertyName); }); if (notAllowedProperties.length > 0) { if (this.validatorOptions && this.validatorOptions.forbidNonWhitelisted) { // throw errors notAllowedProperties.forEach(function (property) { var _a; var validationError = _this.generateValidationError(object, object[property], property); validationError.constraints = (_a = {}, _a[ValidationTypes.WHITELIST] = "property ".concat(property, " should not exist"), _a); validationError.children = undefined; validationErrors.push(validationError); }); } else { // strip non allowed properties notAllowedProperties.forEach(function (property) { return delete object[property]; }); } } }; ValidationExecutor.prototype.stripEmptyErrors = function (errors) { var _this = this; return errors.filter(function (error) { if (error.children) { error.children = _this.stripEmptyErrors(error.children); } if (Object.keys(error.constraints).length === 0) { if (error.children.length === 0) { return false; } else { delete error.constraints; } } return true; }); }; // ------------------------------------------------------------------------- // Private Methods // ------------------------------------------------------------------------- ValidationExecutor.prototype.performValidations = function (object, value, propertyName, definedMetadatas, metadatas, validationErrors) { var customValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CUSTOM_VALIDATION; }); var nestedValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.NESTED_VALIDATION; }); var conditionalValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CONDITIONAL_VALIDATION; }); var validationError = this.generateValidationError(object, value, propertyName); validationErrors.push(validationError); var canValidate = this.conditionalValidations(object, value, conditionalValidationMetadatas); if (!canValidate) { return; } // handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not this.customValidations(object, value, definedMetadatas, validationError); this.mapContexts(object, value, definedMetadatas, validationError); if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) { return; } if (value === null && this.validatorOptions && this.validatorOptions.skipNullProperties === true) { return; } if ((value === null || value === undefined) && this.validatorOptions && this.validatorOptions.skipMissingProperties === true) { return; } this.customValidations(object, value, customValidationMetadatas, validationError); this.nestedValidations(value, nestedValidationMetadatas, validationError.children); this.mapContexts(object, value, metadatas, validationError); this.mapContexts(object, value, customValidationMetadatas, validationError); }; ValidationExecutor.prototype.generateValidationError = function (object, value, propertyName) { var validationError = new ValidationError(); if (!this.validatorOptions || !this.validatorOptions.validationError || this.validatorOptions.validationError.target === undefined || this.validatorOptions.validationError.target === true) validationError.target = object; if (!this.validatorOptions || !this.validatorOptions.validationError || this.validatorOptions.validationError.value === undefined || this.validatorOptions.validationError.value === true) validationError.value = value; validationError.property = propertyName; validationError.children = []; validationError.constraints = {}; return validationError; }; ValidationExecutor.prototype.conditionalValidations = function (object, value, metadatas) { return metadatas .map(function (metadata) { return metadata.constraints[0](object, value); }) .reduce(function (resultA, resultB) { return resultA && resultB; }, true); }; ValidationExecutor.prototype.customValidations = function (object, value, metadatas, error) { var _this = this; metadatas.forEach(function (metadata) { _this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls).forEach(function (customConstraintMetadata) { if (customConstraintMetadata.async && _this.ignoreAsyncValidations) return; if (_this.validatorOptions && _this.validatorOptions.stopAtFirstError && Object.keys(error.constraints || {}).length > 0) return; var validationArguments = { targetName: object.constructor ? object.constructor.name : undefined, property: metadata.propertyName, object: object, value: value, constraints: metadata.constraints, }; if (!metadata.each || !(Array.isArray(value) || value instanceof Set || value instanceof Map)) { var validatedValue = customConstraintMetadata.instance.validate(value, validationArguments); if (isPromise(validatedValue)) { var promise = validatedValue.then(function (isValid) { if (!isValid) { var _a = _this.createValidationError(object, value, metadata, customConstraintMetadata), type = _a[0], message = _a[1]; error.constraints[type] = message; if (metadata.context) { if (!error.contexts) { error.contexts = {}; } error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); } } }); _this.awaitingPromises.push(promise); } else { if (!validatedValue) { var _a = _this.createValidationError(object, value, metadata, customConstraintMetadata), type = _a[0], message = _a[1]; error.constraints[type] = message; } } return; } // convert set and map into array var arrayValue = convertToArray(value); // Validation needs to be applied to each array item var validatedSubValues = arrayValue.map(function (subValue) { return customConstraintMetadata.instance.validate(subValue, validationArguments); }); var validationIsAsync = validatedSubValues.some(function (validatedSubValue) { return isPromise(validatedSubValue); }); if (validationIsAsync) { // Wrap plain values (if any) in promises, so that all are async var asyncValidatedSubValues = validatedSubValues.map(function (validatedSubValue) { return isPromise(validatedSubValue) ? validatedSubValue : Promise.resolve(validatedSubValue); }); var asyncValidationIsFinishedPromise = Promise.all(asyncValidatedSubValues).then(function (flatValidatedValues) { var validationResult = flatValidatedValues.every(function (isValid) { return isValid; }); if (!validationResult) { var _a = _this.createValidationError(object, value, metadata, customConstraintMetadata), type = _a[0], message = _a[1]; error.constraints[type] = message; if (metadata.context) { if (!error.contexts) { error.contexts = {}; } error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); } } }); _this.awaitingPromises.push(asyncValidationIsFinishedPromise); return; } var validationResult = validatedSubValues.every(function (isValid) { return isValid; }); if (!validationResult) { var _b = _this.createValidationError(object, value, metadata, customConstraintMetadata), type = _b[0], message = _b[1]; error.constraints[type] = message; } }); }); }; ValidationExecutor.prototype.nestedValidations = function (value, metadatas, errors) { var _this = this; if (value === void 0) { return; } metadatas.forEach(function (metadata) { var _a; if (metadata.type !== ValidationTypes.NESTED_VALIDATION && metadata.type !== ValidationTypes.PROMISE_VALIDATION) { return; } if (Array.isArray(value) || value instanceof Set || value instanceof Map) { // Treats Set as an array - as index of Set value is value itself and it is common case to have Object as value var arrayLikeValue = value instanceof Set ? Array.from(value) : value; arrayLikeValue.forEach(function (subValue, index) { _this.performValidations(value, subValue, index.toString(), [], metadatas, errors); }); } else if (value instanceof Object) { var targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name; _this.execute(value, targetSchema, errors); } else { var error = new ValidationError(); error.value = value; error.property = metadata.propertyName; error.target = metadata.target; var _b = _this.createValidationError(metadata.target, value, metadata), type = _b[0], message = _b[1]; error.constraints = (_a = {}, _a[type] = message, _a); errors.push(error); } }); }; ValidationExecutor.prototype.mapContexts = function (object, value, metadatas, error) { var _this = this; return metadatas.forEach(function (metadata) { if (metadata.context) { var customConstraint = void 0; if (metadata.type === ValidationTypes.CUSTOM_VALIDATION) { var customConstraints = _this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls); customConstraint = customConstraints[0]; } var type = _this.getConstraintType(metadata, customConstraint); if (error.constraints[type]) { if (!error.contexts) { error.contexts = {}; } error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context); } } }); }; ValidationExecutor.prototype.createValidationError = function (object, value, metadata, customValidatorMetadata) { var targetName = object.constructor ? object.constructor.name : undefined; var type = this.getConstraintType(metadata, customValidatorMetadata); var validationArguments = { targetName: targetName, property: metadata.propertyName, object: object, value: value, constraints: metadata.constraints, }; var message = metadata.message || ''; if (!metadata.message && (!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages))) { if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) { message = customValidatorMetadata.instance.defaultMessage(validationArguments); } } var messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments); return [type, messageString]; }; ValidationExecutor.prototype.getConstraintType = function (metadata, customValidatorMetadata) { var type = customValidatorMetadata && customValidatorMetadata.name ? customValidatorMetadata.name : metadata.type; return type; }; return ValidationExecutor; }()); var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (undefined && undefined.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; /** * Validator performs validation of the given object based on its metadata. */ var Validator = /** @class */ (function () { function Validator() { } /** * Performs validation of the given object based on decorators or validation schema. */ Validator.prototype.validate = function (objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions) { return this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions); }; /** * Performs validation of the given object based on decorators or validation schema and reject on error. */ Validator.prototype.validateOrReject = function (objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions) { return __awaiter(this, void 0, void 0, function () { var errors; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.coreValidate(objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions)]; case 1: errors = _a.sent(); if (errors.length) return [2 /*return*/, Promise.reject(errors)]; return [2 /*return*/]; } }); }); }; /** * Performs validation of the given object based on decorators or validation schema. */ Validator.prototype.validateSync = function (objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions) { var object = typeof objectOrSchemaName === 'string' ? objectOrValidationOptions : objectOrSchemaName; var options = typeof objectOrSchemaName === 'string' ? maybeValidatorOptions : objectOrValidationOptions; var schema = typeof objectOrSchemaName === 'string' ? objectOrSchemaName : undefined; var executor = new ValidationExecutor(this, options); executor.ignoreAsyncValidations = true; var validationErrors = []; executor.execute(object, schema, validationErrors); return executor.stripEmptyErrors(validationErrors); }; // ------------------------------------------------------------------------- // Private Properties // ------------------------------------------------------------------------- /** * Performs validation of the given object based on decorators or validation schema. * Common method for `validateOrReject` and `validate` methods. */ Validator.prototype.coreValidate = function (objectOrSchemaName, objectOrValidationOptions, maybeValidatorOptions) { var object = typeof objectOrSchemaName === 'string' ? objectOrValidationOptions : objectOrSchemaName; var options = typeof objectOrSchemaName === 'string' ? maybeValidatorOptions : objectOrValidationOptions; var schema = typeof objectOrSchemaName === 'string' ? objectOrSchemaName : undefined; var executor = new ValidationExecutor(this, options); var validationErrors = []; executor.execute(object, schema, validationErrors); return Promise.all(executor.awaitingPromises).then(function () { return executor.stripEmptyErrors(validationErrors); }); }; return Validator; }()); /** * Container to be used by this library for inversion control. If container was not implicitly set then by default * container simply creates a new instance of the given class. */ var defaultContainer = new (/** @class */ (function () { function class_1() { this.instances = []; } class_1.prototype.get = function (someClass) { var instance = this.instances.find(function (instance) { return instance.type === someClass; }); if (!instance) { instance = { type: someClass, object: new someClass() }; this.instances.push(instance); } return instance.object; }; return class_1; }()))(); var userContainer; var userContainerOptions; /** * Sets container to be used by this library. */ function useContainer(iocContainer, options) { userContainer = iocContainer; userContainerOptions = options; } /** * Gets the IOC container used by this library. */ function getFromContainer(someClass) { if (userContainer) { try { var instance = userContainer.get(someClass); if (instance) return instance; if (!userContainerOptions || !userContainerOptions.fallback) return instance; } catch (error) { if (!userContainerOptions || !userContainerOptions.fallbackOnErrors) throw error; } } return defaultContainer.get(someClass); } /** * If object has both allowed and not allowed properties a validation error will be thrown. */ function Allow(validationOptions) { return function (object, propertyName) { var args = { type: ValidationTypes.WHITELIST, target: object.constructor, propertyName: propertyName, validationOptions: validationOptions, }; getMetadataStorage().addValidationMetadata(new ValidationMetadata(args)); }; } /** * This metadata interface contains information for custom validators. */ var ConstraintMetadata = /** @class */ (function () { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- function ConstraintMetadata(target, name, async) { if (async === void 0) { async = false; } this.target = target; this.name = name; this.async = async; } Object.defineProperty(ConstraintMetadata.prototype, "instance", { // ------------------------------------------------------------------------- // Accessors // ------------------------------------------------------------------------- /** * Instance of the target custom validation class which performs validation. */ get: function () { return getFromContainer(this.target); }, enumerable: false, configurable: true }); return ConstraintMetadata; }()); /** * Registers a custom validation decorator. */ function registerDecorator(options) { var constraintCls; if (options.validator instanceof Function) { constraintCls = options.validator; var constraintClasses = getFromContainer(MetadataStorage).getTargetValidatorConstraints(options.validator); if (constraintClasses.length > 1) { throw "More than one implementation of ValidatorConstraintInterface found for validator on: ".concat(options.target.name, ":").concat(options.propertyName); } } else { var validator_1 = options.validator; constraintCls = /** @class */ (function () { function CustomConstraint() { } CustomConstraint.prototype.validate = function (value, validationArguments) { return validator_1.validate(value, validationArguments); }; CustomConstraint.prototype.defaultMessage = function (validationArguments) { if (validator_1.defaultMessage) { return validator_1.defaultMessage(validationArguments); } return ''; }; return CustomConstraint; }()); getMetadataStorage().addConstraintMetadata(new ConstraintMetadata(constraintCls, options.name, options.async)); } var validationMetadataArgs = { type: options.name && ValidationTypes.isValid(options.name) ? options.name : ValidationTypes.CUSTOM_VALIDATION, target: options.target, propertyName: options.propertyName, validationOptions: options.options, constraintCls: constraintCls, constraints: options.constraints, }; getMetadataStorage().addValidationMetadata(new ValidationMetadata(validationMetadataArgs)); } function buildMessage(impl, validationOptions) { return function (validationArguments) { var eachPrefix = validationOptions && validationOptions.each ? 'each value in ' : ''; return impl(eachPrefix, validationArguments); }; } function ValidateBy(options, validationOptions) { return function (object, propertyName) { registerDecorator({ name: options.name, target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: options.constraints, validator: options.validator, }); }; } // isDefined is (yet) a special case var IS_DEFINED = ValidationTypes.IS_DEFINED; /** * Checks if value is defined (!== undefined, !== null). */ function isDefined(value) { return value !== undefined && value !== null; } /** * Checks if value is defined (!== undefined, !== null). */ function IsDefined(validationOptions) { return ValidateBy({ name: IS_DEFINED, validator: { validate: function (value) { return isDefined(value); }, defaultMessage: buildMessage(function (eachPrefix) { return eachPrefix + '$property should not be null or undefined'; }, validationOptions), }, }, validationOptions); } /** * Checks if value is missing and if so, ignores all validators. */ function IsOptional(validationOptions) { return function (object, propertyName) { var args = { type: ValidationTypes.CONDITIONAL_VALIDATION, target: object.constructor, propertyName: propertyName, constraints: [ function (object, value) { return object[propertyName] !== null && object[propertyName] !== undefined; }, ], valid