Skip to main content
Glama
validation-utils.ts5.69 kB
/** * Validation utility functions */ import { ValidationError, ValidationResult, ValidationSeverity } from './types'; /** * Utility functions for validation operations */ export class ValidationUtils { /** * Create a validation error with common defaults */ static createError( code: string, message: string, severity: ValidationSeverity = ValidationSeverity.ERROR, options: Partial<ValidationError> = {} ): ValidationError { return { code, message, severity, ...options }; } /** * Merge multiple validation results */ static mergeResults(...results: ValidationResult[]): ValidationResult { const errors: ValidationError[] = []; const warnings: ValidationError[] = []; const info: ValidationError[] = []; for (const result of results) { errors.push(...result.errors); warnings.push(...result.warnings); info.push(...result.info); } return { valid: errors.length === 0, errors, warnings, info, summary: { errorCount: errors.length, warningCount: warnings.length, infoCount: info.length } }; } /** * Format validation error for display */ static formatError(error: ValidationError): string { let formatted = `[${error.severity.toUpperCase()}] ${error.code}: ${error.message}`; if (error.path) { formatted += ` (at ${error.path})`; } if (error.expected && error.actual !== undefined) { formatted += `\n Expected: ${error.expected}`; formatted += `\n Actual: ${error.actual}`; } if (error.specReference) { formatted += `\n Reference: ${error.specReference}`; } return formatted; } /** * Calculate schema complexity score */ static calculateComplexity(schema: Record<string, any>): number { if (!schema || typeof schema !== 'object') { return 0; } let complexity = 1; // Base complexity // Add complexity for nested properties if (schema.properties && typeof schema.properties === 'object') { complexity += Object.keys(schema.properties).length; // Recursively calculate nested complexity for (const value of Object.values(schema.properties)) { if (value && typeof value === 'object') { complexity += ValidationUtils.calculateComplexity(value as Record<string, any>) * 0.5; } } } return Math.round(complexity * 10) / 10; } /** * Format a path array into a dot-notation string */ static formatPath(path: string[]): string { if (!path || path.length === 0) { return ''; } let formatted = path[0] || ''; for (let i = 1; i < path.length; i++) { const segment = path[i] || ''; if (/^\d+$/.test(segment)) { // Array index formatted += `[${segment}]`; } else { // Object property formatted += `.${segment}`; } } return formatted; } /** * Check if a string is a valid JSON Schema type */ static isValidJsonSchemaType(type: string): boolean { if (!type || typeof type !== 'string') { return false; } const validTypes = ['string', 'number', 'integer', 'boolean', 'array', 'object', 'null']; return validTypes.includes(type); } /** * Merge multiple validation results (alias for mergeResults) */ static mergeValidationResults(...results: ValidationResult[]): ValidationResult { return ValidationUtils.mergeResults(...results); } /** * Validate required fields are present in data */ static validateRequired(data: any, required: string[]): ValidationError[] { const errors: ValidationError[] = []; if (!data || typeof data !== 'object') { // If data is null/undefined, all required fields are missing for (const field of required) { errors.push(ValidationUtils.createError( 'REQUIRED_FIELD_MISSING', `Required field '${field}' is missing`, ValidationSeverity.ERROR, { path: field } )); } return errors; } for (const field of required) { if (!(field in data) || data[field] === undefined || data[field] === null) { errors.push(ValidationUtils.createError( 'REQUIRED_FIELD_MISSING', `Required field '${field}' is missing`, ValidationSeverity.ERROR, { path: field } )); } } return errors; } /** * Sanitize error messages to prevent XSS */ static sanitizeErrorMessage(message: any): string { if (!message) { return ''; } const str = String(message); // Remove HTML tags and script content return str .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '') .replace(/<[^>]*>/g, '') .replace(/javascript:/gi, '') .replace(/on\w+\s*=/gi, ''); } /** * Get numeric level for error severity */ static getErrorSeverityLevel(severity: ValidationSeverity): number { switch (severity) { case ValidationSeverity.ERROR: return 3; case ValidationSeverity.WARNING: return 2; case ValidationSeverity.INFO: return 1; default: return 0; } } /** * Sort errors by severity (highest first) */ static sortErrorsBySeverity(errors: ValidationError[]): ValidationError[] { return [...errors].sort((a, b) => { const levelA = ValidationUtils.getErrorSeverityLevel(a.severity); const levelB = ValidationUtils.getErrorSeverityLevel(b.severity); return levelB - levelA; // Descending order (highest first) }); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/learnwithcc/tally-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server