UNPKG

abolish

Version:

A javascript object validator.

418 lines (417 loc) 14.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbolishCompiled = void 0; const AbolishError_1 = __importDefault(require("./AbolishError")); const inbuilt_fn_1 = require("./inbuilt.fn"); class AbolishCompiled { /** * Constructor * @param input */ constructor(input) { /** * Hold Compiled Object */ this.data = {}; /** * Schema Keys and Included Fields */ this.fields = []; /** * If fields has any dot notation set to true */ this.fieldsHasDotNotation = false; /** * Is Object is true, but if a variable is passed, it will return false */ this.isObject = true; /** * if there is an async validator, set to true */ this.async = false; Object.defineProperty(this, "input", { value: input, enumerable: false, writable: true }); } /** * Validate Compiled Schema * @param data */ validateObject(data) { /** * If this compiled input is not an object, throw error */ if (!this.isObject) { throw new Error("Variable compiled input cannot be used to validate an object, use object compiled input!"); } /** * If this compiled input is async, throw error */ if (this.async) { throw new Error("Rules contains an async validator, use validateObjectAsync instead!"); } /** * Validate Object */ const validated = { ...data }; if (this.allowedFields) { const objKeys = Object.keys(validated); const unknownKeys = objKeys.filter((key) => !this.allowedFields.includes(key)); if (unknownKeys.length) { return [ { code: "object.unknown", type: "internal", key: "$strict", validator: "$strict", message: "Data contains unknown fields!", data: { unknown: unknownKeys } }, {} ]; } } /** * Hold current fields * This will be used in the skip section to remove fields that are not included in the input */ let fields = this.fields; /** * Loop through all the fields in the input */ for (const field in this.data) { const compiled = this.data[field]; // Current field value const value = (0, inbuilt_fn_1.abolish_Get)(validated, field, this.fieldsHasDotNotation); // Check if skip rule is set if (compiled.$skip) { let $skip = compiled.$skip; // Run skip if it is a function if (typeof $skip === "function") { $skip = $skip(value, validated); } if ($skip) { // if field is not in included fields, remove it from fields if (this.includedFields && !this.includedFields.includes(field)) { fields = fields.filter((f) => f !== field); } continue; } } /** * Loop through all compiled validators */ for (const validatorName in compiled.validators) { const validator = compiled.validators[validatorName]; let result = false; try { result = validator.func(value, validated); } catch (e) { return [ { code: "default", key: field, type: "internal", validator: validatorName, message: e.message, data: e.stack }, {} ]; } if (typeof result !== undefined && (result === false || (0, inbuilt_fn_1.InstanceOf)(AbolishError_1.default, result))) { return parseErrorMessage(field, value, result, validator, compiled.$name); } } } let result; if (fields.length === 1) { const onlyField = fields[0]; result = { [onlyField]: validated[onlyField] }; } else if (fields.length > 1) { result = (0, inbuilt_fn_1.abolish_Pick)(validated, fields, this.fieldsHasDotNotation); } else { result = {}; } return [undefined, result]; } async validateObjectAsync(data) { /** * If this compiled input is not an object, throw error */ if (!this.isObject) { throw new Error("Variable compiled input cannot be used to validate an object, use object compiled input!"); } /** * Validate Object */ const validated = { ...data }; if (this.allowedFields) { const objKeys = Object.keys(validated); const unknownKeys = objKeys.filter((key) => !this.allowedFields.includes(key)); if (unknownKeys.length) { return [ { code: "object.unknown", type: "internal", key: "$strict", validator: "$strict", message: "Data contains unknown fields!", data: { unknown: unknownKeys } }, {} ]; } } /** * Hold current fields * This will be used in the skip section to remove fields that are not included in the input */ let fields = this.fields; /** * Loop through all the fields in the input */ for (const field in this.data) { const compiled = this.data[field]; // Current field value const value = (0, inbuilt_fn_1.abolish_Get)(validated, field, this.fieldsHasDotNotation); // Check if skip rule is set if (compiled.$skip) { let $skip = compiled.$skip; // Run skip if it is a function if (typeof $skip === "function") { $skip = $skip(value, validated); } if ($skip) { // if field is not in included fields, remove it from fields if (this.includedFields && !this.includedFields.includes(field)) { fields = fields.filter((f) => f !== field); } continue; } } /** * Loop through all compiled validators */ for (const validatorName in compiled.validators) { const validator = compiled.validators[validatorName]; let result = false; try { if (validator.async) { result = (await validator.func(value, validated)); } else { result = validator.func(value, validated); } } catch (e) { return [ { code: "default", key: field, type: "internal", validator: validator.name, message: e.message, data: e.stack }, {} ]; } if (typeof result !== undefined && (result === false || (0, inbuilt_fn_1.InstanceOf)(AbolishError_1.default, result))) { return parseErrorMessage(field, value, result, validator, compiled.$name); } } } let result; if (fields.length === 1) { const onlyField = fields[0]; result = { [onlyField]: validated[onlyField] }; } else if (fields.length > 1) { result = (0, inbuilt_fn_1.abolish_Pick)(validated, fields, this.fieldsHasDotNotation); } else { result = {}; } return [undefined, result]; } /** * Validate a variable using a variable compiled input * @param variable Variable to validate * @returns */ validateVariable(variable) { if (this.isObject) { throw new Error("Object compiled cannot be used to validate a variable, use regular compiled input!"); } this.isObject = true; // set to true to avoid error const data = this.validateObject({ variable }); this.isObject = false; // set back to false // get variable from data data[1] = data[1].variable; return data; } /** * validateVariable async version * @param variable Variable to validate * @returns */ async validateVariableAsync(variable) { if (this.isObject) { throw new Error("Object compiled cannot be used to validate a variable, use regular compiled input!"); } this.isObject = true; // set to true to avoid error const data = await this.validateObjectAsync({ variable }); this.isObject = false; // set back to false // get variable from data data[1] = data[1].variable; return data; } validate(value) { return this.isObject ? this.validateObject(value) : this.validateVariable(value); } async validateAsync(value) { return this.isObject ? this.validateObjectAsync(value) : this.validateVariableAsync(value); } /** * Get `this.input` as AbolishRule */ getInputRule() { return this.input; } /** * Get `this.input` as AbolishSchema */ getInputSchema() { return this.input; } /** * Change a fields validator option * @param fieldName * @param validatorName * @param option */ setValidatorOption(validatorName, option, fieldName) { if (!fieldName) { if (this.isObject) { throw new Error("Field name is required when using object compiled input!"); } else { fieldName = "variable"; } } else if (fieldName && !this.isObject) { throw new Error("Field name is not allowed when using variable compiled input!"); } if (this.data[fieldName] && this.data[fieldName].validators[validatorName]) { this.data[fieldName].validators[validatorName].option = option; } return this; } /** * Copy current compiled instance * This is useful when you want to use the same compiled input for multiple validation * It prevents memory leak */ copy() { const copy = new AbolishCompiled(this.input); // set other properties copy.fields = this.fields; copy.includedFields = this.includedFields; copy.fieldsHasDotNotation = this.fieldsHasDotNotation; copy.isObject = this.isObject; copy.async = this.async; copy.data = {}; // copy data for (const field in this.data) { // copy validators const validators = {}; for (const validatorName in this.data[field].validators) { validators[validatorName] = { ...this.data[field].validators[validatorName] }; } copy.data[field] = { $name: this.data[field].$name, $skip: this.data[field].$skip, validators }; } return copy; } } exports.AbolishCompiled = AbolishCompiled; /** * Parse Error Message * @param field - Field * @param value - Value * @param result - Result * @param validator - Validator * @param $name - Name of field * @returns */ function parseErrorMessage(field, value, result, validator, $name) { let message = validator.error; let data = null; let code = "default"; let modifiedMessage = false; if (validator.customError) { if (validator.errorFn) { modifiedMessage = true; message = validator.errorFn({ code, data, validator: validator.name, value }); } } else { // noinspection SuspiciousTypeOfGuard if ((0, inbuilt_fn_1.InstanceOf)(AbolishError_1.default, result)) { result = result; modifiedMessage = true; message = result.message; data = result.data; code = result.code; } } if (modifiedMessage) { /** * Replace :param with rule converted to upperCase * and if option is stringAble, replace :option with validatorOption */ if (message.includes(":param")) { // Replace all :param with field name message = message.replace(":param", $name || field); } if (validator.optionString && message.includes(":option")) message = message.replace(":option", validator.optionString); } // Return Error using the ValidationResult format return [ { code, key: field, type: "validator", validator: validator.name, message, data }, {} ]; }