| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function typeMatches(schemaType, value) { |
| const list = Array.isArray(schemaType) ? schemaType : [schemaType]; |
| for (const t of list) { |
| if (t === 'null' && value === null) return true; |
| if (t === 'string' && typeof value === 'string') return true; |
| if (t === 'number' && typeof value === 'number') return true; |
| if (t === 'integer' && Number.isInteger(value)) return true; |
| if (t === 'boolean' && typeof value === 'boolean') return true; |
| if (t === 'array' && Array.isArray(value)) return true; |
| if (t === 'object' && value !== null && typeof value === 'object' && !Array.isArray(value)) return true; |
| } |
| return false; |
| } |
|
|
| export function validate(schema, value, path = '$') { |
| const errors = []; |
| if (!schema || typeof schema !== 'object') return errors; |
| if (schema.type && !typeMatches(schema.type, value)) { |
| errors.push(`${path}: expected ${JSON.stringify(schema.type)}, got ${value === null ? 'null' : typeof value}`); |
| return errors; |
| } |
| if (Array.isArray(schema.enum) && !schema.enum.includes(value)) { |
| errors.push(`${path}: value not in enum ${JSON.stringify(schema.enum)}`); |
| } |
| if (value === null || value === undefined) return errors; |
| if (Array.isArray(value) && schema.items) { |
| for (let i = 0; i < value.length; i++) { |
| errors.push(...validate(schema.items, value[i], `${path}[${i}]`)); |
| } |
| } |
| if (value && typeof value === 'object' && !Array.isArray(value)) { |
| if (Array.isArray(schema.required)) { |
| for (const key of schema.required) { |
| if (!(key in value)) errors.push(`${path}.${key}: required key missing`); |
| } |
| } |
| if (schema.properties) { |
| for (const [key, subSchema] of Object.entries(schema.properties)) { |
| if (key in value) errors.push(...validate(subSchema, value[key], `${path}.${key}`)); |
| } |
| } |
| if (schema.additionalProperties === false) { |
| const known = new Set(Object.keys(schema.properties ?? {})); |
| for (const key of Object.keys(value)) { |
| if (!known.has(key)) errors.push(`${path}.${key}: additional property not allowed`); |
| } |
| } else if (schema.additionalProperties && typeof schema.additionalProperties === 'object') { |
| const known = new Set(Object.keys(schema.properties ?? {})); |
| for (const [key, val] of Object.entries(value)) { |
| if (known.has(key)) continue; |
| errors.push(...validate(schema.additionalProperties, val, `${path}.${key}`)); |
| } |
| } |
| } |
| return errors; |
| } |
|
|