UNPKG

@decaf-ts/decorator-validation

Version:
613 lines 25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validationMetadata = validationMetadata; exports.async = async; exports.required = required; exports.min = min; exports.max = max; exports.step = step; exports.minlength = minlength; exports.maxlength = maxlength; exports.pattern = pattern; exports.email = email; exports.url = url; exports.type = type; exports.date = date; exports.password = password; exports.list = list; exports.set = set; exports.eq = eq; exports.diff = diff; exports.lt = lt; exports.lte = lte; exports.gt = gt; exports.gte = gte; require("reflect-metadata"); const constants_1 = require("./Validators/constants.cjs"); const strings_1 = require("./../utils/strings.cjs"); const dates_1 = require("./../utils/dates.cjs"); const decorators_1 = require("./../utils/decorators.cjs"); const Validation_1 = require("./Validation.cjs"); const Decoration_1 = require("./../utils/Decoration.cjs"); const reflection_1 = require("@decaf-ts/reflection"); const constants_2 = require("./../constants/index.cjs"); /** * @description Combined property decorator factory for metadata and attribute marking * @summary Creates a decorator that both marks a property as a model attribute and assigns metadata to it * * @template V * @param {PropertyDecorator} decorator - The metadata key * @param {string} key - The metadata key * @param {V} value - The metadata value to associate with the property * @return {Function} - Combined decorator function * @function validationMetadata * @category Property Decorators */ function validationMetadata(decorator, key, value) { Validation_1.Validation.registerDecorator(key, decorator); return (0, reflection_1.apply)((0, decorators_1.propMetadata)(key, value)); } function async() { return (model) => { if (!Object.prototype.hasOwnProperty.call(model, constants_2.ASYNC_META_KEY)) model[constants_2.ASYNC_META_KEY] = true; }; } /** * @description Property decorator that marks a field as required * @summary Marks the property as required, causing validation to fail if the property is undefined, null, or empty. * Validators to validate a decorated property must use key {@link ValidationKeys#REQUIRED}. * This decorator is commonly used as the first validation step for important fields. * * @param {string} [message] - The error message to display when validation fails. Defaults to {@link DEFAULT_ERROR_MESSAGES#REQUIRED} * @return {PropertyDecorator} A decorator function that can be applied to class properties * * @function required * @category Property Decorators * * @example * ```typescript * class User { * @required() * username: string; * * @required("Email address is mandatory") * email: string; * } * ``` */ function required(message = constants_1.DEFAULT_ERROR_MESSAGES.REQUIRED) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.REQUIRED); const meta = { message: message, description: `defines the attribute as required`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [required, key, meta], }) .apply(); } /** * @description Property decorator that enforces a minimum value constraint * @summary Defines a minimum value for the property, causing validation to fail if the property value is less than the specified minimum. * Validators to validate a decorated property must use key {@link ValidationKeys#MIN}. * This decorator works with numeric values and dates. * * @param {number | Date | string} value - The minimum value allowed. For dates, can be a Date object or a string that can be converted to a date * @param {string} [message] - The error message to display when validation fails. Defaults to {@link DEFAULT_ERROR_MESSAGES#MIN} * @return {PropertyDecorator} A decorator function that can be applied to class properties * * @function min * @category Property Decorators * * @example * ```typescript * class Product { * @min(0) * price: number; * * @min(new Date(2023, 0, 1), "Date must be after January 1, 2023") * releaseDate: Date; * } * ``` */ function min(value, message = constants_1.DEFAULT_ERROR_MESSAGES.MIN) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.MIN); const meta = { [constants_1.ValidationKeys.MIN]: value, message: message, types: [Number.name, Date.name], description: `defines the max value of the attribute as ${value} (applies to numbers or Dates)`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [min, key, meta], }) .apply(); } /** * @summary Defines a maximum value for the property * @description Validators to validate a decorated property must use key {@link ValidationKeys#MAX} * * @param {number | Date} value * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#MAX} * * @function max * @category Property Decorators */ function max(value, message = constants_1.DEFAULT_ERROR_MESSAGES.MAX) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.MAX); const meta = { [constants_1.ValidationKeys.MAX]: value, message: message, types: [Number.name, Date.name], description: `defines the max value of the attribute as ${value} (applies to numbers or Dates)`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [max, key, meta], }) .apply(); } /** * @summary Defines a step value for the property * @description Validators to validate a decorated property must use key {@link ValidationKeys#STEP} * * @param {number} value * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#STEP} * * @function step * @category Property Decorators */ function step(value, message = constants_1.DEFAULT_ERROR_MESSAGES.STEP) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.STEP); const meta = { [constants_1.ValidationKeys.STEP]: value, message: message, types: [Number.name], description: `defines the step of the attribute as ${value}`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [step, key, meta], }) .apply(); } /** * @summary Defines a minimum length for the property * @description Validators to validate a decorated property must use key {@link ValidationKeys#MIN_LENGTH} * * @param {string} value * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#MIN_LENGTH} * * @function minlength * @category Property Decorators */ function minlength(value, message = constants_1.DEFAULT_ERROR_MESSAGES.MIN_LENGTH) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.MIN_LENGTH); const meta = { [constants_1.ValidationKeys.MIN_LENGTH]: value, message: message, types: [String.name, Array.name, Set.name], description: `defines the min length of the attribute as ${value} (applies to strings or lists)`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [minlength, key, meta], }) .apply(); } /** * @summary Defines a maximum length for the property * @description Validators to validate a decorated property must use key {@link ValidationKeys#MAX_LENGTH} * * @param {string} value * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#MAX_LENGTH} * * @function maxlength * @category Property Decorators */ function maxlength(value, message = constants_1.DEFAULT_ERROR_MESSAGES.MAX_LENGTH) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.MAX_LENGTH); const meta = { [constants_1.ValidationKeys.MAX_LENGTH]: value, message: message, types: [String.name, Array.name, Set.name], description: `defines the max length of the attribute as ${value} (applies to strings or lists)`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [maxlength, key, meta], }) .apply(); } /** * @summary Defines a RegExp pattern the property must respect * @description Validators to validate a decorated property must use key {@link ValidationKeys#PATTERN} * * @param {string} value * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#PATTERN} * * @function pattern * @category Property Decorators */ function pattern(value, message = constants_1.DEFAULT_ERROR_MESSAGES.PATTERN) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.PATTERN); const meta = { [constants_1.ValidationKeys.PATTERN]: typeof value === "string" ? value : value.toString(), message: message, types: [String.name], description: `assigns the ${value === "string" ? value : value.toString()} pattern to the attribute`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [pattern, key, meta], }) .apply(); } /** * @summary Defines the property as an email * @description Validators to validate a decorated property must use key {@link ValidationKeys#EMAIL} * * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#EMAIL} * * @function email * @category Property Decorators */ function email(message = constants_1.DEFAULT_ERROR_MESSAGES.EMAIL) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.EMAIL); const meta = { [constants_1.ValidationKeys.PATTERN]: constants_1.DEFAULT_PATTERNS.EMAIL.toString(), message: message, types: [String.name], description: "marks the attribute as an email", async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [email, key, meta], }) .apply(); } /** * @summary Defines the property as an URL * @description Validators to validate a decorated property must use key {@link ValidationKeys#URL} * * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#URL} * * @function url * @category Property Decorators */ function url(message = constants_1.DEFAULT_ERROR_MESSAGES.URL) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.URL); const meta = { [constants_1.ValidationKeys.PATTERN]: constants_1.DEFAULT_PATTERNS.URL.toString(), message: message, types: [String.name], description: "marks the attribute as an url", async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [url, key, meta], }) .apply(); } /** * @summary Enforces type verification * @description Validators to validate a decorated property must use key {@link ValidationKeys#TYPE} * * @param {string[] | string} types accepted types * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#TYPE} * * @function type * @category Property Decorators */ function type(types, message = constants_1.DEFAULT_ERROR_MESSAGES.TYPE) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.TYPE); const meta = { customTypes: types, message: message, description: "defines the accepted types for the attribute", async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: (validationMetadata), args: [type, key, meta], }) .apply(); } /** * @summary Date Handler Decorator * @description Validators to validate a decorated property must use key {@link ValidationKeys#DATE} * * Will enforce serialization according to the selected format * * @param {string} format accepted format according to {@link formatDate} * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#DATE} * * @function date * * @category Property Decorators */ function date(format = "dd/MM/yyyy", message = constants_1.DEFAULT_ERROR_MESSAGES.DATE) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.DATE); function dateDec(format, message) { const meta = { [constants_1.ValidationKeys.FORMAT]: format, message: message, types: [Date.name], description: `defines the attribute as a date with the format ${format}`, async: false, }; return function dateDec(target, propertyKey) { const values = new WeakMap(); Object.defineProperty(target, propertyKey, { configurable: false, set(newValue) { const descriptor = Object.getOwnPropertyDescriptor(this, propertyKey); if (!descriptor || descriptor.configurable) Object.defineProperty(this, propertyKey, { enumerable: true, configurable: false, get: () => values.get(this), set: (newValue) => { let val; try { val = (0, dates_1.parseDate)(format, newValue); values.set(this, val); } catch (e) { console.error((0, strings_1.sf)("Failed to parse date: {0}", e.message || e)); } }, }); this[propertyKey] = newValue; }, get() { return values.get(this); }, }); return validationMetadata(date, key, meta)(target, propertyKey); }; } return Decoration_1.Decoration.for(key) .define({ decorator: dateDec, args: [format, message], }) .apply(); } /** * @summary Password Handler Decorator * @description Validators to validate a decorated property must use key {@link ValidationKeys#PASSWORD} * * @param {RegExp} [pattern] defaults to {@link DEFAULT_PATTERNS#CHAR8_ONE_OF_EACH} * @param {string} [message] the error message. Defaults to {@link DEFAULT_ERROR_MESSAGES#PASSWORD} * * @function password * * @category Property Decorators */ function password(pattern = constants_1.DEFAULT_PATTERNS.PASSWORD.CHAR8_ONE_OF_EACH, message = constants_1.DEFAULT_ERROR_MESSAGES.PASSWORD) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.PASSWORD); const meta = { [constants_1.ValidationKeys.PATTERN]: pattern.toString(), message: message, types: [String.name], description: `attribute as a password`, async: false, }; return Decoration_1.Decoration.for(key) .define({ decorator: validationMetadata, args: [password, key, meta], }) .apply(); } /** * @summary List Decorator * @description Also sets the {@link type} to the provided collection * * @param {ModelConstructor} clazz * @param {string} [collection] The collection being used. defaults to Array * @param {string} [message] defaults to {@link DEFAULT_ERROR_MESSAGES#LIST} * * @function list * * @category Property Decorators */ function list(clazz, collection = "Array", message = constants_1.DEFAULT_ERROR_MESSAGES.LIST) { const key = Validation_1.Validation.key(constants_1.ValidationKeys.LIST); const meta = { clazz: (Array.isArray(clazz) ? clazz.map((c) => (c.name ? c.name : c)) : [clazz.name ? clazz.name : clazz]), type: collection, message: message, async: false, description: `defines the attribute as a ${collection} of ${clazz.name}`, }; return Decoration_1.Decoration.for(key) .define({ decorator: validationMetadata, args: [list, key, meta], }) .apply(); } /** * @summary Set Decorator * @description Wrapper for {@link list} with the 'Set' Collection * * @param {ModelConstructor} clazz * @param {string} [message] defaults to {@link DEFAULT_ERROR_MESSAGES#LIST} * * @function set * * @category Property Decorators */ function set(clazz, message = constants_1.DEFAULT_ERROR_MESSAGES.LIST) { return list(clazz, "Set", message); } /** * @summary Declares that the decorated property must be equal to another specified property. * @description Applies the {@link ValidationKeys.EQUALS} validator to ensure the decorated value matches the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare equality against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.EQUALS] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the equality validation metadata. * * @function eq * @category Property Decorators */ function eq(propertyToCompare, options // message: string = DEFAULT_ERROR_MESSAGES.EQUALS ) { const equalsOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.EQUALS, [constants_1.ValidationKeys.EQUALS]: propertyToCompare, description: `defines attribute as equal to ${propertyToCompare}`, }; return validationMetadata(eq, Validation_1.Validation.key(constants_1.ValidationKeys.EQUALS), { ...equalsOptions, async: false }); } /** * @summary Declares that the decorated property must be different from another specified property. * @description Applies the {@link ValidationKeys.DIFF} validator to ensure the decorated value is different from the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare difference against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.DIFF] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the difference validation metadata. * * @function diff * @category Property Decorators */ function diff(propertyToCompare, options) { const diffOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.DIFF, [constants_1.ValidationKeys.DIFF]: propertyToCompare, description: `defines attribute as different to ${propertyToCompare}`, }; return validationMetadata(diff, Validation_1.Validation.key(constants_1.ValidationKeys.DIFF), { ...diffOptions, async: false, }); } /** * @summary Declares that the decorated property must be less than another specified property. * @description Applies the {@link ValidationKeys.LESS_THAN} validator to ensure the decorated value is less than the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.LESS_THAN] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the less than validation metadata. * * @function lt * @category Property Decorators */ function lt(propertyToCompare, options) { const ltOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.LESS_THAN, [constants_1.ValidationKeys.LESS_THAN]: propertyToCompare, description: `defines attribute as less than to ${propertyToCompare}`, }; return validationMetadata(lt, Validation_1.Validation.key(constants_1.ValidationKeys.LESS_THAN), { ...ltOptions, async: false }); } /** * @summary Declares that the decorated property must be equal or less than another specified property. * @description Applies the {@link ValidationKeys.LESS_THAN_OR_EQUAL} validator to ensure the decorated value is equal or less than the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.LESS_THAN_OR_EQUAL] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the less than or equal validation metadata. * * @function lte * @category Property Decorators */ function lte(propertyToCompare, options) { const lteOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.LESS_THAN_OR_EQUAL, [constants_1.ValidationKeys.LESS_THAN_OR_EQUAL]: propertyToCompare, description: `defines attribute as less or equal to ${propertyToCompare}`, }; return validationMetadata(lte, Validation_1.Validation.key(constants_1.ValidationKeys.LESS_THAN_OR_EQUAL), { ...lteOptions, async: false }); } /** * @summary Declares that the decorated property must be greater than another specified property. * @description Applies the {@link ValidationKeys.GREATER_THAN} validator to ensure the decorated value is greater than the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.GREATER_THAN] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the greater than validation metadata. * * @function gt * @category Property Decorators */ function gt(propertyToCompare, options) { const gtOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.GREATER_THAN, [constants_1.ValidationKeys.GREATER_THAN]: propertyToCompare, description: `defines attribute as greater than ${propertyToCompare}`, }; return validationMetadata(gt, Validation_1.Validation.key(constants_1.ValidationKeys.GREATER_THAN), { ...gtOptions, async: false }); } /** * @summary Declares that the decorated property must be equal or greater than another specified property. * @description Applies the {@link ValidationKeys.GREATER_THAN_OR_EQUAL} validator to ensure the decorated value is equal or greater than the value of the given property. * * @param {string} propertyToCompare - The name of the property to compare against. * @param {ComparisonValidatorOptions} options - Options for the validator. * @param {string} [options.label] - The label text displayed in the error message. * @param {string} [options.message=DEFAULT_ERROR_MESSAGES.GREATER_THAN_OR_EQUAL] - Custom error message to be returned if validation fails. * * @returns {PropertyDecorator} A property decorator used to register the greater than or equal validation metadata. * * @function gte * @category Property Decorators */ function gte(propertyToCompare, options) { const gteOptions = { label: options?.label || propertyToCompare, message: options?.message || constants_1.DEFAULT_ERROR_MESSAGES.GREATER_THAN_OR_EQUAL, [constants_1.ValidationKeys.GREATER_THAN_OR_EQUAL]: propertyToCompare, description: `defines attribute as greater or equal to ${propertyToCompare}`, }; return validationMetadata(gte, Validation_1.Validation.key(constants_1.ValidationKeys.GREATER_THAN_OR_EQUAL), { ...gteOptions, async: false }); } //# sourceMappingURL=decorators.js.map