Phillnet-2 / Tools /utils /schemaValidator.ts
ayjays132's picture
Upload 478 files
101858b verified
export class SchemaValidator {
/**
* Validates data against a JSON schema.
* @returns null if valid, or a string describing the error if invalid.
*/
static validate(schema: any, data: any): string | null {
if (!schema) return null;
// Handle cases where schema is just a string (name of type)
const type = typeof schema === 'string' ? schema : schema.type;
if (type === 'object') {
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
return `Expected object, got ${typeof data}`;
}
const properties = schema.properties || {};
const required = schema.required || [];
// Check required properties
for (const req of required) {
if (!(req in data)) {
return `Missing required property: ${req}`;
}
}
// Recursively validate properties
for (const key in data) {
if (properties[key]) {
const error = this.validate(properties[key], data[key]);
if (error) return `${key}: ${error}`;
}
}
} else if (type === 'array') {
if (!Array.isArray(data)) {
return `Expected array, got ${typeof data}`;
}
if (schema.items) {
for (let i = 0; i < data.length; i++) {
const error = this.validate(schema.items, data[i]);
if (error) return `item[${i}]: ${error}`;
}
}
} else if (type === 'string') {
if (typeof data !== 'string') return `Expected string, got ${typeof data}`;
if (schema.enum && !schema.enum.includes(data)) {
return `Value "${data}" not in enum [${schema.enum.join(', ')}]`;
}
} else if (type === 'number' || type === 'integer') {
if (typeof data !== 'number') return `Expected number, got ${typeof data}`;
} else if (type === 'boolean') {
if (typeof data !== 'boolean') return `Expected boolean, got ${typeof data}`;
}
return null;
}
}