epic-validator
Version:
A simple but powerful Data Validator
574 lines (573 loc) • 31.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationChain = exports.ChainStates = void 0;
const exceptions_1 = require("./exceptions");
const get_ip_range_1 = require("get-ip-range");
const tld_1 = require("./tld");
var ChainStates;
(function (ChainStates) {
ChainStates[ChainStates["WAITING"] = 0] = "WAITING";
ChainStates[ChainStates["EXECUTING"] = 1] = "EXECUTING";
ChainStates[ChainStates["COMPLETED"] = 2] = "COMPLETED";
ChainStates[ChainStates["STOPPED"] = 3] = "STOPPED";
})(ChainStates = exports.ChainStates || (exports.ChainStates = {}));
class ValidationChain {
constructor(CustomValidators, Target, Identifier = "data") {
this.CustomValidators = CustomValidators;
this.Target = Target;
this.Identifier = Identifier;
this.State = ChainStates.WAITING;
this.DefaultRequired = false;
this.DefaultAttributes = (overrides) => (Object.assign({ optional: {
is: false,
falsy: false,
}, required: this.DefaultRequired, not: false, skip: false }, overrides));
this.Attributes = this.DefaultAttributes();
this.Chain = [];
this.Errors = new exceptions_1.ValidatorException();
this.error = (message) => this.custom(() => {
throw message;
}, message);
this.exec = async () => (await this.run()).throw();
// Built-in Validators
this.isString = (message = "Please provide a valid String!") => this.custom((value) => {
if (typeof value !== "string")
throw message;
}, message);
this.isNumber = (message = "Value should be a Number!") => this.custom((value) => {
if (typeof value !== "number")
throw message;
}, message);
this.isNumeric = (options, message) => this.custom((value) => {
if (typeof value !== "number")
if (typeof value !== "string" ||
isNaN(value) ||
isNaN(parseFloat(value)))
throw message || "Value should be Numeric!";
else if (options === null || options === void 0 ? void 0 : options.sanitize)
return parseFloat(value);
}, message);
this.likeNumber = this.isNumeric;
this.isBoolean = (message = "Value should be a Boolean!") => this.custom((value) => {
if (typeof value !== "boolean")
throw message;
}, message);
this.likeBoolean = (options, message) => this.custom((value) => {
// Resolve Value
if (["string", "number", "boolean"].includes(typeof value)) {
if (["true", "1", "on"].includes(value.toString().toLowerCase()))
value = true;
else if (["false", "0", "off"].includes(value.toString().toLowerCase()))
value = false;
}
else if (options === null || options === void 0 ? void 0 : options.falsy)
value = !!value;
// Check Value Type
if (typeof value !== "boolean")
throw message || "Invalid value type! Boolean type is allowed.";
else if (typeof (options === null || options === void 0 ? void 0 : options.isTrue) === "boolean" && value !== options.isTrue)
throw message || `Value should be '${options.isTrue.toString()}'!`;
if (options === null || options === void 0 ? void 0 : options.sanitize)
return value;
}, message);
this.isObject = (message = "Value should be an Object!") => this.custom((value) => {
if (typeof value !== "object")
throw message;
}, message);
this.isArray = (message = "Value should be an Array!") => this.custom((value) => {
if (!(value instanceof Array))
throw message;
}, message);
this.isFunction = (message = "Value should be a Function!") => this.custom((value) => {
if (typeof value !== "function")
throw message;
}, message);
this.isBigInt = (message = "Value should be a BigInt!") => this.custom((value) => {
if (typeof value !== "bigint")
throw message;
}, message);
this.isUndefined = (message = "Value should be Undefined!") => this.custom((value) => {
if (typeof value !== "undefined")
throw message;
}, message);
this.isSymbol = (message = "Value should be a Symbol!") => this.custom((value) => {
if (typeof value !== "symbol")
throw message;
}, message);
this.isJson = (message) => this.custom((value) => {
try {
if (typeof JSON.parse(value) !== "object")
throw message || "Value is not a valid JSON format!";
}
catch (error) {
throw message || error.message || error;
}
}, message);
this.likeObject = (options, message) => this.custom((value) => {
if (typeof value === "string")
try {
value = JSON.parse(value);
}
catch (error) {
throw message || error.message || error;
}
if (typeof value !== "object")
throw message || "Value should be an Object!";
else if (options === null || options === void 0 ? void 0 : options.sanitize)
return value;
}, message);
this.jsonParse = (message) => this.likeObject({ sanitize: true }, message);
this.jsonStringify = (message) => this.custom((value) => {
try {
return JSON.stringify(value);
}
catch (error) {
throw message || error.message || error;
}
}, message);
this.likeArray = (options, message) => this.custom((value) => {
if (typeof value === "string")
try {
value = JSON.parse(value);
}
catch (error) {
throw message || error.message || error;
}
if (!(value instanceof Array))
throw message || "Value should be an Array!";
else if (options === null || options === void 0 ? void 0 : options.sanitize)
return value;
}, message);
this.isEmpty = (options, message) => this.custom((value) => {
if (typeof value === "string" && value !== "")
throw message || "Value should be empty!";
if (options === null || options === void 0 ? void 0 : options.isObject) {
if (typeof value === "object") {
if (value instanceof Array && value.length)
throw message || "Array should be Empty!";
else if (JSON.stringify(value) !== "{}")
throw message || "Object should be Empty!";
}
else
throw message || "Value should be an Object!";
}
if ((options === null || options === void 0 ? void 0 : options.isFalsy) && value)
throw message || "Value should be falsy!";
if ((options === null || options === void 0 ? void 0 : options.isUndefined) && value !== undefined)
throw message || "Value should be Undefined!";
if ((options === null || options === void 0 ? void 0 : options.isNullable) && value !== null)
throw message || "Value should be Null!";
}, message);
this.notEmpty = (options, message) => this.custom((value) => {
if (value === undefined ||
(typeof value === "string" && value === "") ||
value === null ||
((options === null || options === void 0 ? void 0 : options.falsy) && !value))
throw message || "Value should not be empty!";
else if (options === null || options === void 0 ? void 0 : options.isObject) {
if (typeof value === "object") {
if (value instanceof Array && !value.length)
throw message || "Array should not be Empty!";
else if (JSON.stringify(value) === "{}")
throw message || "Object should not be Empty!";
}
else
throw message || "Value should be an Object!";
}
}, message);
this.isIn = (choices, message) => this.custom((value) => {
if (!choices.includes(value))
throw message || `Unacceptable value has been provided!`;
}, message);
this.matches = (target, message) => this.custom(async (value) => {
try {
// Check Value
if (typeof target === "function")
target = await target(value);
}
catch (error) {
throw message || error.message || error;
}
// Exact Match
if (value === target)
return;
// Match with RegExp
else if (target instanceof RegExp) {
if (!target.test(value))
throw message || "Value does not match the expected Regexp!";
}
else
throw message || "Value does not match the expected!";
}, message);
this.isAlpha = (options, message) => this.custom((value) => {
if (!/^[a-zA-Z\s]+$/.test(value))
throw (message ||
`Just alphabets are allowed for ${(options === null || options === void 0 ? void 0 : options.title) || "value"}!`);
else if ((options === null || options === void 0 ? void 0 : options.allowUpperCase) === true && !/^[A-Z]+$/.test(value))
throw (message ||
`${options.title || "value"} should also contain upper case alphabets!`);
else if ((options === null || options === void 0 ? void 0 : options.allowUpperCase) === false && /^[A-Z]+$/.test(value))
throw (message ||
`Upper case alphabets are not allowed in ${(options === null || options === void 0 ? void 0 : options.title) || "value"}!`);
else if (!(options === null || options === void 0 ? void 0 : options.allowSpaces) && /[\s]+/.test(value))
throw (message ||
`Spaces are not allowed in the ${(options === null || options === void 0 ? void 0 : options.title) || "value"}!`);
}, message);
this.isAlphanumeric = (options, message) => this.custom((value) => {
if (!/^[a-zA-Z0-9\s$-/:-?{-~!"^_`\[\]]+$/.test(value))
throw (message ||
`${(options === null || options === void 0 ? void 0 : options.title) || "value"} should be a valid alphanumeric string!`);
else if ((options === null || options === void 0 ? void 0 : options.allowUpperCase) === true && !/^[A-Z]+$/.test(value))
throw (message ||
`Upper case alphabets are required in ${options.title || "value"}!`);
else if ((options === null || options === void 0 ? void 0 : options.allowUpperCase) === false && /^[A-Z]+$/.test(value))
throw (message ||
`Upper case alphabets are not allowed in ${options.title || "value"}!`);
else if ((options === null || options === void 0 ? void 0 : options.allowLowerCase) === true && !/^[a-z]+$/.test(value))
throw (message ||
`Lower case alphabets are required in ${options.title || "value"}!`);
else if ((options === null || options === void 0 ? void 0 : options.allowLowerCase) === false && /^[a-z]+$/.test(value))
throw (message ||
`Lower case alphabets are not allowed in ${options.title || "value"}!`);
else if ((options === null || options === void 0 ? void 0 : options.allowNumbers) === true && !/[0-9]+/.test(value))
throw (message || `${options.title || "value"} should also contain numbers!`);
else if ((options === null || options === void 0 ? void 0 : options.allowNumbers) === false && /[0-9]+/.test(value))
throw (message || `${options.title || "value"} should not contain numbers!`);
else if ((options === null || options === void 0 ? void 0 : options.allowSymbols) === true &&
!/[$-/:-?{-~!"^_`\[\]]+/.test(value))
throw (message || `${options.title || "value"} should also contain symbols!`);
else if ((options === null || options === void 0 ? void 0 : options.allowSymbols) === false &&
/[$-/:-?{-~!"^_`\[\]]+/.test(value))
throw (message || `${options.title || "value"} should not contain symbols!`);
else if ((options === null || options === void 0 ? void 0 : options.allowSpaces) === true && !/[\s]+/.test(value))
throw (message || `${options.title || "value"} should also contain spaces!`);
else if ((options === null || options === void 0 ? void 0 : options.allowSpaces) === false && /[\s]+/.test(value))
throw (message || `${options.title || "value"} should not contain spaces!`);
}, message);
this.isEmail = (message) => this.custom((value) => {
if (!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value))
throw message || "Invalid email has been provided!";
}, message);
this.isIpv4 = (message) => this.custom((value) => {
if (!/^\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b$/.test(value))
throw message || "Invalid IPV4 Address has been provided!";
}, message);
this.isIpv6 = (message) => this.custom((value) => {
if (!/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(value))
throw message || "Invalid IPV6 Address has been provided!";
}, message);
this.isIpAddress = (message) => this.custom((value) => {
if (!/^\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b$/.test(value) &&
!/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(value))
throw message || "Invalid IP Address has been provided!";
}, message);
this.inIps = (ips, message) => this.custom((value) => {
if (!/^\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b$/.test(value) &&
!/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(value))
throw message || "Invalid IP Address has been provided!";
else if (!ips.includes(value))
throw message || `IP address '${value}' is not allowed!`;
}, message);
this.inIpRange = (options, message) => this.custom((value) => {
if (!/^\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b$/.test(value) &&
!/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(value))
throw message || "Invalid IP Address has been provided!";
else if (!(0, get_ip_range_1.getIPRange)(...options.range).includes(value))
throw message || `IP address '${value}' is not allowed!`;
}, message);
this.isIBAN = (message) => this.custom((value) => {
if (!/^([A-Z]{2}[ -]?[0-9]{2})(?=(?:[ -]?[A-Z0-9]){9,30}$)((?:[ -]?[A-Z0-9]{3,5}){2,7})([ -]?[A-Z0-9]{1,3})?$/.test(value))
throw message || "Invalid IBAN has been provided!";
}, message);
this.isLength = (options, message) => this.custom((value) => {
let allow = true;
if (typeof value !== "undefined") {
if (typeof value === "string" || value instanceof Array) {
if (typeof options.min === "number" && value.length < options.min)
allow = false;
if (typeof options.max === "number" && value.length > options.max)
allow = false;
}
else if (typeof value === "object" || typeof value === "number") {
const toString = Object(value).toString();
if (typeof options.min === "number" && toString.length < options.min)
allow = false;
if (typeof options.max === "number" && toString.length > options.max)
allow = false;
}
else
throw (message || `Cannot check length of value of type '${typeof value}'!`);
if (!allow)
throw message || "Invalid value length!";
}
else if (!this.getErrors().List.length)
throw (message || "Cannot check the length of value of type 'undefined'!");
}, message);
this.isAmount = (options, message) => this.custom((value) => {
if (typeof value === "number") {
if (typeof options.min === "number")
if (options.min > value)
throw message || `Minimum amount allowed is '${options.min}'!`;
if (typeof options.max === "number")
if (options.max < value)
throw message || `Maximum amount allowed is '${options.max}'!`;
}
else if (!this.getErrors().List.length && (options.min || 0) > 0)
throw message || "Value should be a number type!";
}, message);
this.isURL = (options, message) => this.custom((value) => {
try {
// Parse URL
const Url = new URL(value);
// Validate Protocol & TLD
if ((options === null || options === void 0 ? void 0 : options.protocols) instanceof Array &&
!options.protocols.includes(Url.protocol.replace(":", "")))
throw `Invalid URL protocol has been provided! Protocol not allowed.`;
else if ([undefined, true].includes(options === null || options === void 0 ? void 0 : options.requireTLD) &&
!new tld_1.TLD(Url.hostname.split(".").pop()).isValid())
throw `Invalid URL TLD or TLD has not been found!`;
else if (options === null || options === void 0 ? void 0 : options.sanitize)
return Url;
}
catch (error) {
throw message || error.message || error;
}
}, message);
}
getState() {
return this.State;
}
getIdentifier() {
return this.Identifier;
}
getAttributes() {
return this.Attributes;
}
get() {
return this.Target;
}
getErrors() {
return this.Errors;
}
optional(options) {
// Push a method to the Chain
this.Chain.push(async () => {
this.Attributes.optional = Object.assign(Object.assign(Object.assign({}, this.Attributes.optional), { is: true }), (typeof options === "function"
? await options(this.get())
: options));
});
return this;
}
skip(predicate) {
// Push a method to the Chain
this.Chain.push(async () => {
this.Attributes.skip =
typeof predicate === "function"
? !!(await predicate(this.get()))
: true;
});
return this;
}
not(predicate) {
// Push a method to the Chain
this.Chain.push(async () => {
this.Attributes.not =
typeof predicate === "function"
? !!(await predicate(this.get()))
: true;
});
return this;
}
required(predicate) {
// Push a method to the Chain
this.Chain.push(async () => {
this.Attributes.required =
typeof predicate === "function"
? !!(await predicate(this.get()))
: true;
});
return this;
}
notRequired(predicate) {
// Push a method to the Chain
this.Chain.push(async () => {
this.Attributes.required =
typeof predicate === "function"
? !(await predicate(this.get()))
: false;
});
return this;
}
requireAll() {
this.DefaultRequired = true;
return this.required();
}
use(method, ...args) {
if (typeof this.CustomValidators[method] !== "function")
throw `Invalid Use of '${method}' Validator! Validator not found or is invalid.`;
return this.CustomValidators[method](this, ...args);
}
custom(method, message) {
// Push a method to the Chain
this.Chain.push(async () => {
// Method Execution Process
await (async () => {
// Check Optional
if (this.getAttributes().optional.is) {
// Set Default Value If Any
this.Target =
this.Target === undefined
? this.getAttributes().optional.default
: this.Target;
// Skip If Undefined
if (this.get() === undefined)
return;
// Check Falsy
if (this.getAttributes().optional.falsy &&
!this.get() &&
this.get() !== 0) {
if (this.getAttributes().optional.default !== undefined)
this.Target = this.getAttributes().optional.default;
// Check Falsy
if (!this.get() && this.get() !== 0)
return;
}
}
// Skip if needed
if (this.getAttributes().skip)
return;
try {
// Validate & Sanitize Target
const Results = await method(this.get(), new ValidationChain(this.CustomValidators, this.get(), this.Identifier));
// If Result is a Chain
if (Results instanceof ValidationChain) {
// Run New Chain
const Chain = await Results.run();
// Publish New Chain Value
this.Target = Chain.get();
// Combine Errors
this.Errors.combine(Chain.getErrors());
// Stop Execution if required
if (Chain.getState() === ChainStates.STOPPED)
this.State = ChainStates.STOPPED;
}
else {
this.Target = Results !== undefined ? Results : this.get();
// Error If Negated
if (this.getAttributes().not) {
// Stop Chain Execution if Required
if (this.getAttributes().required)
this.State = ChainStates.STOPPED;
this.Errors.push(message || "Invalid value has been provided!", this.Identifier, this.get());
}
}
}
catch (e) {
// No Error If Negated
if (this.getAttributes().not)
return;
// Stop Chain Execution if Required
if (this.getAttributes().required)
this.State = ChainStates.STOPPED;
// Push Error to the List
this.Errors.push(message || (e === null || e === void 0 ? void 0 : e.message) || e, this.Identifier, this.get());
}
})();
// Set Default Attributes
this.Attributes = this.DefaultAttributes({
optional: this.getAttributes().optional,
});
});
return this;
}
schema(schema, options) {
return this.custom(async (Target) => {
if (typeof schema !== "object")
throw "Invalid schema has been provided!";
else {
// Get All Schema Fields
const SchemaFields = Object.keys(schema);
// Catch Unexpected Fields
if ([undefined, true].includes(options === null || options === void 0 ? void 0 : options.strict)) {
// Check Target
if (typeof Target === "object") {
// Get All Value Fields
const ValueFields = Object.keys(Target);
// Check Each Field
ValueFields.forEach((field) => {
var _a;
if (Target[field] !== undefined)
if (!((_a = options === null || options === void 0 ? void 0 : options.ignoreProperties) === null || _a === void 0 ? void 0 : _a.includes(field)))
if (!SchemaFields.includes(field))
throw `Unexpected field '${field}' has been provided!`;
});
}
else
throw "Cannot validate the schema of a non-object!";
}
// Run Multiple Chains
for (const field in schema) {
if (this.State === ChainStates.STOPPED)
return;
// Run New Chain
const Chain = await schema[field](new ValidationChain(this.CustomValidators, Target[field], `${this.Identifier}.${field}`)).run();
// Publish New Chain Value
this.Target[field] = Chain.get();
// Combine Errors
this.Errors.combine(Chain.getErrors());
// Stop Execution if required
if (Chain.getState() === ChainStates.STOPPED)
this.State = ChainStates.STOPPED;
}
}
});
}
each(validator) {
return this.custom(async (Target) => {
// Check Target
if (typeof Target !== "object")
throw "Please provide a valid Iterator!";
// Run Multiple Chains
for (const field in Target) {
if (this.State === ChainStates.STOPPED)
return;
// Run New Chain
const Chain = await validator(new ValidationChain(this.CustomValidators, Target[field], `${this.Identifier}.${field}`)).run();
// Publish New Chain Value
this.Target[field] = Chain.get();
// Combine Errors
this.Errors.combine(Chain.getErrors());
// Stop Execution if required
if (Chain.getState() === ChainStates.STOPPED)
this.State = ChainStates.STOPPED;
}
});
}
nextState() {
this.State =
this.State === ChainStates.WAITING
? ChainStates.EXECUTING
: this.State === ChainStates.EXECUTING
? ChainStates.COMPLETED
: this.State;
return this;
}
async run() {
// Update State
this.nextState();
// Execute Chain
for (const Method of this.Chain)
if (this.State === ChainStates.EXECUTING)
await Method();
// Update State
this.nextState();
return this;
}
throw() {
// Throw if any Errors exists
if (this.Errors.List.length)
throw this.getErrors();
return this;
}
}
exports.ValidationChain = ValidationChain;