Skip to main content
Glama

MCP Design System Bridge

sync.ts8.95 kB
import { ChangeSet, ValidationResult, ValidationError, ValidationWarning } from '../types/mcp'; export class SyncValidator { validate(changes: ChangeSet): ValidationResult { const errors: ValidationError[] = []; const warnings: ValidationWarning[] = []; // Validate components for (const change of changes.components) { if (change.type === 'add') { this.validateNewComponent(change, errors, warnings); } else if (change.type === 'update') { this.validateComponentUpdate(change, errors, warnings); } else if (change.type === 'delete') { this.validateComponentDeletion(change, errors, warnings); } } // Validate tokens for (const change of changes.tokens) { if (change.type === 'add') { this.validateNewToken(change, errors, warnings); } else if (change.type === 'update') { this.validateTokenUpdate(change, errors, warnings); } else if (change.type === 'delete') { this.validateTokenDeletion(change, errors, warnings); } } // Validate icons for (const change of changes.icons) { if (change.type === 'add') { this.validateNewIcon(change, errors, warnings); } else if (change.type === 'update') { this.validateIconUpdate(change, errors, warnings); } else if (change.type === 'delete') { this.validateIconDeletion(change, errors, warnings); } } // Validate images for (const change of changes.images) { if (change.type === 'add') { this.validateNewImage(change, errors, warnings); } else if (change.type === 'update') { this.validateImageUpdate(change, errors, warnings); } else if (change.type === 'delete') { this.validateImageDeletion(change, errors, warnings); } } return { valid: errors.length === 0, errors, warnings, }; } private validateNewComponent(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check required fields if (!details.name) { errors.push({ code: 'MISSING_NAME', message: 'Component name is required', path: change.path, }); } if (!details.props) { errors.push({ code: 'MISSING_PROPS', message: 'Component props are required', path: change.path, }); } // Check naming conventions if (details.name && !/^[A-Z][a-zA-Z0-9]*$/.test(details.name)) { errors.push({ code: 'INVALID_NAME', message: 'Component name must be PascalCase', path: change.path, }); } // Check for duplicate props const propNames = new Set<string>(); for (const prop of details.props || []) { if (propNames.has(prop.name)) { errors.push({ code: 'DUPLICATE_PROP', message: `Duplicate prop name: ${prop.name}`, path: change.path, }); } propNames.add(prop.name); } } private validateComponentUpdate(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check for breaking changes if (details.breakingChanges) { errors.push({ code: 'BREAKING_CHANGE', message: 'Breaking changes detected', path: change.path, }); } // Check for deprecated props if (details.deprecatedProps) { warnings.push({ code: 'DEPRECATED_PROP', message: 'Using deprecated props', path: change.path, }); } } private validateComponentDeletion(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { // Check if component is in use if (change.details.usageCount > 0) { errors.push({ code: 'COMPONENT_IN_USE', message: `Component is used in ${change.details.usageCount} places`, path: change.path, }); } } private validateNewToken(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check required fields if (!details.name) { errors.push({ code: 'MISSING_NAME', message: 'Token name is required', path: change.path, }); } if (!details.value) { errors.push({ code: 'MISSING_VALUE', message: 'Token value is required', path: change.path, }); } // Check naming conventions if (details.name && !/^[a-z][a-zA-Z0-9-]*$/.test(details.name)) { errors.push({ code: 'INVALID_NAME', message: 'Token name must be kebab-case', path: change.path, }); } } private validateTokenUpdate(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check for breaking changes if (details.breakingChanges) { errors.push({ code: 'BREAKING_CHANGE', message: 'Breaking changes detected', path: change.path, }); } // Check for deprecated tokens if (details.deprecated) { warnings.push({ code: 'DEPRECATED_TOKEN', message: 'Using deprecated token', path: change.path, }); } } private validateTokenDeletion(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { // Check if token is in use if (change.details.usageCount > 0) { errors.push({ code: 'TOKEN_IN_USE', message: `Token is used in ${change.details.usageCount} places`, path: change.path, }); } } private validateNewIcon(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check required fields if (!details.name) { errors.push({ code: 'MISSING_NAME', message: 'Icon name is required', path: change.path, }); } if (!details.svg) { errors.push({ code: 'MISSING_SVG', message: 'Icon SVG is required', path: change.path, }); } // Check naming conventions if (details.name && !/^[a-z][a-zA-Z0-9-]*$/.test(details.name)) { errors.push({ code: 'INVALID_NAME', message: 'Icon name must be kebab-case', path: change.path, }); } } private validateIconUpdate(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check for breaking changes if (details.breakingChanges) { errors.push({ code: 'BREAKING_CHANGE', message: 'Breaking changes detected', path: change.path, }); } // Check for deprecated icons if (details.deprecated) { warnings.push({ code: 'DEPRECATED_ICON', message: 'Using deprecated icon', path: change.path, }); } } private validateIconDeletion(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { // Check if icon is in use if (change.details.usageCount > 0) { errors.push({ code: 'ICON_IN_USE', message: `Icon is used in ${change.details.usageCount} places`, path: change.path, }); } } private validateNewImage(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check required fields if (!details.name) { errors.push({ code: 'MISSING_NAME', message: 'Image name is required', path: change.path, }); } if (!details.url) { errors.push({ code: 'MISSING_URL', message: 'Image URL is required', path: change.path, }); } // Check naming conventions if (details.name && !/^[a-z][a-zA-Z0-9-]*$/.test(details.name)) { errors.push({ code: 'INVALID_NAME', message: 'Image name must be kebab-case', path: change.path, }); } } private validateImageUpdate(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { const { details } = change; // Check for breaking changes if (details.breakingChanges) { errors.push({ code: 'BREAKING_CHANGE', message: 'Breaking changes detected', path: change.path, }); } // Check for deprecated images if (details.deprecated) { warnings.push({ code: 'DEPRECATED_IMAGE', message: 'Using deprecated image', path: change.path, }); } } private validateImageDeletion(change: any, errors: ValidationError[], warnings: ValidationWarning[]): void { // Check if image is in use if (change.details.usageCount > 0) { errors.push({ code: 'IMAGE_IN_USE', message: `Image is used in ${change.details.usageCount} places`, path: change.path, }); } } }

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/brunonepomuceno/mcp-design-system-bridge-cursor'

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