import fs from "fs"; interface VerificationResult { valid: boolean; errors: string[]; } /** * Verifies that an enriched JSON file matches the expected schema. * Returns detailed error messages for any violations. */ export function verifyEnrichedFile(filePath: string): VerificationResult { const errors: string[] = []; if (!fs.existsSync(filePath)) { return { valid: false, errors: ["File does not exist"] }; } let data: unknown; try { data = JSON.parse(fs.readFileSync(filePath, "utf-8")); } catch (_error: unknown) { return { valid: false, errors: ["Invalid JSON syntax"] }; } if (typeof data !== "object" || data === null || Array.isArray(data)) { return { valid: false, errors: ["Root must be an object"] }; } const obj = data as Record; // Required top-level fields const requiredFields = ["word", "language", "pos", "senses"]; for (const field of requiredFields) { if (!(field in obj)) { errors.push(`Missing required field: "${field}"`); } } // Validate senses array if (!Array.isArray(obj["senses"])) { errors.push('"senses" must be an array'); } else if (obj["senses"].length === 0) { errors.push('"senses" array cannot be empty'); } else { for (let i = 0; i < obj["senses"].length; i++) { const sense = obj["senses"][i] as Record; const sensePrefix = `senses[${i}]`; if (!sense["sense"] || typeof sense["sense"] !== "string") { errors.push(`${sensePrefix}: missing or invalid "sense"`); } if (!sense["example"] || typeof sense["example"] !== "string") { errors.push(`${sensePrefix}: missing or invalid "example"`); } if ( !["easy", "medium", "hard"].includes( sense["difficulty_level"] as string, ) ) { errors.push(`${sensePrefix}: invalid "difficulty_level"`); } if (!sense["translations"] || typeof sense["translations"] !== "object") { errors.push(`${sensePrefix}: missing "translations"`); } else { const trans = sense["translations"] as Record; for (const lang of ["de", "it", "es", "fr"]) { if (!Array.isArray(trans[lang])) { errors.push( `${sensePrefix}: missing or invalid "${lang}" translations`, ); } else { for (let j = 0; j < (trans[lang] as unknown[]).length; j++) { const t = (trans[lang] as unknown[])[j] as Record< string, unknown >; if (!t["word"] || typeof t["word"] !== "string") { errors.push(`${sensePrefix}.${lang}[${j}]: missing "word"`); } if ( !["masculine", "feminine", "neuter", null].includes( t["gender"] as string | null, ) ) { errors.push(`${sensePrefix}.${lang}[${j}]: invalid "gender"`); } } } } } } } return { valid: errors.length === 0, errors }; }