/** * Manifest-based validation service * Validates values against manifest type declarations */ interface ManifestVariable { name: string; type: 'string' | 'integer' | 'number' | 'boolean' | 'array' | 'object'; required?: boolean; minimum?: number; maximum?: number; pattern?: string; default?: unknown; } export interface ManifestValidationResult { valid: boolean; errors?: string[]; } /** * Validate a value against manifest type declaration * * Performs basic type checking based on manifest.yml variable declarations. * For complex types (array, object), this only validates type - structure * validation requires JSON Schema. * * @param value - Value to validate (already parsed) * @param variable - Variable declaration from manifest * @returns Validation result */ export function validateAgainstManifest( value: unknown, variable: ManifestVariable, ): ManifestValidationResult { const errors: string[] = []; // Type validation switch (variable.type) { case 'string': if (typeof value !== 'string') { errors.push(`Expected string, got ${typeof value}`); } else if (variable.pattern) { const regex = new RegExp(variable.pattern); if (!regex.test(value)) { errors.push(`Value does not match pattern: ${variable.pattern}`); } } break; case 'integer': if (!Number.isInteger(value)) { errors.push(`Expected integer, got ${typeof value}`); } else if (typeof value === 'number') { if (variable.minimum !== undefined && value < variable.minimum) { errors.push(`Value must be >= ${variable.minimum}`); } if (variable.maximum !== undefined && value > variable.maximum) { errors.push(`Value must be <= ${variable.maximum}`); } } break; case 'number': if (typeof value !== 'number' || Number.isNaN(value)) { errors.push(`Expected number, got ${typeof value}`); } else { if (variable.minimum !== undefined && value < variable.minimum) { errors.push(`Value must be >= ${variable.minimum}`); } if (variable.maximum !== undefined && value > variable.maximum) { errors.push(`Value must be <= ${variable.maximum}`); } } break; case 'boolean': if (typeof value !== 'boolean') { errors.push(`Expected boolean, got ${typeof value}`); } break; case 'array': if (!Array.isArray(value)) { errors.push(`Expected array, got ${typeof value}`); } // Note: Array structure validation requires JSON Schema break; case 'object': if (typeof value !== 'object' || value === null || Array.isArray(value)) { errors.push(`Expected object, got ${typeof value}`); } // Note: Object structure validation requires JSON Schema break; default: errors.push(`Unknown type: ${variable.type}`); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : undefined, }; } /** * Format manifest validation errors for display */ export function formatManifestValidationErrors(errors: string[]): string { if (errors.length === 0) { return 'Validation failed'; } if (errors.length === 1) { return `Validation error: ${errors[0]}`; } return `Validation errors:\n${errors.map((err) => ` - ${err}`).join('\n')}`; }