@decaf-ts/decorator-validation
Version:
simple decorator based validation engine
125 lines • 5.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseValidator = void 0;
const constants_1 = require("./constants.cjs");
const strings_1 = require("./../../utils/strings.cjs");
const utils_1 = require("./utils.cjs");
/**
* @description Abstract base class for all validators in the validation framework.
* @summary The BaseValidator class provides the foundation for all synchronous and asynchronous validator implementations.
* It handles type checking, error message formatting, and defines the interface that all validators must implement.
* This class is designed to be extended by specific validator classes that define their own validation logic.
*
* @template V - Validator options type
* @template IsAsync - Whether the validator is async (true) or sync (false). Default `false`.
*
* @param {boolean} async - Defines if the validator is async (must match the subclass signature)
* @param {string} message - Default error message to display when validation fails (defaults to {@link DEFAULT_ERROR_MESSAGES#DEFAULT})
* @param {string[]} acceptedTypes - Type names that this validator accepts (used for runtime type checking)
*
* @class BaseValidator
* @abstract
*
* @example
* // Example of a synchronous validator
* class SyncValidator extends BaseValidator<SomeOptions, false> {
* constructor() {
* super(false, "Sync validation failed", String.name);
* }
*
* public hasErrors(value: any, options?: SomeOptions): string | undefined {
* if (typeof value !== "string") return this.getMessage(this.message);
* return undefined;
* }
* }
*
* @example
* // Example of an asynchronous custom validator
* class AsyncValidator extends BaseValidator<SomeOptions, true> {
* constructor() {
* super(true, "Async validation failed", String.name);
* }
*
* public async hasErrors(value: any, options?: SomeOptions): Promise<string | undefined> {
* const result = await someAsyncCheck(value);
* if (!result) return this.getMessage(this.message);
* return undefined;
* }
* }
*
* @mermaid
* sequenceDiagram
* participant C as Client
* participant V as Validator Subclass
* participant B as BaseValidator
*
* C->>V: new CustomValidator(async, message)
* V->>B: super(async, message, acceptedTypes)
* B->>B: Store message, async flag, and accepted types
* B->>B: Optionally wrap hasErrors with type checking
* C->>V: hasErrors(value, options)
* alt value type not in acceptedTypes
* B-->>C: Type error message
* else value type is accepted
* V->>V: Custom validation logic
* V-->>C: Validation result
* end
*
* @category Validators
*/
class BaseValidator {
constructor(async, message = constants_1.DEFAULT_ERROR_MESSAGES.DEFAULT, ...acceptedTypes) {
this.async = async;
this.message = message;
if (acceptedTypes.length)
this.acceptedTypes = acceptedTypes;
if (this.acceptedTypes)
this.hasErrors = this.checkTypeAndHasErrors(this.hasErrors.bind(this));
}
/**
* @description Formats an error message with optional arguments
* @summary Creates a formatted error message by replacing placeholders with provided arguments.
* This method uses the string formatting utility to generate consistent error messages
* across all validators.
*
* @param {string} message - The message template with placeholders
* @param {...any} args - Values to insert into the message template
* @return {string} The formatted error message
* @protected
*/
getMessage(message, ...args) {
return (0, strings_1.sf)(message, ...args);
}
/**
* @description Creates a type-checking wrapper around the hasErrors method
* @summary Wraps the hasErrors method with type validation logic to ensure that
* the value being validated is of an accepted type before performing specific validation.
* This method is called during construction if acceptedTypes are provided.
*
* @param {Function} unbound - The original hasErrors method to be wrapped
* @return {Function} A new function that performs type checking before calling the original method
* @private
*/
checkTypeAndHasErrors(unbound) {
return function (value, options, proxy, ...args) {
if (value === undefined || !this.acceptedTypes)
return unbound(value, options, proxy, ...args);
if (!(0, utils_1.checkTypes)(value, this.acceptedTypes))
return this.getMessage(constants_1.DEFAULT_ERROR_MESSAGES.TYPE, this.acceptedTypes.join(", "), typeof value !== "number"
? typeof value
: isNaN(value)
? "NaN"
: typeof value);
return unbound(value, options, proxy, ...args);
}.bind(this);
}
/**
* @summary Duck typing for Validators
* @param val
*/
static isValidator(val) {
return val.constructor && !!val["hasErrors"];
}
}
exports.BaseValidator = BaseValidator;
//# sourceMappingURL=BaseValidator.js.map