UNPKG

perfect-validator

Version:

A TypeScript-based validation library that supports both static and dynamic validation with serializable models.

346 lines (318 loc) 9.64 kB
import { PerfectValidator } from './types'; import { validateAgainstModel, validateDataModel } from './validators'; import { deserializeValidationModel, serializeValidationModel, getValidationTypeParams, } from './utils'; import userProfileModel, { testCases } from './utils/model'; export class PV { private storage?: PerfectValidator.IModelStorage; private static instance: PV | null = null; constructor(storage?: PerfectValidator.IModelStorage) { this.storage = storage; } /** * Static validation with direct model and data */ public validateStatic<T>( data: T, model: PerfectValidator.ValidationModel, allowUnknownFields?: boolean ): PerfectValidator.ValidationResponse<T> { const modelValidation: PerfectValidator.ModelValidationResponse = this.validateModel( model ); if (!modelValidation.isValid && modelValidation.errors) { return { isValid: false, errors: modelValidation.errors.map(error => ({ field: 'model', message: error, })) as PerfectValidator.ValidationError[], }; } return validateAgainstModel(data, model , allowUnknownFields); } public static getInstance(storage?: PerfectValidator.IModelStorage): PV { if (!PV.instance) { try { PV.instance = new PV(storage); } catch (error) { if (error instanceof Error) { throw new Error(`Failed to create PV instance: ${error.message}`); } throw new Error('Failed to create PV instance: Unknown error'); } } return PV.instance; } /** * Dynamic validation using stored model with optional collection */ public async validateDynamic<T>( data: T, modelName: string, version?: number, collection?: string, allowUnknownFields?: boolean ): Promise<PerfectValidator.ValidationResponse<T>> { if (!this.storage) { throw new Error('Storage is required for dynamic validation'); } try { let serializedModel: string | null; if (version !== undefined) { const modelVersion = await this.storage.getModelVersion( modelName, version, collection ); if (!modelVersion) { throw new Error(`Model ${modelName} version ${version} not found`); } serializedModel = modelVersion.model; } else { const latestVersion = await this.storage.getLatestModelVersion( modelName, collection ); if (!latestVersion) { throw new Error(`Model ${modelName} not found`); } serializedModel = latestVersion.model; } if (!serializedModel) { throw new Error(`Model ${modelName} not found`); } const model: PerfectValidator.ValidationModel = deserializeValidationModel( serializedModel ); return validateAgainstModel(data, model, allowUnknownFields); } catch (error) { if (error instanceof Error) { return { isValid: false, errors: [ { field: 'model', message: `Failed to load model: ${error.message}`, }, ], }; } return { isValid: false, errors: [ { field: 'model', message: 'Failed to load model: Unknown error', }, ], }; } } /** * Store model with validation and optional collection */ public async storeModel( modelName: string, model: PerfectValidator.ValidationModel, version?: number, collection?: string ): Promise<PerfectValidator.ModelValidationResponse> { try { if (!this.storage) { throw new Error('Storage is required for model storage'); } // Validate and store model with optional collection const modelValidation = this.validateModel(model); if (!modelValidation.isValid) { throw new Error( `Model validation failed: ${modelValidation.errors?.join(', ')}` ); } const serialized = await this.serializeModelSafely(model); const deserialized = await this.deserializeAndValidate(serialized); if (version !== undefined) { await this.storage.storeModelVersion( modelName, serialized, version, collection ); } else { const latestVersion = await this.storage.getLatestModelVersion( modelName, collection ); const newVersion = latestVersion ? latestVersion.version + 1 : 1; await this.storage.storeModelVersion( modelName, serialized, newVersion, collection ); } return { isValid: true, errors: null }; } catch (error) { if (error instanceof Error) { return { isValid: false, errors: [`Failed to store model: ${error.message}`], }; } return { isValid: false, errors: ['Failed to store model: Unknown error'], }; } } /** * Validate model structure */ public validateModel( model: PerfectValidator.ValidationModel ): PerfectValidator.ModelValidationResponse { return validateDataModel(model); } private async serializeModelSafely( model: PerfectValidator.ValidationModel ): Promise<string> { try { return serializeValidationModel(model); } catch (error) { if (error instanceof Error) { throw new Error(`Model serialization failed: ${error.message}`); } throw new Error('Model serialization failed: Unknown error'); } } private async deserializeAndValidate( serialized: string ): Promise<PerfectValidator.ValidationModel> { try { const model: PerfectValidator.ValidationModel = deserializeValidationModel( serialized ); const validation: PerfectValidator.ModelValidationResponse = validateDataModel( model ); if (!validation.isValid) { throw new Error( `Invalid model after deserialization: ${validation.errors?.join( ', ' ) || 'Unknown error'}` ); } return model; } catch (error) { if (error instanceof Error) { throw new Error(`Model deserialization failed: ${error.message}`); } throw new Error('Model deserialization failed: Unknown error'); } } /** * Get all supported data types with descriptions */ public getDataTypes(): Record<string, PerfectValidator.ValidationType> { return PerfectValidator.ValidationTypes; } /** * Get example model with different validation cases */ public getModelExample(): PerfectValidator.ValidationModel { return userProfileModel; } /** * Get example data matching the model example */ public getDataExample(): Array<{ name: string; data: any }> { return testCases; } public getValidationTypeParams( type: PerfectValidator.ValidationType ): PerfectValidator.IValidationTypeParams { try { return getValidationTypeParams(type); } catch (error) { if (error instanceof Error) { throw new Error( `Failed to get validation type params: ${error.message}` ); } throw new Error('Failed to get validation type params: Unknown error'); } } // Add new utility methods for version management public async getLatestModelVersion( modelName: string, collection?: string ): Promise<PerfectValidator.ValidationModel | null> { if (!this.storage) { throw new Error('Storage is required'); } const latestVersion = await this.storage.getLatestModelVersion( modelName, collection ); if (!latestVersion) { throw new Error(`Model ${modelName} not found`); } return deserializeValidationModel(latestVersion.model); } /** * Get model of a specific version * @param modelName Name of the model * @param version Version number of the model * @returns The model version or null if not found */ public async getModelVersion( modelName: string, version: number, collection?: string ): Promise<PerfectValidator.ValidationModel | null> { if (!this.storage) { throw new Error('Storage is required'); } try { const modelVersion: PerfectValidator.ModelVersion | null = await this.storage.getModelVersion( modelName, version, collection ); if (!modelVersion) { throw new Error(`Model ${modelName} version ${version} not found`); } return deserializeValidationModel(modelVersion.model); } catch (error) { if (error instanceof Error) { throw new Error(`Failed to get model version: ${error.message}`); } throw new Error('Failed to get model version: Unknown error'); } } /** * Get list of all versions for a model * @param modelName Name of the model * @param collection Optional collection name * @returns Array of version numbers in descending order (newest first) */ public async listModelVersions( modelName: string, collection?: string ): Promise<number[]> { if (!this.storage) { throw new Error('Storage is required'); } try { return await this.storage.listModelVersions(modelName, collection); } catch (error) { if (error instanceof Error) { throw new Error(`Failed to list model versions: ${error.message}`); } throw new Error('Failed to list model versions: Unknown error'); } } }