UNPKG

altheia-async-data-validator

Version:

A very simple, fast and customizable async data validator

198 lines (197 loc) 6.72 kB
"use strict"; var __awaiter = (this && this.__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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeBase = void 0; const createTest_1 = require("../utils/createTest"); // Return an object and call a callback if needed const returnOrCallback = (result, callback) => { if (callback) { callback(result); } return result; }; /** * All type inherit this Class */ class TypeBase { /** * Constructor * * @param {Altheia} inst */ constructor(inst) { this.createTest = createTest_1.createTest; this.createTestResult = createTest_1.createTestResult; // eslint-disable-next-line global-require this.inst = inst || require('./index'); this.tests = []; this._required = false; this._needCast = false; } /** * Clone this class */ clone() { const clone = Object.assign(Object.create(this), this); // Quick deep clone clone.tests = this.tests.slice(0); return clone; } /** * Add a test * * @param name * @param func * @param args */ test(name, func, args = {}) { this.tests.push(() => { return this.createTest({ type: `${this.name}.${name}`, func, args, }); }); } /** * Validate a value based on all tests added * @param {mixed} toTest * @param {Function} callback */ validate(toTest, callback) { return __awaiter(this, void 0, void 0, function* () { // Test presence early to fail/pass early const presence = this.presence(toTest); if (presence === false) { if (this._required === false) { return returnOrCallback(false, callback); } return returnOrCallback(this.createTestResult(this.createTest({ type: 'required', }), false), callback); } if (this._needCast) { /* eslint-disable no-param-reassign */ toTest = yield this._cast(toTest); /* eslint-enable no-param-reassign */ } // Iterate all tests for (let i = 0; i < this.tests.length; i++) { const test = this.tests[i](); // Special condition for IF() we need to display error of deep validation const internalResult = this.testToTestResult(yield test.func(toTest)); // Let test override current test type/arguments // Helps when you encapsulate test inside other test and want a transparent error // e.g: `if()` if (internalResult.overrideWith) { test.type = internalResult.overrideWith.type; test.args = internalResult.overrideWith.args; } if (typeof internalResult.error === 'undefined' || typeof internalResult.valid === 'undefined') { throw new Error('test() should return a boolean or an object { valid: boolean, error: string }'); } // Do not go deeper in test if (!internalResult.valid) { return returnOrCallback(this.createTestResult(test, internalResult.valid, internalResult), callback); } } return returnOrCallback(false, callback); }); } /** * Force the value to be non-null */ required() { this._required = true; return this; } /** * Force the value to be casted before any check */ cast() { this._needCast = true; return this; } /** * Custom validator * * @param {string} name * @param {Function} callback * @param {Function} message */ custom(name, callback) { this.test(`custom.${name}`, (value) => __awaiter(this, void 0, void 0, function* () { try { return yield callback(value); } catch (e) { return false; } }), {}); return this; } /** * Presence validation * * @param {mixed} toTest */ presence(toTest) { if (toTest === null || typeof toTest === 'undefined' || toTest === undefined) { return false; } if (typeof toTest === 'string' && toTest.length <= 0) { // If the flag is passed to true // we consider the string valid and can be deeply tested, // this will trigger the noEmpty() test if (this._noEmpty) { return true; } return false; } return true; } /** * If validation * * @param {function} options.test * @param {function} options.then * @param {function} options.otherwise */ if({ test, then, otherwise, }) { this.test('if', (str) => __awaiter(this, void 0, void 0, function* () { const clone = this.clone(); clone.tests = []; const hasError = yield test(clone).validate(str); if (!hasError) { clone.tests = []; const temp = yield then(clone).validate(str); return temp && temp.result ? Object.assign(Object.assign({}, temp.result), { overrideWith: temp }) : true; } clone.tests = []; const temp = yield otherwise(clone).validate(str); return temp && temp.result ? Object.assign(Object.assign({}, temp.result), { overrideWith: temp }) : true; })); return this; } testToTestResult(result) { if (typeof result === 'boolean') { return { valid: result, error: '' }; } return result; } } exports.TypeBase = TypeBase; exports.default = TypeBase;