class-validator
Version:
Decorator-based property validation for classes.
964 lines (950 loc) • 739 kB
JavaScript
(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.name = args.name;
this.target = args.target;
this.propertyName = args.propertyName;
this.constraints = args === null || args === void 0 ? void 0 : 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,
name: validation.name,
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';
}
var __values = (undefined && undefined.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __read$2 = (undefined && undefined.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
/**
* Storage all metadatas.
*/
var MetadataStorage = /** @class */ (function () {
function MetadataStorage() {
// -------------------------------------------------------------------------
// Private properties
// -------------------------------------------------------------------------
this.validationMetadatas = new Map();
this.constraintMetadatas = new Map();
}
Object.defineProperty(MetadataStorage.prototype, "hasValidationMetaData", {
get: function () {
return !!this.validationMetadatas.size;
},
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) {
var existingMetadata = this.validationMetadatas.get(metadata.target);
if (existingMetadata) {
existingMetadata.push(metadata);
}
else {
this.validationMetadatas.set(metadata.target, [metadata]);
}
};
/**
* Adds a new constraint metadata.
*/
MetadataStorage.prototype.addConstraintMetadata = function (metadata) {
var existingMetadata = this.constraintMetadatas.get(metadata.target);
if (existingMetadata) {
existingMetadata.push(metadata);
}
else {
this.constraintMetadatas.set(metadata.target, [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 e_1, _a;
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 filteredForOriginalMetadatasSearch = this.validationMetadatas.get(targetConstructor) || [];
var originalMetadatas = filteredForOriginalMetadatasSearch.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 filteredForInheritedMetadatasSearch = [];
try {
for (var _b = __values(this.validationMetadatas.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = __read$2(_c.value, 2), key = _d[0], value = _d[1];
if (targetConstructor.prototype instanceof key) {
filteredForInheritedMetadatasSearch.push.apply(filteredForInheritedMetadatasSearch, __spreadArray([], __read$2(value), false));
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
var inheritedMetadatas = filteredForInheritedMetadatasSearch.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.get(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
* @param showConstraintMessages show constraint messages instead of constraint names
*/
ValidationError.prototype.toString = function (shouldDecorate, hasParent, parentPath, showConstraintMessages) {
var _this = this;
if (shouldDecorate === void 0) { shouldDecorate = false; }
if (hasParent === void 0) { hasParent = false; }
if (parentPath === void 0) { parentPath = ""; }
if (showConstraintMessages === void 0) { showConstraintMessages = false; }
var boldStart = shouldDecorate ? "\u001B[1m" : "";
var boldEnd = shouldDecorate ? "\u001B[22m" : "";
var constraintsToString = function () { var _a; return (showConstraintMessages ? Object.values : Object.keys)((_a = _this.constraints) !== null && _a !== void 0 ? _a : {}).join(", "); };
var propConstraintFailed = function (propertyName) {
return " - property ".concat(boldStart).concat(parentPath).concat(propertyName).concat(boldEnd, " has failed the following constraints: ").concat(boldStart).concat(constraintsToString()).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, showConstraintMessages); })
.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), showConstraintMessages);
})
.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(', ');
}
if (typeof constraint === 'symbol') {
constraint = constraint.description;
}
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 &&
['string', 'boolean', 'number'].includes(typeof validationArguments.value))
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;
}());
var __read$1 = (undefined && undefined.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
/**
* 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, _b;
/**
* 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 validation metadata found. No validation will be performed. There are multiple possible reasons:\n" +
" - There may be multiple class-validator versions installed. You will need to flatten your dependencies to fix the issue.\n" +
" - This validation runs before any file with validation decorator was parsed by NodeJS.");
}
var groups = this.validatorOptions ? this.validatorOptions.groups : undefined;
var strictGroups = (this.validatorOptions && this.validatorOptions.strictGroups) || false;
var always = (this.validatorOptions && this.validatorOptions.always) || false;
/** Forbid unknown values are turned on by default and any other value than false will enable it. */
var forbidUnknownValues = ((_b = this.validatorOptions) === null || _b === void 0 ? void 0 : _b.forbidUnknownValues) === undefined || this.validatorOptions.forbidUnknownValues !== false;
var targetMetadatas = this.metadataStorage.getTargetValidationMetadatas(object.constructor, targetSchema, always, strictGroups, groups);
var groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas);
if (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);
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 = __read$1(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), 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 = __read$1(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), 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 = __read$1(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), 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 = __read$1(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _b[0], message = _b[1];
error.constraints[type] = message;
}
});
});
};
ValidationExecutor.prototype.nestedValidations = function (value, metadatas, error) {
var _this = this;
if (value === void 0) {
return;
}
metadatas.forEach(function (metadata) {
if (metadata.type !== ValidationTypes.NESTED_VALIDATION && metadata.type !== ValidationTypes.PROMISE_VALIDATION) {
return;
}
else if (_this.validatorOptions &&
_this.validatorOptions.stopAtFirstError &&
Object.keys(error.constraints || {}).length > 0) {
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, error.children);
});
}
else if (value instanceof Object) {
var targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name;
_this.execute(value, targetSchema, error.children);
}
else {
var _a = __read$1(_this.createValidationError(metadata.target, value, metadata), 2), type = _a[0], message = _a[1];
error.constraints[type] = message;
}
});
};
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 (g && (g = 0, op[0] && (_ = 0)), _) 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