Extraction du cœur autonomie de calculs.trans-former.fr (12 modules, sans singleton, couplages sans mutation, console derrière debug), découpage en trois moteurs d'axe + intégrateur (mode global = calculs, mode par_axe = plateforme), moteurs commun.contexte (Corse 2A/2B, DOM 97x corrigés) et commun.geometrie, table des coefficients harmonisés (docs/CONSTANTES.md pour Jules, R-7), validation structurelle des données (schemas/), tests hors ligne : 28 assertions d'origine + épingle générée depuis le code d'astro-pro + écarts d'harmonisation figés + contrat des moteurs. dist/ committé. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UBHYPdeQYy2m1GVJp6b12
78 lines
3.0 KiB
JavaScript
78 lines
3.0 KiB
JavaScript
// valider.ts — validation structurelle d'un JSON de données contre le schéma
|
|
// de sa table (schemas/<table>.schema.json, incarné par schemas.ts). Sans
|
|
// dépendance, sans I/O. Ce n'est pas un validateur JSON Schema complet : type,
|
|
// required, properties, items, enum, minItems.
|
|
import { SCHEMAS } from './schemas.js';
|
|
const MAX_ERREURS = 20;
|
|
function typeDe(v) {
|
|
if (v === null)
|
|
return 'null';
|
|
if (Array.isArray(v))
|
|
return 'array';
|
|
if (typeof v === 'number')
|
|
return Number.isInteger(v) ? 'integer' : 'number';
|
|
if (typeof v === 'string')
|
|
return 'string';
|
|
if (typeof v === 'boolean')
|
|
return 'boolean';
|
|
return 'object';
|
|
}
|
|
function typeAccepte(attendu, reel) {
|
|
const liste = Array.isArray(attendu) ? attendu : [attendu];
|
|
return liste.some((t) => t === reel || (t === 'number' && reel === 'integer'));
|
|
}
|
|
function verifie(schema, valeur, chemin, erreurs) {
|
|
if (erreurs.length >= MAX_ERREURS)
|
|
return;
|
|
const reel = typeDe(valeur);
|
|
if (!typeAccepte(schema.type, reel)) {
|
|
erreurs.push(`${chemin} : type ${reel}, attendu ${Array.isArray(schema.type) ? schema.type.join('|') : schema.type}`);
|
|
return;
|
|
}
|
|
if (schema.enum && !schema.enum.includes(valeur)) {
|
|
erreurs.push(`${chemin} : valeur « ${String(valeur)} » hors de ${schema.enum.map(String).join(', ')}`);
|
|
}
|
|
if (reel === 'array') {
|
|
const tableau = valeur;
|
|
if (schema.minItems != null && tableau.length < schema.minItems) {
|
|
erreurs.push(`${chemin} : ${tableau.length} élément(s), minimum ${schema.minItems}`);
|
|
}
|
|
if (schema.items) {
|
|
for (let i = 0; i < tableau.length && erreurs.length < MAX_ERREURS; i++) {
|
|
verifie(schema.items, tableau[i], `${chemin}[${i}]`, erreurs);
|
|
}
|
|
}
|
|
}
|
|
else if (reel === 'object') {
|
|
const objet = valeur;
|
|
for (const cle of schema.required ?? []) {
|
|
if (!(cle in objet) || objet[cle] === undefined)
|
|
erreurs.push(`${chemin}.${cle} : requis, absent`);
|
|
}
|
|
if (schema.properties) {
|
|
for (const [cle, sous] of Object.entries(schema.properties)) {
|
|
if (cle in objet && objet[cle] !== undefined)
|
|
verifie(sous, objet[cle], `${chemin}.${cle}`, erreurs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Valide un JSON de données contre le schéma de sa table. Renvoie au plus
|
|
* vingt erreurs, chacune avec son chemin.
|
|
*/
|
|
export function valider(table, json) {
|
|
const schema = SCHEMAS[table];
|
|
if (!schema)
|
|
return { ok: false, erreurs: [`table inconnue : ${String(table)}`] };
|
|
const erreurs = [];
|
|
verifie(schema, json, table, erreurs);
|
|
return { ok: erreurs.length === 0, erreurs };
|
|
}
|
|
/** Valide un objet contre un schéma arbitraire (même sous-ensemble). */
|
|
export function validerContre(schema, json, nom = '$') {
|
|
const erreurs = [];
|
|
verifie(schema, json, nom, erreurs);
|
|
return { ok: erreurs.length === 0, erreurs };
|
|
}
|