import { z } from 'zod'; export interface ValidationResult { valid: boolean; errors?: Array<{ path: string; message: string; expected?: string; received?: string; }>; warnings?: Array<{ path: string; message: string; }>; } /** * Validate configuration against a schema */ export function validateConfig(config: unknown, schema: z.ZodSchema): ValidationResult { try { schema.parse(config); return { valid: true }; } catch (error) { if (error instanceof z.ZodError) { const errors = error.errors.map(err => ({ path: err.path.join('.'), message: err.message, expected: 'expected' in err ? String(err.expected) : undefined, received: 'received' in err ? String(err.received) : undefined, })); return { valid: false, errors }; } throw error; } } /** * Check for required environment variables */ export function checkRequiredEnvVars(required: string[]): ValidationResult { const errors: ValidationResult['errors'] = []; for (const envVar of required) { if (!process.env[envVar]) { errors.push({ path: `env.${envVar}`, message: `Required environment variable ${envVar} is not set`, }); } } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, }; } /** * Validate configuration completeness */ export function validateCompleteness( config: Record, required: string[] ): ValidationResult { const errors: ValidationResult['errors'] = []; for (const path of required) { const keys = path.split('.'); let current: any = config; let found = true; for (const key of keys) { if (current && typeof current === 'object' && key in current) { current = current[key]; } else { found = false; break; } } if (!found || current === undefined || current === null) { errors.push({ path, message: `Required configuration value is missing`, }); } } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, }; } /** * Format validation result for display */ export function formatValidationResult(result: ValidationResult): string { const lines: string[] = []; if (result.valid) { lines.push('✅ Configuration is valid'); } else { lines.push('❌ Configuration validation failed'); } if (result.errors && result.errors.length > 0) { lines.push('\nErrors:'); for (const error of result.errors) { lines.push(` - ${error.path}: ${error.message}`); if (error.expected && error.received) { lines.push(` Expected: ${error.expected}, Received: ${error.received}`); } } } if (result.warnings && result.warnings.length > 0) { lines.push('\nWarnings:'); for (const warning of result.warnings) { lines.push(` - ${warning.path}: ${warning.message}`); } } return lines.join('\n'); } /** * Create a strict schema that doesn't allow extra properties */ export function createStrictSchema(shape: T): z.ZodObject { return z.object(shape).strict(); } /** * Merge multiple schemas */ export function mergeSchemas( ...schemas: T ): z.ZodIntersection { if (schemas.length < 2) { throw new Error('At least two schemas required for merge'); } let result = schemas[0]!.and(schemas[1]!); for (let i = 2; i < schemas.length; i++) { result = result.and(schemas[i]!) as any; } return result as any; }