UNPKG

fortify-schema

Version:

A modern TypeScript validation library designed around familiar interface syntax and powerful conditional validation. Experience schema validation that feels natural to TypeScript developers while unlocking advanced runtime validation capabilities.

172 lines (142 loc) 4.7 kB
import { FieldValidationResult, ValidationResult, } from "../../../../types/extension.type"; import { SchemaInterface } from "../../../../types/SchemaValidator.type"; import { ValidationEngine } from "../../mods"; /** * Live validator for real-time validation */ export class LiveValidator { private currentData: any = {}; private currentErrors: Record<string, string[]> = {}; private listeners: Array<(result: ValidationResult) => void> = []; private fieldListeners: Record< string, Array<(result: FieldValidationResult) => void> > = {}; constructor(private schema: SchemaInterface) {} /** * Validate a single field in real-time */ validateField(fieldName: string, value: any): FieldValidationResult { this.currentData[fieldName] = value; // Check if schema is an Interface schema (has definition property) let fieldSchema; if ( (this.schema as any).definition && (this.schema as any).definition[fieldName] ) { fieldSchema = (this.schema as any).definition[fieldName]; } else { fieldSchema = (this.schema as any)[fieldName]; } // console.log("field: ", fieldSchema); // console.log("fieldName: ", fieldName); const validationResult = ValidationEngine.validateField(fieldSchema, value); this.currentErrors[fieldName] = validationResult.errors; const result: FieldValidationResult = { field: fieldName, value, isValid: validationResult.isValid, errors: validationResult.errors, }; // Notify field listeners if (this.fieldListeners[fieldName]) { this.fieldListeners[fieldName].forEach((listener) => listener(result)); } // Notify global listeners this.notifyListeners(); // Validate all this.validateAll(); return result; } /** * Validate all current data */ validateAll(): ValidationResult { // Check if schema is an Interface schema (has safeParse method) if ((this.schema as any).safeParse) { // Use Interface schema's built-in validation const validationResult = (this.schema as any).safeParse(this.currentData); const errors: Record<string, string[]> = {}; if (!validationResult.success && validationResult.errors) { // Convert Interface errors to LiveValidator format Object.entries(validationResult.errors).forEach(([key, value]) => { errors[key] = Array.isArray(value) ? value : [String(value)]; }); } this.currentErrors = errors; const result: ValidationResult = { isValid: validationResult.success, data: this.currentData, errors: this.currentErrors, timestamp: new Date(), }; // Notify listeners after creating the result this.listeners.forEach((listener) => listener(result)); return result; } else { // Fallback to ValidationEngine for other schema types const validationResult = ValidationEngine.validateObject( this.schema, this.currentData ); // Update current errors with validation results this.currentErrors = validationResult.errors; const result: ValidationResult = { isValid: validationResult.isValid, data: this.currentData, errors: this.currentErrors, timestamp: new Date(), }; // Notify listeners after creating the result this.listeners.forEach((listener) => listener(result)); return result; } } /** * Listen for validation changes */ onValidation(listener: (result: ValidationResult) => void): void { this.listeners.push(listener); } /** * Listen for specific field validation */ onFieldValidation( fieldName: string, listener: (result: FieldValidationResult) => void ): void { if (!this.fieldListeners[fieldName]) { this.fieldListeners[fieldName] = []; } this.fieldListeners[fieldName].push(listener); } /** * Get current validation state */ get isValid(): boolean { return Object.values(this.currentErrors).every( (errors) => errors.length === 0 ); } get errors(): Record<string, string[]> { return { ...this.currentErrors }; } get data(): any { return { ...this.currentData }; } private notifyListeners(): void { // Create validation result without calling validateAll to avoid recursion const result: ValidationResult = { isValid: Object.values(this.currentErrors).every( (errors) => errors.length === 0 ), data: this.currentData, errors: this.currentErrors, timestamp: new Date(), }; this.listeners.forEach((listener) => listener(result)); } }