Skip to main content
Glama
mhmzdev

Figma to Flutter MCP Server

by mhmzdev
validation.ts5.92 kB
// utils/validation.mts import {FigmaError} from '../types/errors.js'; /** * Utility class for validating Figma-related inputs */ export class FigmaValidator { /** * Validate Figma file ID * @param fileId The file ID to validate * @param fieldName Optional field name for error messages */ static validateFileId(fileId: unknown, fieldName = 'fileId'): string { if (typeof fileId !== 'string') { throw new FigmaError(`${fieldName} must be a string`, 'VALIDATION_ERROR'); } const trimmed = fileId.trim(); if (trimmed.length === 0) { throw new FigmaError(`${fieldName} cannot be empty`, 'VALIDATION_ERROR'); } if (trimmed.length < 10 || trimmed.length > 50) { throw new FigmaError( `${fieldName} length must be between 10-50 characters, got ${trimmed.length}`, 'VALIDATION_ERROR' ); } const validPattern = /^[a-zA-Z0-9\-_]+$/; if (!validPattern.test(trimmed)) { throw new FigmaError( `${fieldName} contains invalid characters. Only alphanumeric, hyphens, and underscores allowed`, 'VALIDATION_ERROR' ); } return trimmed; } /** * Validate Figma node ID * @param nodeId The node ID to validate * @param fieldName Optional field name for error messages */ static validateNodeId(nodeId: unknown, fieldName = 'nodeId'): string { if (typeof nodeId !== 'string') { throw new FigmaError(`${fieldName} must be a string`, 'VALIDATION_ERROR'); } const trimmed = nodeId.trim(); if (trimmed.length === 0) { throw new FigmaError(`${fieldName} cannot be empty`, 'VALIDATION_ERROR'); } // Figma node IDs are typically in format "number:number" const nodePattern = /^\d+:\d+$/; if (!nodePattern.test(trimmed)) { throw new FigmaError( `${fieldName} must be in format "123:456", got "${trimmed}"`, 'VALIDATION_ERROR' ); } return trimmed; } /** * Validate depth parameter * @param depth The depth to validate * @param fieldName Optional field name for error messages */ static validateDepth(depth: unknown, fieldName = 'depth'): number { if (depth === undefined || depth === null) { return 3; // Default depth } if (typeof depth !== 'number' || !Number.isInteger(depth)) { throw new FigmaError(`${fieldName} must be an integer`, 'VALIDATION_ERROR'); } if (depth < 1 || depth > 10) { throw new FigmaError( `${fieldName} must be between 1-10, got ${depth}`, 'VALIDATION_ERROR' ); } return depth; } /** * Validate max components parameter * @param maxComponents The max components to validate * @param fieldName Optional field name for error messages */ static validateMaxComponents(maxComponents: unknown, fieldName = 'maxComponents'): number { if (maxComponents === undefined || maxComponents === null) { return 10; // Default max components } if (typeof maxComponents !== 'number' || !Number.isInteger(maxComponents)) { throw new FigmaError(`${fieldName} must be an integer`, 'VALIDATION_ERROR'); } if (maxComponents < 1 || maxComponents > 100) { throw new FigmaError( `${fieldName} must be between 1-100, got ${maxComponents}`, 'VALIDATION_ERROR' ); } return maxComponents; } /** * Validate and sanitize widget name for Flutter * @param name The widget name to validate * @param fieldName Optional field name for error messages */ static validateWidgetName(name: unknown, fieldName = 'widgetName'): string { if (typeof name !== 'string') { throw new FigmaError(`${fieldName} must be a string`, 'VALIDATION_ERROR'); } const trimmed = name.trim(); if (trimmed.length === 0) { throw new FigmaError(`${fieldName} cannot be empty`, 'VALIDATION_ERROR'); } // Check for valid Dart/Flutter class name const validDartName = /^[A-Z][a-zA-Z0-9_]*$/; if (!validDartName.test(trimmed)) { throw new FigmaError( `${fieldName} "${trimmed}" is not a valid Flutter class name. Must start with uppercase letter and contain only letters, numbers, and underscores`, 'VALIDATION_ERROR' ); } return trimmed; } /** * Comprehensive validation for tool inputs * @param inputs Object containing inputs to validate */ static validateToolInputs(inputs: { fileId?: unknown; nodeId?: unknown; depth?: unknown; maxComponents?: unknown; widgetName?: unknown; }): { fileId?: string; nodeId?: string; depth?: number; maxComponents?: number; widgetName?: string; } { const result: any = {}; if (inputs.fileId !== undefined) { result.fileId = this.validateFileId(inputs.fileId); } if (inputs.nodeId !== undefined) { result.nodeId = this.validateNodeId(inputs.nodeId); } if (inputs.depth !== undefined) { result.depth = this.validateDepth(inputs.depth); } if (inputs.maxComponents !== undefined) { result.maxComponents = this.validateMaxComponents(inputs.maxComponents); } if (inputs.widgetName !== undefined) { result.widgetName = this.validateWidgetName(inputs.widgetName); } return result; } }

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/mhmzdev/figma-flutter-mcp'

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