UNPKG

@decaf-ts/decorator-validation

Version:
405 lines (404 loc) 18.4 kB
import { BuilderRegistry } from "../utils/registry"; import { ModelErrorDefinition } from "./ModelErrorDefinition"; import { Comparable, Constructor, Hashable, ModelArg, ModelBuilderFunction, ModelConstructor, Serializable, Validatable } from "./types"; import { ConditionalAsync } from "../types"; /** * @description Registry type for storing and retrieving model constructors * @summary The ModelRegistry type defines a registry for model constructors that extends * the BuilderRegistry interface. It provides a standardized way to register, retrieve, * and build model instances, enabling the model system to work with different types of models. * * @interface ModelRegistry * @template T Type of model that can be registered, must extend Model * @extends BuilderRegistry<T> * @memberOf module:decorator-validation * @category Model */ export type ModelRegistry<T extends Model> = BuilderRegistry<T>; /** * @description Registry manager for model constructors that enables serialization and rebuilding * @summary The ModelRegistryManager implements the ModelRegistry interface and provides * functionality for registering, retrieving, and building model instances. It maintains * a cache of model constructors indexed by name, allowing for efficient lookup and instantiation. * This class is essential for the serialization and deserialization of model objects. * * @param {function(Record<string, any>): boolean} [testFunction] - Function to test if an object is a model, defaults to {@link Model#isModel} * * @class ModelRegistryManager * @template M Type of model that can be registered, must extend Model * @implements ModelRegistry<M> * @category Model * * @example * ```typescript * // Create a model registry * const registry = new ModelRegistryManager(); * * // Register a model class * registry.register(User); * * // Retrieve a model constructor by name * const UserClass = registry.get("User"); * * // Build a model instance from a plain object * const userData = { name: "John", age: 30 }; * const user = registry.build(userData, "User"); * ``` * * @mermaid * sequenceDiagram * participant C as Client * participant R as ModelRegistryManager * participant M as Model Class * * C->>R: new ModelRegistryManager(testFunction) * C->>R: register(ModelClass) * R->>R: Store in cache * C->>R: get("ModelName") * R-->>C: ModelClass constructor * C->>R: build(data, "ModelName") * R->>R: Get constructor from cache * R->>M: new ModelClass(data) * M-->>R: Model instance * R-->>C: Model instance */ export declare class ModelRegistryManager<M extends Model<true | false>> implements ModelRegistry<M> { private cache; private readonly testFunction; constructor(testFunction?: (obj: Record<string, any>) => boolean); /** * @description Registers a model constructor with the registry * @summary Adds a model constructor to the registry cache, making it available for * later retrieval and instantiation. If no name is provided, the constructor's name * property is used as the key in the registry. * * @param {ModelConstructor<M>} constructor - The model constructor to register * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name * @return {void} * @throws {Error} If the constructor is not a function */ register(constructor: ModelConstructor<M>, name?: string): void; /** * @summary Gets a registered Model {@link ModelConstructor} * @param {string} name */ get(name: string): ModelConstructor<M> | undefined; /** * @param {Record<string, any>} obj * @param {string} [clazz] when provided, it will attempt to find the matching constructor * * @throws Error If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property */ build(obj?: Record<string, any>, clazz?: string): M; } /** * @summary Bulk Registers Models * @description Useful when using bundlers that might not evaluate all the code at once * * @template M extends Model * @param {Array<Constructor<M>> | Array<{name: string, constructor: Constructor<M>}>} [models] * * @memberOf module:decorator-validation * @category Model */ export declare function bulkModelRegister<M extends Model>(...models: (Constructor<M> | { name: string; constructor: Constructor<M>; })[]): void; /** * @summary Abstract class representing a Validatable Model object * @description Meant to be used as a base class for all Model classes * * Model objects must: * - Have all their required properties marked with '!'; * - Have all their optional properties marked as '?': * * @param {ModelArg<Model>} model base object from which to populate properties from * * @class Model * @category Model * @abstract * @implements Validatable * @implements Serializable * * @example * class ClassName { * @required() * requiredPropertyName!: PropertyType; * * optionalPropertyName?: PropertyType; * } */ export declare abstract class Model<Async extends boolean = false> implements Validatable<Async>, Serializable, Hashable, Comparable<Model<Async>> { protected constructor(arg?: ModelArg<Model> | undefined); isAsync(): boolean; /** * @description Validates the model object against its defined validation rules * @summary Validates the object according to its decorated properties, returning any validation errors * * @param {any[]} [exceptions] - Properties in the object to be ignored for the validation. Marked as 'any' to allow for extension but expects strings * @return {ModelErrorDefinition | undefined} - Returns a ModelErrorDefinition object if validation errors exist, otherwise undefined */ hasErrors(...exceptions: any[]): ConditionalAsync<Async, ModelErrorDefinition | undefined>; /** * @description Determines if this model is equal to another object * @summary Compare object equality recursively, checking all properties unless excluded * * @param {any} obj - Object to compare to * @param {string[]} [exceptions] - Property names to be excluded from the comparison * @return {boolean} - True if objects are equal, false otherwise */ equals(obj: any, ...exceptions: string[]): boolean; /** * @description Converts the model to a serialized string representation * @summary Returns the serialized model according to the currently defined {@link Serializer} * * @return {string} - The serialized string representation of the model */ serialize(): string; /** * @description Provides a human-readable string representation of the model * @summary Override the implementation for js's 'toString()' to provide a more useful representation * * @return {string} - A string representation of the model including its class name and JSON representation * @override */ toString(): string; /** * @description Generates a hash string for the model object * @summary Defines a default implementation for object hash, relying on a basic implementation based on Java's string hash * * @return {string} - A hash string representing the model */ hash(): string; /** * @description Converts a serialized string back into a model instance * @summary Deserializes a Model from its string representation * * @param {string} str - The serialized string to convert back to a model * @return {any} - The deserialized model instance * @throws {Error} If it fails to parse the string, or if it fails to build the model */ static deserialize(str: string): any; /** * @description Copies properties from a source object to a model instance * @summary Repopulates the Object properties with the ones from the new object * * @template T * @param {T} self - The target model instance to update * @param {T | Record<string, any>} [obj] - The source object containing properties to copy * @return {T} - The updated model instance */ static fromObject<T extends Model<any>>(self: T, obj?: T | Record<string, any>): T; /** * @description Copies and rebuilds properties from a source object to a model instance, handling nested models * @summary Repopulates the instance with properties from the new Model Object, recursively rebuilding nested models * * @template T * @param {T} self - The target model instance to update * @param {T | Record<string, any>} [obj] - The source object containing properties to copy * @return {T} - The updated model instance with rebuilt nested models * * @mermaid * sequenceDiagram * participant C as Client * participant M as Model.fromModel * participant B as Model.build * participant R as Reflection * * C->>M: fromModel(self, obj) * M->>M: Get attributes from self * loop For each property * M->>M: Copy property from obj to self * alt Property is a model * M->>M: Check if property is a model * M->>B: build(property, modelType) * B-->>M: Return built model * else Property is a complex type * M->>R: Get property decorators * R-->>M: Return decorators * M->>M: Filter type decorators * alt Property is Array/Set with list decorator * M->>M: Process each item in collection * loop For each item * M->>B: build(item, itemModelType) * B-->>M: Return built model * end * else Property is another model type * M->>B: build(property, propertyType) * B-->>M: Return built model * end * end * end * M-->>C: Return updated self */ static fromModel<T extends Model>(self: T, obj?: T | Record<string, any>): T; /** * @description Configures the global model builder function * @summary Sets the Global {@link ModelBuilderFunction} used for building model instances * * @param {ModelBuilderFunction} [builder] - The builder function to set as the global builder * @return {void} */ static setBuilder(builder?: ModelBuilderFunction): void; /** * @description Retrieves the currently configured global model builder function * @summary Returns the current global {@link ModelBuilderFunction} used for building model instances * * @return {ModelBuilderFunction | undefined} - The current global builder function or undefined if not set */ static getBuilder(): ModelBuilderFunction | undefined; /** * @description Provides access to the current model registry * @summary Returns the current {@link ModelRegistryManager} instance, creating one if it doesn't exist * * @return {ModelRegistry<any>} - The current model registry, defaults to a new {@link ModelRegistryManager} if not set * @private */ private static getRegistry; /** * @description Configures the model registry to be used by the Model system * @summary Sets the current model registry to a custom implementation * * @param {BuilderRegistry<any>} modelRegistry - The new implementation of Registry to use * @return {void} */ static setRegistry(modelRegistry: BuilderRegistry<any>): void; /** * @description Registers a model constructor with the model registry * @summary Registers new model classes to make them available for serialization and deserialization * * @template T * @param {ModelConstructor<T>} constructor - The model constructor to register * @param {string} [name] - Optional name to register the constructor under, defaults to constructor.name * @return {void} * * @see ModelRegistry */ static register<T extends Model>(constructor: ModelConstructor<T>, name?: string): void; /** * @description Retrieves a registered model constructor by name * @summary Gets a registered Model {@link ModelConstructor} from the model registry * * @template T * @param {string} name - The name of the model constructor to retrieve * @return {ModelConstructor<T> | undefined} - The model constructor if found, undefined otherwise * * @see ModelRegistry */ static get<T extends Model>(name: string): ModelConstructor<T> | undefined; /** * @description Creates a model instance from a plain object * @summary Builds a model instance using the model registry, optionally specifying the model class * * @template T * @param {Record<string, any>} obj - The source object to build the model from * @param {string} [clazz] - When provided, it will attempt to find the matching constructor by name * @return {T} - The built model instance * @throws {Error} If clazz is not found, or obj is not a {@link Model} meaning it has no {@link ModelKeys.ANCHOR} property * * @see ModelRegistry */ static build<T extends Model>(obj?: Record<string, any>, clazz?: string): T; /** * @description Retrieves the model metadata from a model instance * @summary Gets the metadata associated with a model instance, typically the model class name * * @template M * @param {M} model - The model instance to get metadata from * @return {string} - The model metadata (typically the class name) */ static getMetadata<M extends Model>(model: M): any; /** * @description Retrieves all attribute names from a model class or instance * @summary Gets all attributes defined in a model, traversing the prototype chain to include inherited attributes * * @template V * @param {Constructor<V> | V} model - The model class or instance to get attributes from * @return {string[]} - Array of attribute names defined in the model */ static getAttributes<V extends Model>(model: Constructor<V> | V): string[]; /** * @description Compares two model instances for equality * @summary Determines if two model instances are equal by comparing their properties * * @template M * @param {M} obj1 - First model instance to compare * @param {M} obj2 - Second model instance to compare * @param {any[]} [exceptions] - Property names to exclude from comparison * @return {boolean} - True if the models are equal, false otherwise */ static equals<M extends Model>(obj1: M, obj2: M, ...exceptions: any[]): boolean; /** * @description Validates a model instance against its validation rules * @summary Checks if a model has validation errors, optionally ignoring specified properties * * @template M * @param {M} model - The model instance to validate * @param {boolean} async - A flag indicating whether validation should be asynchronous. * @param {string[]} [propsToIgnore] - Properties to exclude from validation * @return {ModelErrorDefinition | undefined} - Returns validation errors if any, otherwise undefined */ static hasErrors<M extends Model, Async extends boolean = false>(model: M, async: Async, ...propsToIgnore: string[]): ConditionalAsync<Async, ModelErrorDefinition | undefined>; /** * @description Converts a model instance to a serialized string * @summary Serializes a model instance using the configured serializer or the default one * * @template M * @param {M} model - The model instance to serialize * @return {string} - The serialized string representation of the model */ static serialize<M extends Model<boolean>>(model: M): any; /** * @description Generates a hash string for a model instance * @summary Creates a hash representation of a model using the configured algorithm or the default one * * @template M * @param {M} model - The model instance to hash * @return {string} - The hash string representing the model */ static hash<M extends Model<boolean>>(model: M): any; /** * @description Creates a metadata key for use with the Reflection API * @summary Builds the key to store as Metadata under Reflections * * @param {string} str - The base key to concatenate with the model reflection prefix * @return {string} - The complete metadata key */ static key(str: string): string; /** * @description Determines if an object is a model instance or has model metadata * @summary Checks whether a given object is either an instance of the Model class or * has model metadata attached to it. This function is essential for serialization and * deserialization processes, as it helps identify model objects that need special handling. * It safely handles potential errors during metadata retrieval. * * @param {Record<string, any>} target - The object to check * @return {boolean} True if the object is a model instance or has model metadata, false otherwise * * @example * ```typescript * // Check if an object is a model * const user = new User({ name: "John" }); * const isUserModel = isModel(user); // true * * // Check a plain object * const plainObject = { name: "John" }; * const isPlainObjectModel = isModel(plainObject); // false * ``` */ static isModel(target: Record<string, any>): boolean; /** * @description Checks if a property of a model is itself a model or has a model type * @summary Determines whether a specific property of a model instance is either a model instance * or has a type that is registered as a model * * @template M * @param {M} target - The model instance to check * @param {string} attribute - The property name to check * @return {boolean | string | undefined} - Returns true if the property is a model instance, * the model name if the property has a model type, or undefined if not a model */ static isPropertyModel<M extends Model>(target: M, attribute: string): boolean | string | undefined; static describe<M extends Model>(model: M | Constructor<M>, key?: keyof M): any; }