UNPKG

epic-validator

Version:
574 lines (573 loc) 31.4 kB
"use strict"; 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;