From 9d5dc9ae7de5f531f7ddcafbfdf812c4029b374c Mon Sep 17 00:00:00 2001 From: Alejandro Lembke Barrientos Date: Wed, 23 Jul 2025 05:20:31 +0000 Subject: [PATCH] Fixing eslint issues --- eslint.config.js | 126 ++++++++++ src/@types/custom.d.ts | 2 +- src/ai/diagram-analyzer.ts | 102 ++++---- .../smart-architecture-generator.ts | 87 ++++--- src/generators/smart-bpmn-generator.ts | 63 +++-- src/generators/smart-er-generator.ts | 67 +++--- src/index.ts | 105 +++++---- src/tests/server.test.ts | 221 ------------------ src/tests/setup.ts | 38 --- src/tests/tools/create-diagram.test.ts | 1 - src/tools/create-diagram.ts | 84 +++---- src/utils/drawio-converter.ts | 106 ++++----- 12 files changed, 432 insertions(+), 570 deletions(-) create mode 100644 eslint.config.js delete mode 100644 src/tests/server.test.ts diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..04eb1f7 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,126 @@ +import js from '@eslint/js'; +import tseslint from '@typescript-eslint/eslint-plugin'; +import tsparser from '@typescript-eslint/parser'; + +export default [ + // Base JavaScript configuration + js.configs.recommended, + + // Configuration for TypeScript files + { + files: ['**/*.ts', '**/*.tsx'], + ignores: ['**/*.test.ts', '**/*.spec.ts', 'src/tests/**/*.ts'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + project: './tsconfig.json', + }, + globals: { + console: 'readonly', + process: 'readonly', + Buffer: 'readonly', + global: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + }, + rules: { + // TypeScript recommended rules + ...tseslint.configs.recommended.rules, + + // Custom rules for the project + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-var-requires': 'error', + + // General JavaScript/TypeScript rules + 'no-console': 'warn', + 'no-debugger': 'error', + 'no-duplicate-imports': 'error', + 'no-unused-expressions': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + + // Style rules + 'indent': ['error', 2], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + 'comma-dangle': ['error', 'always-multiline'], + 'object-curly-spacing': ['error', 'always'], + 'array-bracket-spacing': ['error', 'never'], + }, + }, + + // Specific configuration for test files + { + files: ['**/*.test.ts', '**/*.spec.ts', 'src/tests/**/*.ts'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + globals: { + console: 'readonly', + process: 'readonly', + Buffer: 'readonly', + global: 'writable', + describe: 'readonly', + it: 'readonly', + test: 'readonly', + expect: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + beforeAll: 'readonly', + afterAll: 'readonly', + jest: 'readonly', + require: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + }, + rules: { + // Allow console.log in tests + 'no-console': 'off', + // Allow any in tests for mocking + '@typescript-eslint/no-explicit-any': 'off', + // Allow unused vars in tests (for setup) + '@typescript-eslint/no-unused-vars': 'off', + }, + }, + + // Configuration for JavaScript files (if any) + { + files: ['**/*.js', '**/*.mjs'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, + rules: { + 'no-console': 'warn', + 'no-debugger': 'error', + 'prefer-const': 'error', + 'no-var': 'error', + }, + }, + + // Files and directories to ignore + { + ignores: [ + 'node_modules/**', + 'build/**', + 'dist/**', + '*.config.js', + 'coverage/**', + '.git/**', + '.vscode/**', + '*.min.js', + ], + }, +]; diff --git a/src/@types/custom.d.ts b/src/@types/custom.d.ts index 203d2c4..eb412fa 100644 --- a/src/@types/custom.d.ts +++ b/src/@types/custom.d.ts @@ -1,5 +1,5 @@ // index.d.ts -declare module "*.gql" { +declare module '*.gql' { const content: any; export default content; } diff --git a/src/ai/diagram-analyzer.ts b/src/ai/diagram-analyzer.ts index 84a3f8e..0f7f51a 100644 --- a/src/ai/diagram-analyzer.ts +++ b/src/ai/diagram-analyzer.ts @@ -6,98 +6,98 @@ const DIAGRAM_KEYWORDS: Record = { 'proceso', 'flujo', 'workflow', 'aprobación', 'tarea', 'decisión', 'gateway', 'evento', 'actividad', 'secuencia', 'paralelo', 'exclusivo', 'inicio', 'fin', 'subprocess', 'process', 'flow', 'approval', 'task', 'decision', 'activity', 'sequence', 'parallel', - 'exclusive', 'start', 'end', 'business process' + 'exclusive', 'start', 'end', 'business process', ], [DiagramType.BPMN_COLLABORATION]: [ 'colaboración', 'participante', 'pool', 'lane', 'mensaje', 'collaboration', - 'participant', 'message', 'proceso', 'flujo', 'workflow' + 'participant', 'message', 'proceso', 'flujo', 'workflow', ], [DiagramType.BPMN_CHOREOGRAPHY]: [ 'coreografía', 'intercambio', 'mensaje', 'choreography', 'exchange', 'message', - 'comunicación', 'communication', 'protocolo', 'protocol' + 'comunicación', 'communication', 'protocolo', 'protocol', ], [DiagramType.ER_DIAGRAM]: [ 'base de datos', 'tabla', 'entidad', 'relación', 'campo', 'clave', 'foreign key', 'primary key', 'atributo', 'cardinalidad', 'normalización', 'esquema', 'database', 'table', 'entity', 'relationship', 'field', 'key', 'attribute', - 'cardinality', 'normalization', 'schema', 'sql', 'modelo de datos' + 'cardinality', 'normalization', 'schema', 'sql', 'modelo de datos', ], [DiagramType.DATABASE_SCHEMA]: [ - 'esquema', 'schema', 'base de datos', 'database', 'tabla', 'table', 'sql' + 'esquema', 'schema', 'base de datos', 'database', 'tabla', 'table', 'sql', ], [DiagramType.CONCEPTUAL_MODEL]: [ - 'modelo conceptual', 'conceptual model', 'concepto', 'concept', 'dominio', 'domain' + 'modelo conceptual', 'conceptual model', 'concepto', 'concept', 'dominio', 'domain', ], [DiagramType.SYSTEM_ARCHITECTURE]: [ 'arquitectura', 'sistema', 'componente', 'servicio', 'api', 'microservicio', 'capa', 'módulo', 'interfaz', 'dependencia', 'infraestructura', 'servidor', 'architecture', 'system', 'component', 'service', 'microservice', 'layer', - 'module', 'interface', 'dependency', 'infrastructure', 'server', 'backend', 'frontend' + 'module', 'interface', 'dependency', 'infrastructure', 'server', 'backend', 'frontend', ], [DiagramType.MICROSERVICES]: [ - 'microservicio', 'microservice', 'servicio', 'service', 'api', 'contenedor', 'container' + 'microservicio', 'microservice', 'servicio', 'service', 'api', 'contenedor', 'container', ], [DiagramType.LAYERED_ARCHITECTURE]: [ - 'capas', 'layers', 'arquitectura por capas', 'layered architecture', 'nivel', 'tier' + 'capas', 'layers', 'arquitectura por capas', 'layered architecture', 'nivel', 'tier', ], [DiagramType.C4_CONTEXT]: [ - 'c4', 'contexto', 'context', 'sistema', 'system', 'usuario', 'user', 'actor' + 'c4', 'contexto', 'context', 'sistema', 'system', 'usuario', 'user', 'actor', ], [DiagramType.C4_CONTAINER]: [ - 'c4', 'contenedor', 'container', 'aplicación', 'application', 'servicio', 'service' + 'c4', 'contenedor', 'container', 'aplicación', 'application', 'servicio', 'service', ], [DiagramType.C4_COMPONENT]: [ - 'c4', 'componente', 'component', 'módulo', 'module', 'clase', 'class' + 'c4', 'componente', 'component', 'módulo', 'module', 'clase', 'class', ], [DiagramType.UML_CLASS]: [ 'clase', 'objeto', 'herencia', 'polimorfismo', 'encapsulación', 'método', 'atributo', 'class', 'object', 'inheritance', 'polymorphism', 'encapsulation', 'method', - 'uml', 'orientado a objetos', 'oop' + 'uml', 'orientado a objetos', 'oop', ], [DiagramType.UML_SEQUENCE]: [ - 'secuencia', 'sequence', 'mensaje', 'message', 'tiempo', 'time', 'interacción', 'interaction' + 'secuencia', 'sequence', 'mensaje', 'message', 'tiempo', 'time', 'interacción', 'interaction', ], [DiagramType.UML_USE_CASE]: [ - 'caso de uso', 'use case', 'actor', 'funcionalidad', 'functionality', 'requisito', 'requirement' + 'caso de uso', 'use case', 'actor', 'funcionalidad', 'functionality', 'requisito', 'requirement', ], [DiagramType.UML_ACTIVITY]: [ - 'actividad', 'activity', 'flujo', 'flow', 'acción', 'action', 'proceso', 'process' + 'actividad', 'activity', 'flujo', 'flow', 'acción', 'action', 'proceso', 'process', ], [DiagramType.UML_STATE]: [ - 'estado', 'state', 'transición', 'transition', 'máquina de estados', 'state machine' + 'estado', 'state', 'transición', 'transition', 'máquina de estados', 'state machine', ], [DiagramType.UML_COMPONENT]: [ - 'componente', 'component', 'interfaz', 'interface', 'dependencia', 'dependency' + 'componente', 'component', 'interfaz', 'interface', 'dependencia', 'dependency', ], [DiagramType.UML_DEPLOYMENT]: [ - 'despliegue', 'deployment', 'nodo', 'node', 'artefacto', 'artifact', 'hardware' + 'despliegue', 'deployment', 'nodo', 'node', 'artefacto', 'artifact', 'hardware', ], [DiagramType.NETWORK_TOPOLOGY]: [ 'red', 'router', 'switch', 'firewall', 'vlan', 'subred', 'ip', 'tcp', 'protocolo', - 'network', 'topology', 'subnet', 'protocol', 'ethernet', 'wifi', 'lan', 'wan' + 'network', 'topology', 'subnet', 'protocol', 'ethernet', 'wifi', 'lan', 'wan', ], [DiagramType.INFRASTRUCTURE]: [ - 'infraestructura', 'infrastructure', 'servidor', 'server', 'hardware', 'datacenter' + 'infraestructura', 'infrastructure', 'servidor', 'server', 'hardware', 'datacenter', ], [DiagramType.CLOUD_ARCHITECTURE]: [ - 'nube', 'cloud', 'aws', 'azure', 'gcp', 'kubernetes', 'docker', 'contenedor' + 'nube', 'cloud', 'aws', 'azure', 'gcp', 'kubernetes', 'docker', 'contenedor', ], [DiagramType.FLOWCHART]: [ 'diagrama de flujo', 'flowchart', 'algoritmo', 'lógica', 'condición', 'bucle', - 'algorithm', 'logic', 'condition', 'loop', 'if', 'while', 'for' + 'algorithm', 'logic', 'condition', 'loop', 'if', 'while', 'for', ], [DiagramType.ORGCHART]: [ - 'organigrama', 'orgchart', 'jerarquía', 'hierarchy', 'organización', 'organization' + 'organigrama', 'orgchart', 'jerarquía', 'hierarchy', 'organización', 'organization', ], [DiagramType.MINDMAP]: [ - 'mapa mental', 'mindmap', 'idea', 'concepto', 'brainstorming', 'lluvia de ideas' + 'mapa mental', 'mindmap', 'idea', 'concepto', 'brainstorming', 'lluvia de ideas', ], [DiagramType.WIREFRAME]: [ - 'wireframe', 'mockup', 'prototipo', 'prototype', 'interfaz', 'interface', 'ui', 'ux' + 'wireframe', 'mockup', 'prototipo', 'prototype', 'interfaz', 'interface', 'ui', 'ux', ], [DiagramType.GANTT]: [ - 'gantt', 'cronograma', 'timeline', 'proyecto', 'project', 'tarea', 'task', 'tiempo' - ] + 'gantt', 'cronograma', 'timeline', 'proyecto', 'project', 'tarea', 'task', 'tiempo', + ], }; // Entity extraction patterns for different diagram types @@ -105,18 +105,18 @@ const ENTITY_PATTERNS: Partial>> = { [DiagramType.BPMN_PROCESS]: { tasks: /(?:tarea|task|actividad|activity|paso|step)\s*:?\s*([^,.\n]+)/gi, events: /(?:evento|event|inicio|start|fin|end)\s*:?\s*([^,.\n]+)/gi, - decisions: /(?:decisión|decision|gateway|condición|condition)\s*:?\s*([^,.\n]+)/gi + decisions: /(?:decisión|decision|gateway|condición|condition)\s*:?\s*([^,.\n]+)/gi, }, [DiagramType.ER_DIAGRAM]: { entities: /(?:tabla|table|entidad|entity)\s*:?\s*([^,.\n]+)/gi, attributes: /(?:campo|field|atributo|attribute|columna|column)\s*:?\s*([^,.\n]+)/gi, - relationships: /(?:relación|relationship|conecta|connects?|relaciona)\s*([^,.\n]+)/gi + relationships: /(?:relación|relationship|conecta|connects?|relaciona)\s*([^,.\n]+)/gi, }, [DiagramType.SYSTEM_ARCHITECTURE]: { components: /(?:componente|component|servicio|service|módulo|module)\s*:?\s*([^,.\n]+)/gi, layers: /(?:capa|layer|nivel|tier)\s*:?\s*([^,.\n]+)/gi, - services: /(?:api|servicio|service|microservicio|microservice)\s*:?\s*([^,.\n]+)/gi - } + services: /(?:api|servicio|service|microservicio|microservice)\s*:?\s*([^,.\n]+)/gi, + }, }; /** @@ -147,7 +147,7 @@ export const analyzeDiagramDescription = async (description: string): Promise): { type: Diagra return { type: DiagramType.FLOWCHART, confidence: 0.3 }; } - const bestType = entries.find(([_, score]) => score === maxScore)?.[0] as DiagramType; + const bestType = entries.find(([_category, score]) => score === maxScore)?.[0] as DiagramType; const totalKeywords = DIAGRAM_KEYWORDS[bestType]?.length || 1; const confidence = Math.min(maxScore / totalKeywords, 1); @@ -197,7 +197,7 @@ const extractEntities = (description: string, diagramType: DiagramType): string[ } // Extract entities using specific patterns - for (const [category, pattern] of Object.entries(patterns)) { + for (const pattern of Object.values(patterns)) { const matches = [...description.matchAll(pattern)]; entities.push(...matches.map(match => match[1]?.trim()).filter(Boolean)); } @@ -221,7 +221,7 @@ const extractGenericEntities = (description: string): string[] => { const entities = [ ...capitalizedWords, - ...quotedStrings.map(s => s.replace(/"/g, '')) + ...quotedStrings.map(s => s.replace(/"/g, '')), ]; return [...new Set(entities)].slice(0, 10); // Limit to 10 entities @@ -233,7 +233,7 @@ const extractGenericEntities = (description: string): string[] => { const extractRelationships = ( description: string, entities: string[], - diagramType: DiagramType + diagramType: DiagramType, ): Array<{ from: string; to: string; type: string; label?: string }> => { const relationships: Array<{ from: string; to: string; type: string; label?: string }> = []; @@ -241,7 +241,7 @@ const extractRelationships = ( const relationshipKeywords = [ 'conecta', 'connects', 'relaciona', 'relates', 'depende', 'depends', 'hereda', 'inherits', 'implementa', 'implements', 'usa', 'uses', - 'tiene', 'has', 'contiene', 'contains', 'pertenece', 'belongs' + 'tiene', 'has', 'contiene', 'contains', 'pertenece', 'belongs', ]; // Simple relationship extraction based on proximity and keywords @@ -254,7 +254,7 @@ const extractRelationships = ( const pattern = new RegExp( `${entity1}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity2}|` + `${entity2}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity1}`, - 'i' + 'i', ); if (pattern.test(description)) { @@ -262,7 +262,7 @@ const extractRelationships = ( from: entity1, to: entity2, type: getRelationshipType(diagramType), - label: extractRelationshipLabel(description, entity1, entity2) + label: extractRelationshipLabel(description, entity1, entity2), }); } } @@ -276,16 +276,16 @@ const extractRelationships = ( */ const getRelationshipType = (diagramType: DiagramType): string => { switch (diagramType) { - case DiagramType.BPMN_PROCESS: - return 'sequence'; - case DiagramType.ER_DIAGRAM: - return 'relationship'; - case DiagramType.SYSTEM_ARCHITECTURE: - return 'dependency'; - case DiagramType.UML_CLASS: - return 'association'; - default: - return 'connection'; + case DiagramType.BPMN_PROCESS: + return 'sequence'; + case DiagramType.ER_DIAGRAM: + return 'relationship'; + case DiagramType.SYSTEM_ARCHITECTURE: + return 'dependency'; + case DiagramType.UML_CLASS: + return 'association'; + default: + return 'connection'; } }; @@ -313,7 +313,7 @@ const getMatchedKeywords = (text: string, diagramType: DiagramType): string[] => const generateReasoning = ( bestMatch: { type: DiagramType; confidence: number }, keywords: string[], - entities: string[] + entities: string[], ): string => { return `Detected ${bestMatch.type} with ${Math.round(bestMatch.confidence * 100)}% confidence. ` + `Found ${keywords.length} relevant keywords: ${keywords.slice(0, 5).join(', ')}. ` + diff --git a/src/generators/smart-architecture-generator.ts b/src/generators/smart-architecture-generator.ts index c6e53cc..9695ae2 100644 --- a/src/generators/smart-architecture-generator.ts +++ b/src/generators/smart-architecture-generator.ts @@ -1,5 +1,4 @@ -import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js'; -import { DiagramAnalysis } from '../types/diagram-types.js'; +import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js'; /** * Generate intelligent system architecture diagram based on analysis @@ -7,7 +6,7 @@ import { DiagramAnalysis } from '../types/diagram-types.js'; export const generateSmartArchitectureDiagram = ( description: string, analysis: DiagramAnalysis, - preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {} + preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}, ): DiagramData => { const { entities, relationships } = analysis; const complexity = preferences.complexity || 'detailed'; @@ -55,7 +54,7 @@ export const generateSmartArchitectureDiagram = ( `component-${sourceComponent.name}`, `component-${targetComponent.name}`, connection.type, - connection.label + connection.label, ); connectionElements.push(connectionElement); } @@ -69,8 +68,8 @@ export const generateSmartArchitectureDiagram = ( format: 'drawio' as any, created: new Date().toISOString(), modified: new Date().toISOString(), - version: '1.0' - } + version: '1.0', + }, }; }; @@ -98,7 +97,7 @@ const extractArchitectureComponents = (entities: string[], description: string): frontend: ['frontend', 'cliente', 'client', 'ui', 'interfaz', 'react', 'vue', 'angular'], backend: ['backend', 'servidor', 'server', 'node', 'express', 'spring'], middleware: ['middleware', 'proxy', 'gateway', 'balanceador', 'load balancer'], - external: ['externo', 'external', 'tercero', 'third party', 'integración', 'integration'] + external: ['externo', 'external', 'tercero', 'third party', 'integración', 'integration'], }; // Extract components from entities @@ -110,7 +109,7 @@ const extractArchitectureComponents = (entities: string[], description: string): name: cleanComponentName(entity), type: componentType, description: extractComponentDescription(entity, description), - technologies + technologies, }); }); @@ -124,7 +123,7 @@ const extractArchitectureComponents = (entities: string[], description: string): { name: 'Frontend', type: 'frontend', technologies: ['React'] }, { name: 'API Gateway', type: 'middleware', technologies: ['Express'] }, { name: 'Backend Service', type: 'backend', technologies: ['Node.js'] }, - { name: 'Database', type: 'database', technologies: ['PostgreSQL'] } + { name: 'Database', type: 'database', technologies: ['PostgreSQL'] }, ); } @@ -136,7 +135,7 @@ const extractArchitectureComponents = (entities: string[], description: string): */ const inferComponentType = ( componentName: string, - typeMap: Record + typeMap: Record, ): 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external' => { const name = componentName.toLowerCase(); @@ -157,7 +156,7 @@ const extractTechnologies = (componentName: string, description: string): string const techKeywords = [ 'react', 'vue', 'angular', 'node', 'express', 'spring', 'django', 'mysql', 'postgresql', 'mongodb', 'redis', 'docker', 'kubernetes', - 'aws', 'azure', 'gcp', 'nginx', 'apache', 'java', 'python', 'javascript' + 'aws', 'azure', 'gcp', 'nginx', 'apache', 'java', 'python', 'javascript', ]; const text = description.toLowerCase(); @@ -177,7 +176,7 @@ const extractComponentDescription = (componentName: string, description: string) // Look for descriptions near the component name const patterns = [ new RegExp(`${componentName}\\s+(?:es|is|se encarga de|handles?)\\s+([^.]{1,100})`, 'gi'), - new RegExp(`${componentName}\\s*:?\\s*([^.]{1,100})`, 'gi') + new RegExp(`${componentName}\\s*:?\\s*([^.]{1,100})`, 'gi'), ]; for (const pattern of patterns) { @@ -195,7 +194,7 @@ const extractComponentDescription = (componentName: string, description: string) */ const extractComponentsFromDescription = ( description: string, - typeMap: Record + typeMap: Record, ): Array<{ name: string; type: 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external'; @@ -212,7 +211,7 @@ const extractComponentsFromDescription = ( // Look for component patterns const componentPatterns = [ /(?:componente|component|servicio|service|módulo|module)\s*:?\s*([^,.\n]+)/gi, - /(?:incluye|includes?|tiene|has)\s+(?:un|una|a|an)?\s*([^,.\n]+)/gi + /(?:incluye|includes?|tiene|has)\s+(?:un|una|a|an)?\s*([^,.\n]+)/gi, ]; for (const pattern of componentPatterns) { @@ -226,7 +225,7 @@ const extractComponentsFromDescription = ( components.push({ name, type, - technologies + technologies, }); } } @@ -240,7 +239,7 @@ const extractComponentsFromDescription = ( */ const extractArchitectureLayers = ( description: string, - components: Array<{ name: string; type: string }> + components: Array<{ name: string; type: string }>, ): Array<{ name: string; type: 'presentation' | 'business' | 'data' | 'integration'; @@ -256,33 +255,33 @@ const extractArchitectureLayers = ( const layerMappings = { presentation: { name: 'Capa de Presentación', - types: ['frontend', 'ui', 'client'] + types: ['frontend', 'ui', 'client'], }, business: { name: 'Capa de Negocio', - types: ['service', 'backend', 'api'] + types: ['service', 'backend', 'api'], }, data: { name: 'Capa de Datos', - types: ['database', 'storage'] + types: ['database', 'storage'], }, integration: { name: 'Capa de Integración', - types: ['middleware', 'external', 'gateway'] - } + types: ['middleware', 'external', 'gateway'], + }, }; // Organize components into layers Object.entries(layerMappings).forEach(([layerType, layerConfig]) => { const layerComponents = components.filter(component => - layerConfig.types.some(type => component.type.includes(type)) + layerConfig.types.some(type => component.type.includes(type)), ); if (layerComponents.length > 0) { layers.push({ name: layerConfig.name, type: layerType as any, - components: layerComponents + components: layerComponents, }); } }); @@ -293,18 +292,18 @@ const extractArchitectureLayers = ( { name: 'Capa de Presentación', type: 'presentation', - components: components.filter(c => c.type === 'frontend') + components: components.filter(c => c.type === 'frontend'), }, { name: 'Capa de Negocio', type: 'business', - components: components.filter(c => ['service', 'backend', 'api'].includes(c.type)) + components: components.filter(c => ['service', 'backend', 'api'].includes(c.type)), }, { name: 'Capa de Datos', type: 'data', - components: components.filter(c => c.type === 'database') - } + components: components.filter(c => c.type === 'database'), + }, ); } @@ -317,7 +316,7 @@ const extractArchitectureLayers = ( const extractArchitectureConnections = ( relationships: Array<{ from: string; to: string; type: string; label?: string }>, description: string, - components: Array<{ name: string; type: string }> + components: Array<{ name: string; type: string }>, ): Array<{ from: string; to: string; type: string; label?: string }> => { const connections: Array<{ from: string; to: string; type: string; label?: string }> = []; @@ -325,7 +324,7 @@ const extractArchitectureConnections = ( relationships.forEach(rel => { connections.push({ ...rel, - type: inferConnectionType(rel.from, rel.to, components) + type: inferConnectionType(rel.from, rel.to, components), }); }); @@ -340,7 +339,7 @@ const extractArchitectureConnections = ( // Check if connection already exists const exists = connections.some(conn => (conn.from === comp1.name && conn.to === comp2.name) || - (conn.from === comp2.name && conn.to === comp1.name) + (conn.from === comp2.name && conn.to === comp1.name), ); if (!exists) { @@ -359,7 +358,7 @@ const extractArchitectureConnections = ( const inferConnectionType = ( from: string, to: string, - components: Array<{ name: string; type: string }> + components: Array<{ name: string; type: string }>, ): string => { const fromComp = components.find(c => c.name === from); const toComp = components.find(c => c.name === to); @@ -381,7 +380,7 @@ const inferConnectionType = ( const inferImplicitConnection = ( comp1: { name: string; type: string }, comp2: { name: string; type: string }, - description: string + _description: string, ): { from: string; to: string; type: string; label?: string } | null => { // Common architecture patterns const patterns = [ @@ -389,7 +388,7 @@ const inferImplicitConnection = ( { from: 'api', to: 'database', label: 'Query' }, { from: 'service', to: 'database', label: 'Data Access' }, { from: 'middleware', to: 'service', label: 'Route' }, - { from: 'frontend', to: 'service', label: 'API Call' } + { from: 'frontend', to: 'service', label: 'API Call' }, ]; for (const pattern of patterns) { @@ -399,7 +398,7 @@ const inferImplicitConnection = ( from: comp1.name, to: comp2.name, type: inferConnectionType(comp1.name, comp2.name, [comp1, comp2]), - label: pattern.label + label: pattern.label, }; } } @@ -420,7 +419,7 @@ const cleanComponentName = (name: string): string => { const findComponentInLayers = ( layers: Array<{ components: Array<{ name: string; type: string }> }>, - componentName: string + componentName: string, ): { name: string; type: string } | null => { for (const layer of layers) { const component = layer.components.find(c => c.name === componentName); @@ -438,7 +437,7 @@ const createLayer = ( layer: { name: string; type: string; components: Array<{ name: string; type: string }> }, x: number, y: number, - complexity: 'simple' | 'detailed' + _complexity: 'simple' | 'detailed', ): DiagramElement => { const width = 800; const height = 150; @@ -449,7 +448,7 @@ const createLayer = ( label: layer.name, geometry: { x, y, width, height }, style: 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fillColor=#f8f9fa;strokeColor=#dee2e6;', - properties: { layerType: layer.type, layerName: layer.name } + properties: { layerType: layer.type, layerName: layer.name }, }; }; @@ -460,7 +459,7 @@ const createComponent = ( component: { name: string; type: string; description?: string; technologies?: string[] }, x: number, y: number, - complexity: 'simple' | 'detailed' + complexity: 'simple' | 'detailed', ): DiagramElement => { const width = complexity === 'detailed' ? 180 : 120; const height = complexity === 'detailed' ? 100 : 80; @@ -483,7 +482,7 @@ const createComponent = ( api: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;', service: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;', middleware: 'rhombus;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;', - external: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;dashed=1;' + external: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;dashed=1;', }; const style = styleMap[component.type as keyof typeof styleMap] || styleMap.service; @@ -498,8 +497,8 @@ const createComponent = ( componentType: component.type, componentName: component.name, technologies: component.technologies || [], - description: component.description || '' - } + description: component.description || '', + }, }; }; @@ -510,7 +509,7 @@ const createArchitectureConnection = ( sourceId: string, targetId: string, connectionType: string, - label?: string + label?: string, ): DiagramConnection => { // Choose style based on connection type const styleMap = { @@ -518,7 +517,7 @@ const createArchitectureConnection = ( data_access: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#82b366;', service_call: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#6c8ebf;', proxy: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#d79b00;', - dependency: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#666666;' + dependency: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#666666;', }; const style = styleMap[connectionType as keyof typeof styleMap] || styleMap.dependency; @@ -529,6 +528,6 @@ const createArchitectureConnection = ( target: targetId, label: label || '', style, - properties: { connectionType } + properties: { connectionType }, }; }; diff --git a/src/generators/smart-bpmn-generator.ts b/src/generators/smart-bpmn-generator.ts index a7fe7ee..232481a 100644 --- a/src/generators/smart-bpmn-generator.ts +++ b/src/generators/smart-bpmn-generator.ts @@ -1,5 +1,4 @@ -import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js'; -import { DiagramAnalysis } from '../types/diagram-types.js'; +import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js'; /** * Generate intelligent BPMN diagram based on analysis @@ -7,9 +6,9 @@ import { DiagramAnalysis } from '../types/diagram-types.js'; export const generateSmartBPMNDiagram = ( description: string, analysis: DiagramAnalysis, - preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {} + preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}, ): DiagramData => { - const { entities, relationships } = analysis; + const { entities } = analysis; const complexity = preferences.complexity || 'detailed'; // Identify different types of BPMN elements from entities @@ -22,7 +21,7 @@ export const generateSmartBPMNDiagram = ( const connections: DiagramConnection[] = []; let currentX = 100; - let currentY = 150; + const currentY = 150; const spacing = 200; // Add start event @@ -39,17 +38,17 @@ export const generateSmartBPMNDiagram = ( let element: DiagramElement; switch (flowElement.type) { - case 'task': - element = createTask(flowElement.name, currentX, currentY, complexity); - break; - case 'gateway': - element = createGateway(flowElement.name, currentX, currentY, flowElement.gatewayType || 'exclusive'); - break; - case 'event': - element = createIntermediateEvent(flowElement.name, currentX, currentY); - break; - default: - element = createTask(flowElement.name, currentX, currentY, complexity); + case 'task': + element = createTask(flowElement.name, currentX, currentY, complexity); + break; + case 'gateway': + element = createGateway(flowElement.name, currentX, currentY, flowElement.gatewayType || 'exclusive'); + break; + case 'event': + element = createIntermediateEvent(flowElement.name, currentX, currentY); + break; + default: + element = createTask(flowElement.name, currentX, currentY, complexity); } elements.push(element); @@ -68,7 +67,7 @@ export const generateSmartBPMNDiagram = ( currentX, currentY, spacing, - complexity + complexity, ); elements.push(...branchResults.elements); @@ -93,8 +92,8 @@ export const generateSmartBPMNDiagram = ( format: 'drawio' as any, created: new Date().toISOString(), modified: new Date().toISOString(), - version: '1.0' - } + version: '1.0', + }, }; }; @@ -136,7 +135,7 @@ const extractTasks = (entities: string[], description: string): string[] => { /** * Extract events from entities and description */ -const extractEvents = (entities: string[], description: string): string[] => { +const extractEvents = (entities: string[], _description: string): string[] => { const eventKeywords = ['evento', 'event', 'inicio', 'start', 'fin', 'end', 'trigger', 'disparador']; const events: string[] = []; @@ -175,7 +174,7 @@ const extractGateways = (entities: string[], description: string): Array<{ name: /si\s+(.+?)\s+entonces/gi, /if\s+(.+?)\s+then/gi, /cuando\s+(.+?)\s+[,.]?/gi, - /en caso de\s+(.+?)\s+[,.]?/gi + /en caso de\s+(.+?)\s+[,.]?/gi, ]; for (const pattern of decisionPatterns) { @@ -196,7 +195,7 @@ const extractGateways = (entities: string[], description: string): Array<{ name: const buildProcessFlow = ( tasks: string[], gateways: Array<{ name: string; type: 'exclusive' | 'parallel' | 'inclusive' }>, - events: string[] + _events: string[], ): Array<{ type: string; name: string; gatewayType?: string; branches?: string[][] }> => { const flow: Array<{ type: string; name: string; gatewayType?: string; branches?: string[][] }> = []; @@ -221,7 +220,7 @@ const buildProcessFlow = ( type: 'gateway', name: gateway.name, gatewayType: gateway.type, - branches: branches + branches: branches, }); gatewayIndex++; @@ -265,7 +264,7 @@ const createStartEvent = (x: number, y: number): DiagramElement => ({ label: 'Inicio', geometry: { x, y, width: 36, height: 36 }, style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;', - properties: { eventType: 'start' } + properties: { eventType: 'start' }, }); const createEndEvent = (x: number, y: number): DiagramElement => ({ @@ -274,7 +273,7 @@ const createEndEvent = (x: number, y: number): DiagramElement => ({ label: 'Fin', geometry: { x, y, width: 36, height: 36 }, style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;strokeWidth=3;', - properties: { eventType: 'end' } + properties: { eventType: 'end' }, }); const createTask = (name: string, x: number, y: number, complexity: 'simple' | 'detailed'): DiagramElement => { @@ -287,7 +286,7 @@ const createTask = (name: string, x: number, y: number, complexity: 'simple' | ' label: name, geometry: { x, y, width, height }, style: 'rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: { taskType: 'user' } + properties: { taskType: 'user' }, }; }; @@ -297,7 +296,7 @@ const createGateway = (name: string, x: number, y: number, gatewayType: string): label: name, geometry: { x, y, width: 50, height: 50 }, style: 'rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;', - properties: { gatewayType } + properties: { gatewayType }, }); const createIntermediateEvent = (name: string, x: number, y: number): DiagramElement => ({ @@ -306,7 +305,7 @@ const createIntermediateEvent = (name: string, x: number, y: number): DiagramEle label: name, geometry: { x, y, width: 36, height: 36 }, style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#e1d5e7;strokeColor=#9673a6;', - properties: { eventType: 'intermediate' } + properties: { eventType: 'intermediate' }, }); const createSequenceFlow = (sourceId: string, targetId: string, label?: string): DiagramConnection => ({ @@ -315,7 +314,7 @@ const createSequenceFlow = (sourceId: string, targetId: string, label?: string): target: targetId, label: label || '', style: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;', - properties: { flowType: 'sequence' } + properties: { flowType: 'sequence' }, }); /** @@ -327,7 +326,7 @@ const createGatewayBranchElements = ( startX: number, startY: number, spacing: number, - complexity: 'simple' | 'detailed' + complexity: 'simple' | 'detailed', ): { elements: DiagramElement[]; connections: DiagramConnection[]; @@ -350,7 +349,7 @@ const createGatewayBranchElements = ( let branchX = startX; let previousId = gatewayId; - branch.forEach((taskName, taskIndex) => { + branch.forEach((taskName, _taskIndex) => { branchX += spacing; const task = createTask(taskName, branchX, branchY, complexity); elements.push(task); @@ -370,6 +369,6 @@ const createGatewayBranchElements = ( elements, connections, nextX: maxX + spacing, - convergenceGatewayId: convergenceGateway.id + convergenceGatewayId: convergenceGateway.id, }; }; diff --git a/src/generators/smart-er-generator.ts b/src/generators/smart-er-generator.ts index 38cfaf9..4962a86 100644 --- a/src/generators/smart-er-generator.ts +++ b/src/generators/smart-er-generator.ts @@ -1,5 +1,4 @@ -import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js'; -import { DiagramAnalysis } from '../types/diagram-types.js'; +import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js'; /** * Generate intelligent ER diagram based on analysis @@ -7,7 +6,7 @@ import { DiagramAnalysis } from '../types/diagram-types.js'; export const generateSmartERDiagram = ( description: string, analysis: DiagramAnalysis, - preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {} + preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}, ): DiagramData => { const { entities, relationships } = analysis; const complexity = preferences.complexity || 'detailed'; @@ -47,7 +46,7 @@ export const generateSmartERDiagram = ( `entity-${sourceEntity.name}`, `entity-${targetEntity.name}`, relationship.type, - relationship.label + relationship.label, ); connections.push(connection); } @@ -61,8 +60,8 @@ export const generateSmartERDiagram = ( format: 'drawio' as any, created: new Date().toISOString(), modified: new Date().toISOString(), - version: '1.0' - } + version: '1.0', + }, }; }; @@ -107,7 +106,7 @@ const extractDatabaseEntities = (entities: string[], description: string): Array const attributes = generateEntityAttributes(entityName, description); dbEntities.push({ name: entityName, - attributes + attributes, }); }); @@ -119,7 +118,7 @@ const extractDatabaseEntities = (entities: string[], description: string): Array */ const generateEntityAttributes = ( entityName: string, - description: string + description: string, ): Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> => { const attributes: Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> = []; @@ -127,7 +126,7 @@ const generateEntityAttributes = ( attributes.push({ name: 'id', type: 'INT', - isPrimaryKey: true + isPrimaryKey: true, }); // Generate context-specific attributes based on entity name @@ -141,7 +140,7 @@ const generateEntityAttributes = ( // Add common attributes attributes.push( { name: 'created_at', type: 'TIMESTAMP' }, - { name: 'updated_at', type: 'TIMESTAMP' } + { name: 'updated_at', type: 'TIMESTAMP' }, ); return attributes.slice(0, 8); // Limit to 8 attributes for readability @@ -156,55 +155,55 @@ const getContextualAttributes = (entityName: string): Array<{ name: string; type { name: 'nombre', type: 'VARCHAR(100)' }, { name: 'email', type: 'VARCHAR(255)' }, { name: 'password', type: 'VARCHAR(255)' }, - { name: 'telefono', type: 'VARCHAR(20)' } + { name: 'telefono', type: 'VARCHAR(20)' }, ], user: [ { name: 'name', type: 'VARCHAR(100)' }, { name: 'email', type: 'VARCHAR(255)' }, { name: 'password', type: 'VARCHAR(255)' }, - { name: 'phone', type: 'VARCHAR(20)' } + { name: 'phone', type: 'VARCHAR(20)' }, ], producto: [ { name: 'nombre', type: 'VARCHAR(200)' }, { name: 'descripcion', type: 'TEXT' }, { name: 'precio', type: 'DECIMAL(10,2)' }, - { name: 'stock', type: 'INT' } + { name: 'stock', type: 'INT' }, ], product: [ { name: 'name', type: 'VARCHAR(200)' }, { name: 'description', type: 'TEXT' }, { name: 'price', type: 'DECIMAL(10,2)' }, - { name: 'stock', type: 'INT' } + { name: 'stock', type: 'INT' }, ], pedido: [ { name: 'numero', type: 'VARCHAR(50)' }, { name: 'total', type: 'DECIMAL(10,2)' }, { name: 'estado', type: 'VARCHAR(50)' }, - { name: 'fecha', type: 'DATE' } + { name: 'fecha', type: 'DATE' }, ], order: [ { name: 'number', type: 'VARCHAR(50)' }, { name: 'total', type: 'DECIMAL(10,2)' }, { name: 'status', type: 'VARCHAR(50)' }, - { name: 'date', type: 'DATE' } + { name: 'date', type: 'DATE' }, ], cliente: [ { name: 'nombre', type: 'VARCHAR(100)' }, { name: 'email', type: 'VARCHAR(255)' }, { name: 'direccion', type: 'TEXT' }, - { name: 'telefono', type: 'VARCHAR(20)' } + { name: 'telefono', type: 'VARCHAR(20)' }, ], customer: [ { name: 'name', type: 'VARCHAR(100)' }, { name: 'email', type: 'VARCHAR(255)' }, { name: 'address', type: 'TEXT' }, - { name: 'phone', type: 'VARCHAR(20)' } - ] + { name: 'phone', type: 'VARCHAR(20)' }, + ], }; return attributeMap[entityName] || [ { name: 'name', type: 'VARCHAR(100)' }, - { name: 'description', type: 'TEXT' } + { name: 'description', type: 'TEXT' }, ]; }; @@ -213,14 +212,14 @@ const getContextualAttributes = (entityName: string): Array<{ name: string; type */ const extractAttributesFromDescription = ( description: string, - entityName: string + entityName: string, ): Array<{ name: string; type: string }> => { const attributes: Array<{ name: string; type: string }> = []; // Look for attribute patterns const attributePatterns = [ /(?:campo|field|atributo|attribute|columna|column)\s*:?\s*([^,.\n]+)/gi, - new RegExp(`${entityName}\\s+(?:tiene|has|incluye|includes?)\\s+([^,.\n]+)`, 'gi') + new RegExp(`${entityName}\\s+(?:tiene|has|incluye|includes?)\\s+([^,.\n]+)`, 'gi'), ]; for (const pattern of attributePatterns) { @@ -271,7 +270,7 @@ const inferAttributeType = (attributeName: string): string => { const extractDatabaseRelationships = ( relationships: Array<{ from: string; to: string; type: string; label?: string }>, description: string, - entities: Array<{ name: string; attributes: any[] }> + entities: Array<{ name: string; attributes: any[] }>, ): Array<{ from: string; to: string; type: string; label?: string; cardinality?: string }> => { const dbRelationships: Array<{ from: string; to: string; type: string; label?: string; cardinality?: string }> = []; @@ -280,7 +279,7 @@ const extractDatabaseRelationships = ( const cardinality = inferCardinality(rel.from, rel.to, description); dbRelationships.push({ ...rel, - cardinality + cardinality, }); }); @@ -293,7 +292,7 @@ const extractDatabaseRelationships = ( // Check if relationship already exists const existingRel = dbRelationships.find( rel => (rel.from === entity1.name && rel.to === entity2.name) || - (rel.from === entity2.name && rel.to === entity1.name) + (rel.from === entity2.name && rel.to === entity1.name), ); if (!existingRel) { @@ -331,13 +330,13 @@ const inferCardinality = (entity1: string, entity2: string, description: string) const commonOneToMany = [ ['usuario', 'pedido'], ['user', 'order'], ['cliente', 'pedido'], ['customer', 'order'], - ['categoria', 'producto'], ['category', 'product'] + ['categoria', 'producto'], ['category', 'product'], ]; const commonManyToMany = [ ['producto', 'categoria'], ['product', 'category'], ['usuario', 'rol'], ['user', 'role'], - ['estudiante', 'curso'], ['student', 'course'] + ['estudiante', 'curso'], ['student', 'course'], ]; for (const [first, second] of commonOneToMany) { @@ -363,7 +362,7 @@ const inferCardinality = (entity1: string, entity2: string, description: string) const inferImplicitRelationship = ( entity1: string, entity2: string, - description: string + _description: string, ): { from: string; to: string; type: string; label?: string; cardinality?: string } | null => { const e1 = entity1.toLowerCase(); const e2 = entity2.toLowerCase(); @@ -375,7 +374,7 @@ const inferImplicitRelationship = ( { entities: ['cliente', 'pedido'], label: 'hace', cardinality: '1:N' }, { entities: ['customer', 'order'], label: 'makes', cardinality: '1:N' }, { entities: ['producto', 'categoria'], label: 'pertenece a', cardinality: 'N:M' }, - { entities: ['product', 'category'], label: 'belongs to', cardinality: 'N:M' } + { entities: ['product', 'category'], label: 'belongs to', cardinality: 'N:M' }, ]; for (const pattern of relationshipPatterns) { @@ -387,7 +386,7 @@ const inferImplicitRelationship = ( to: entity2, type: 'relationship', label: pattern.label, - cardinality: pattern.cardinality + cardinality: pattern.cardinality, }; } } @@ -425,7 +424,7 @@ const createEntity = ( entity: { name: string; attributes: Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> }, x: number, y: number, - complexity: 'simple' | 'detailed' + complexity: 'simple' | 'detailed', ): DiagramElement => { const width = complexity === 'detailed' ? 200 : 150; const attributeHeight = complexity === 'detailed' ? 20 : 16; @@ -457,7 +456,7 @@ const createEntity = ( label, geometry: { x, y, width, height }, style: 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: { entityType: 'table', entityName: entity.name } + properties: { entityType: 'table', entityName: entity.name }, }; }; @@ -468,7 +467,7 @@ const createRelationship = ( sourceId: string, targetId: string, relationshipType: string, - label?: string + label?: string, ): DiagramConnection => { return { id: `rel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, @@ -476,6 +475,6 @@ const createRelationship = ( target: targetId, label: label || '', style: 'edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERoneToMany;startArrow=ERmandOne;', - properties: { relationshipType, cardinality: '1:N' } + properties: { relationshipType, cardinality: '1:N' }, }; }; diff --git a/src/index.ts b/src/index.ts index 62fb778..77b8c61 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,6 @@ import { ListToolsRequestSchema, McpError, ReadResourceRequestSchema, - InitializeRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; // Import tools and utilities @@ -64,103 +63,103 @@ const createToolDefinitions = () => [ properties: { name: { type: 'string', - description: 'Name of the diagram file (without extension)' + description: 'Name of the diagram file (without extension)', }, type: { type: 'string', enum: Object.values(DiagramType), - description: 'Type of diagram to create' + description: 'Type of diagram to create', }, format: { type: 'string', enum: Object.values(DiagramFormat), default: 'drawio', - description: 'File format for the diagram' + description: 'File format for the diagram', }, description: { type: 'string', - description: 'Natural language description of the diagram to generate using AI' + description: 'Natural language description of the diagram to generate using AI', }, outputPath: { type: 'string', - description: 'Output directory path (relative to workspace)' + description: 'Output directory path (relative to workspace)', }, complexity: { type: 'string', enum: ['simple', 'detailed'], default: 'detailed', - description: 'Complexity level of the generated diagram' + description: 'Complexity level of the generated diagram', }, language: { type: 'string', default: 'es', - description: 'Language for diagram labels and text' + description: 'Language for diagram labels and text', }, // Legacy parameters for backward compatibility processName: { type: 'string', - description: 'Name of the BPMN process (legacy)' + description: 'Name of the BPMN process (legacy)', }, tasks: { type: 'array', items: { type: 'string' }, - description: 'List of tasks for BPMN process (legacy)' + description: 'List of tasks for BPMN process (legacy)', }, gatewayType: { type: 'string', enum: ['exclusive', 'parallel'], - description: 'Type of gateway for BPMN process (legacy)' + description: 'Type of gateway for BPMN process (legacy)', }, branches: { type: 'array', items: { type: 'array', - items: { type: 'string' } + items: { type: 'string' }, }, - description: 'Branches for BPMN gateway (legacy)' + description: 'Branches for BPMN gateway (legacy)', }, beforeGateway: { type: 'array', items: { type: 'string' }, - description: 'Tasks before gateway (legacy)' + description: 'Tasks before gateway (legacy)', }, afterGateway: { type: 'array', items: { type: 'string' }, - description: 'Tasks after gateway (legacy)' + description: 'Tasks after gateway (legacy)', }, classes: { type: 'array', items: { type: 'string' }, - description: 'List of classes for UML class diagram (legacy)' + description: 'List of classes for UML class diagram (legacy)', }, entities: { type: 'array', items: { type: 'string' }, - description: 'List of entities for ER diagram (legacy)' + description: 'List of entities for ER diagram (legacy)', }, components: { type: 'array', items: { type: 'string' }, - description: 'List of components for network or architecture diagram (legacy)' + description: 'List of components for network or architecture diagram (legacy)', }, processes: { type: 'array', items: { type: 'string' }, - description: 'List of processes for flowchart (legacy)' - } + description: 'List of processes for flowchart (legacy)', + }, }, - required: ['name', 'type'] - } + required: ['name', 'type'], + }, }, { name: 'get_diagram_types', description: 'Get list of supported diagram types with descriptions', inputSchema: { type: 'object', - properties: {} - } - } + properties: {}, + }, + }, ]; // Pure function to create resource definitions @@ -169,8 +168,8 @@ const createResourceDefinitions = () => [ uri: 'diagrams://types/supported', name: 'Supported Diagram Types', mimeType: 'application/json', - description: 'List of supported diagram types and their descriptions' - } + description: 'List of supported diagram types and their descriptions', + }, ]; // Pure function to create tool handlers @@ -179,22 +178,22 @@ const createToolHandlers = (config: ServerConfig): ToolHandlers => ({ if (!validateCreateDiagramInput(args)) { throw new McpError( ErrorCode.InvalidParams, - 'Invalid create_diagram arguments' + 'Invalid create_diagram arguments', ); } const result = await createDiagram({ ...args, - workspaceRoot: args.workspaceRoot || config.workspaceRoot + workspaceRoot: args.workspaceRoot || config.workspaceRoot, }); return { content: [ { type: 'text', - text: JSON.stringify(result, null, 2) - } - ] + text: JSON.stringify(result, null, 2), + }, + ], }; }, @@ -202,7 +201,7 @@ const createToolHandlers = (config: ServerConfig): ToolHandlers => ({ const types = getSupportedDiagramTypes(); const typesWithDescriptions = types.map(type => ({ type, - description: getDiagramTypeDescription(type) + description: getDiagramTypeDescription(type), })); return { @@ -211,21 +210,21 @@ const createToolHandlers = (config: ServerConfig): ToolHandlers => ({ type: 'text', text: JSON.stringify({ success: true, - supportedTypes: typesWithDescriptions - }, null, 2) - } - ] + supportedTypes: typesWithDescriptions, + }, null, 2), + }, + ], }; - } + }, }); // Pure function to create resource handlers -const createResourceHandlers = (config: ServerConfig): ResourceHandlers => ({ +const createResourceHandlers = (_config: ServerConfig): ResourceHandlers => ({ 'diagrams://types/supported': async () => { const types = getSupportedDiagramTypes(); const typesWithDescriptions = types.map(type => ({ type, - description: getDiagramTypeDescription(type) + description: getDiagramTypeDescription(type), })); return { @@ -233,18 +232,18 @@ const createResourceHandlers = (config: ServerConfig): ResourceHandlers => ({ { uri: 'diagrams://types/supported', mimeType: 'application/json', - text: JSON.stringify(typesWithDescriptions, null, 2) - } - ] + text: JSON.stringify(typesWithDescriptions, null, 2), + }, + ], }; - } + }, }); // Pure function to setup tool request handlers const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) => { // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: createToolDefinitions() + tools: createToolDefinitions(), })); // Handle tool calls @@ -254,7 +253,7 @@ const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) => if (!handler) { throw new McpError( ErrorCode.MethodNotFound, - `Unknown tool: ${request.params.name}` + `Unknown tool: ${request.params.name}`, ); } @@ -280,7 +279,7 @@ const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) => const setupResourceRequestHandlers = (server: Server, resourceHandlers: ResourceHandlers) => { // List available resources server.setRequestHandler(ListResourcesRequestSchema, async () => ({ - resources: createResourceDefinitions() + resources: createResourceDefinitions(), })); // Handle resource requests @@ -292,7 +291,7 @@ const setupResourceRequestHandlers = (server: Server, resourceHandlers: Resource if (!handler) { throw new McpError( ErrorCode.InvalidRequest, - `Unknown resource URI: ${uri}` + `Unknown resource URI: ${uri}`, ); } @@ -300,7 +299,7 @@ const setupResourceRequestHandlers = (server: Server, resourceHandlers: Resource } catch (error) { throw new McpError( ErrorCode.InternalError, - `Failed to read resource: ${error}` + `Failed to read resource: ${error}`, ); } }); @@ -323,7 +322,7 @@ const createMCPServer = (config: ServerConfig): Server => { }, { capabilities: config.capabilities, - } + }, ); const toolHandlers = createToolHandlers(config); @@ -368,7 +367,7 @@ const isInitializeRequest = (body: any): boolean => { }; // Pure function to create session transport -const createSessionTransport = (config: ServerConfig): StreamableHTTPServerTransport => { +const createSessionTransport = (_config: ServerConfig): StreamableHTTPServerTransport => { return new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sessionId) => { @@ -484,8 +483,8 @@ const setupAPIRoutes = (app: express.Application, config: ServerConfig) => { services: { mcp: 'operational', diagramGeneration: 'operational', - sessions: Object.keys(transports).length - } + sessions: Object.keys(transports).length, + }, }); }); diff --git a/src/tests/server.test.ts b/src/tests/server.test.ts deleted file mode 100644 index f509334..0000000 --- a/src/tests/server.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; - -// Mock Express and MCP modules -jest.mock('express'); -jest.mock('@modelcontextprotocol/sdk/server/index.js'); -jest.mock('@modelcontextprotocol/sdk/server/streamableHttp.js'); - -describe('MCP Server Tests', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe('Server Configuration', () => { - it('should create server configuration with default values', () => { - // Test basic server configuration creation - const config = { - name: 'drawio-mcp-server', - version: '0.1.0', - workspaceRoot: process.cwd(), - httpPort: 3000, - capabilities: { - resources: {}, - tools: {} - } - }; - - expect(config.name).toBe('drawio-mcp-server'); - expect(config.version).toBe('0.1.0'); - expect(config.httpPort).toBe(3000); - expect(typeof config.workspaceRoot).toBe('string'); - }); - - it('should create server configuration with custom values', () => { - const customConfig = { - name: 'drawio-mcp-server', - version: '0.1.0', - workspaceRoot: '/custom/workspace', - httpPort: 8080, - capabilities: { - resources: {}, - tools: {} - } - }; - - expect(customConfig.workspaceRoot).toBe('/custom/workspace'); - expect(customConfig.httpPort).toBe(8080); - }); - }); - - describe('Tool Definitions', () => { - it('should create correct tool definitions', () => { - const toolDefinitions = [ - { - name: 'create_diagram', - description: 'Create a new diagram of specified type (BPMN, UML, ER, Network, Architecture, etc.) with AI-powered generation', - inputSchema: { - type: 'object', - properties: { - name: { - type: 'string', - description: 'Name of the diagram file (without extension)' - }, - type: { - type: 'string', - description: 'Type of diagram to create' - } - }, - required: ['name', 'type'] - } - }, - { - name: 'get_diagram_types', - description: 'Get list of supported diagram types with descriptions', - inputSchema: { - type: 'object', - properties: {} - } - } - ]; - - expect(toolDefinitions).toHaveLength(2); - expect(toolDefinitions[0].name).toBe('create_diagram'); - expect(toolDefinitions[1].name).toBe('get_diagram_types'); - - // Check required fields - expect(toolDefinitions[0].inputSchema.required).toContain('name'); - expect(toolDefinitions[0].inputSchema.required).toContain('type'); - }); - }); - - describe('Resource Definitions', () => { - it('should create correct resource definitions', () => { - const resourceDefinitions = [ - { - uri: 'diagrams://types/supported', - name: 'Supported Diagram Types', - mimeType: 'application/json', - description: 'List of supported diagram types and their descriptions' - } - ]; - - expect(resourceDefinitions).toHaveLength(1); - expect(resourceDefinitions[0].uri).toBe('diagrams://types/supported'); - expect(resourceDefinitions[0].mimeType).toBe('application/json'); - }); - }); - - describe('HTTP Endpoints', () => { - it('should define correct HTTP endpoints', () => { - const endpoints = [ - { method: 'POST', path: '/mcp', description: 'Client-to-server MCP communication' }, - { method: 'GET', path: '/mcp', description: 'Server-to-client notifications via SSE' }, - { method: 'DELETE', path: '/mcp', description: 'Session termination' }, - { method: 'GET', path: '/health', description: 'Health check' }, - { method: 'GET', path: '/', description: 'API documentation' } - ]; - - expect(endpoints).toHaveLength(5); - - const postEndpoint = endpoints.find(e => e.method === 'POST' && e.path === '/mcp'); - expect(postEndpoint).toBeDefined(); - - const healthEndpoint = endpoints.find(e => e.method === 'GET' && e.path === '/health'); - expect(healthEndpoint).toBeDefined(); - }); - }); - - describe('Session Management', () => { - it('should handle session creation', () => { - const sessionId = 'test-session-123'; - const sessions: Record = {}; - - // Simulate session creation - sessions[sessionId] = { - id: sessionId, - created: new Date().toISOString(), - active: true - }; - - expect(sessions[sessionId]).toBeDefined(); - expect(sessions[sessionId].id).toBe(sessionId); - expect(sessions[sessionId].active).toBe(true); - }); - - it('should handle session cleanup', () => { - const sessionId = 'test-session-123'; - const sessions: Record = { - [sessionId]: { - id: sessionId, - created: new Date().toISOString(), - active: true - } - }; - - // Simulate session cleanup - delete sessions[sessionId]; - - expect(sessions[sessionId]).toBeUndefined(); - }); - }); - - describe('Error Handling', () => { - it('should handle invalid MCP requests', () => { - const invalidRequest = { - jsonrpc: '2.0', - method: 'invalid_method', - id: 1 - }; - - const errorResponse = { - jsonrpc: '2.0', - error: { - code: -32601, - message: 'Method not found' - }, - id: 1 - }; - - expect(errorResponse.error.code).toBe(-32601); - expect(errorResponse.error.message).toBe('Method not found'); - }); - - it('should handle missing session ID', () => { - const errorResponse = { - jsonrpc: '2.0', - error: { - code: -32000, - message: 'Bad Request: No valid session ID provided' - }, - id: null - }; - - expect(errorResponse.error.code).toBe(-32000); - expect(errorResponse.error.message).toContain('session ID'); - }); - }); - - describe('Health Check', () => { - it('should return correct health status', () => { - const healthResponse = { - status: 'healthy', - timestamp: new Date().toISOString(), - version: '0.1.0', - services: { - mcp: 'operational', - diagramGeneration: 'operational', - sessions: 0 - } - }; - - expect(healthResponse.status).toBe('healthy'); - expect(healthResponse.services.mcp).toBe('operational'); - expect(healthResponse.services.diagramGeneration).toBe('operational'); - expect(typeof healthResponse.services.sessions).toBe('number'); - }); - }); -}); diff --git a/src/tests/setup.ts b/src/tests/setup.ts index d19832b..66cbd91 100644 --- a/src/tests/setup.ts +++ b/src/tests/setup.ts @@ -1,44 +1,6 @@ // Test setup file for Jest import { beforeEach, afterEach } from '@jest/globals'; -// Type declarations for global test utilities -declare global { - var createMockDiagramData: () => any; - var createMockFileConfig: () => any; - var createMockVSCodeConfig: () => any; -} - -// Global test utilities -(global as any).createMockDiagramData = () => ({ - elements: [ - { - id: 'element-1', - type: 'rectangle', - label: 'Test Element', - geometry: { x: 100, y: 100, width: 200, height: 100 }, - style: 'rounded=0;whiteSpace=wrap;html=1;', - properties: {} - } - ], - connections: [], - metadata: { - type: 'flowchart', - format: 'drawio', - created: '2025-01-01T00:00:00.000Z', - modified: '2025-01-01T00:00:00.000Z', - version: '1.0' - } -}); - -(global as any).createMockFileConfig = () => ({ - workspaceRoot: '/test/workspace' -}); - -(global as any).createMockVSCodeConfig = () => ({ - workspaceRoot: '/test/workspace', - extensionId: 'hediet.vscode-drawio' -}); - // Setup test environment beforeEach(() => { // Clear any previous test state diff --git a/src/tests/tools/create-diagram.test.ts b/src/tests/tools/create-diagram.test.ts index 7d7ab57..2dc0c67 100644 --- a/src/tests/tools/create-diagram.test.ts +++ b/src/tests/tools/create-diagram.test.ts @@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals import { createDiagram, validateCreateDiagramInput, getSupportedDiagramTypes, getDiagramTypeDescription } from '../../tools/create-diagram.js'; import { DiagramType, DiagramFormat } from '../../types/diagram-types.js'; import * as fs from 'fs'; -import * as path from 'path'; // Mock file system operations jest.mock('fs', () => ({ diff --git a/src/tools/create-diagram.ts b/src/tools/create-diagram.ts index cf6bb35..e63c553 100644 --- a/src/tools/create-diagram.ts +++ b/src/tools/create-diagram.ts @@ -1,5 +1,5 @@ import { DiagramType, DiagramFormat, DiagramData } from '../types/diagram-types.js'; -import { convertToDrawioXML, createDiagramFile, getDefaultStyle, generateId } from '../utils/drawio-converter.js'; +import { createDiagramFile } from '../utils/drawio-converter.js'; import { analyzeDiagramDescription } from '../ai/diagram-analyzer.js'; import { generateSmartBPMNDiagram } from '../generators/smart-bpmn-generator.js'; import { generateSmartERDiagram } from '../generators/smart-er-generator.js'; @@ -45,26 +45,26 @@ const createSuccessResult = ( content: string, diagramType: DiagramType, format: DiagramFormat, - name: string + name: string, ): CreateDiagramResult => ({ success: true, filePath, content, message: `Successfully created ${diagramType} diagram: ${name}`, diagramType, - format + format, }); // Pure function to create error result const createErrorResult = ( error: unknown, diagramType: DiagramType, - format: DiagramFormat + format: DiagramFormat, ): CreateDiagramResult => ({ success: false, message: `Failed to create diagram: ${error instanceof Error ? error.message : String(error)}`, diagramType, - format + format, }); // Pure function to ensure directory exists @@ -93,33 +93,33 @@ const generateAIDiagram = async (input: CreateDiagramInput): Promise { x: 100, y: 100, width: 200, - height: 100 + height: 100, }, style: 'rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: {} + properties: {}, }], connections: [], metadata: { @@ -145,8 +145,8 @@ const generateBasicDiagram = (input: CreateDiagramInput): DiagramData => { format: input.format || DiagramFormat.DRAWIO, created: new Date().toISOString(), modified: new Date().toISOString(), - version: '1.0' - } + version: '1.0', + }, }; }; @@ -167,10 +167,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { x: 100, y: 100 + (index * 120), width: 120, - height: 80 + height: 80, }, style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: { isTask: true } + properties: { isTask: true }, }); if (index > 0) { @@ -180,7 +180,7 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { target: `task-${index}`, label: '', style: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;', - properties: {} + properties: {}, }); } }); @@ -195,10 +195,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { x: 100 + (index * 200), y: 100, width: 120, - height: 80 + height: 80, }, style: 'whiteSpace=wrap;html=1;align=center;fillColor=#e1d5e7;strokeColor=#9673a6;', - properties: { isEntity: true } + properties: { isEntity: true }, }); }); } else if (input.classes && input.classes.length > 0) { @@ -212,10 +212,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { x: 100 + (index * 200), y: 100, width: 160, - height: 120 + height: 120, }, style: 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: { isClass: true } + properties: { isClass: true }, }); }); } else if (input.components && input.components.length > 0) { @@ -229,10 +229,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { x: 100 + (index % 2) * 300, y: 100 + Math.floor(index / 2) * 150, width: 200, - height: 100 + height: 100, }, style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;', - properties: { isComponent: true } + properties: { isComponent: true }, }); }); } else { @@ -248,8 +248,8 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => { format: input.format || DiagramFormat.DRAWIO, created: new Date().toISOString(), modified: new Date().toISOString(), - version: '1.0' - } + version: '1.0', + }, }; }; @@ -333,7 +333,7 @@ const getDiagramTypeDescriptions = (): Readonly> => [DiagramType.ORGCHART]: 'Organizational chart showing hierarchy', [DiagramType.MINDMAP]: 'Mind map diagram for brainstorming and organizing ideas', [DiagramType.WIREFRAME]: 'Wireframe diagram for UI/UX design', - [DiagramType.GANTT]: 'Gantt chart for project management and scheduling' + [DiagramType.GANTT]: 'Gantt chart for project management and scheduling', }); // Pure function to get description for a specific diagram type diff --git a/src/utils/drawio-converter.ts b/src/utils/drawio-converter.ts index a9c7785..c47bc1a 100644 --- a/src/utils/drawio-converter.ts +++ b/src/utils/drawio-converter.ts @@ -13,13 +13,13 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => { agent: 'Cline MCP Server', version: '24.7.17', etag: generateEtag(), - type: 'device' + type: 'device', }; // Create diagram structure const diagram = { id: generateId(), - name: `${metadata.type}-diagram` + name: `${metadata.type}-diagram`, }; // Create mxGraphModel @@ -38,7 +38,7 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => { pageWidth: '827', pageHeight: '1169', math: '0', - shadow: '0' + shadow: '0', }; // Create root cell @@ -68,7 +68,7 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => { * Convert diagram element to mxCell */ const convertElementToMxCell = (element: DiagramElement, id: number): any => { - const { geometry, style, label, properties } = element; + const { geometry, style, label } = element; return { id: id.toString(), @@ -81,8 +81,8 @@ const convertElementToMxCell = (element: DiagramElement, id: number): any => { y: geometry?.y?.toString() || '0', width: geometry?.width?.toString() || '120', height: geometry?.height?.toString() || '80', - as: 'geometry' - } + as: 'geometry', + }, }; }; @@ -90,7 +90,7 @@ const convertElementToMxCell = (element: DiagramElement, id: number): any => { * Convert diagram connection to mxCell */ const convertConnectionToMxCell = (connection: DiagramConnection, id: number): any => { - const { style, label, source, target, properties } = connection; + const { style, label, source, target } = connection; return { id: id.toString(), @@ -102,8 +102,8 @@ const convertConnectionToMxCell = (connection: DiagramConnection, id: number): a target: findElementIdByName(target), geometry: { relative: '1', - as: 'geometry' - } + as: 'geometry', + }, }; }; @@ -123,7 +123,7 @@ const buildDrawioXML = ( mxfile: any, diagram: any, mxGraphModel: any, - mxCells: any[] + mxCells: any[], ): string => { let xml = '\n'; xml += `\n`; @@ -181,21 +181,21 @@ const buildMxCellXML = (cell: any): string => { */ export const convertDiagramToFormat = ( diagramData: DiagramData, - format: DiagramFormat + format: DiagramFormat, ): string => { switch (format) { - case DiagramFormat.DRAWIO: - case DiagramFormat.XML: - return convertToDrawioXML(diagramData); + case DiagramFormat.DRAWIO: + case DiagramFormat.XML: + return convertToDrawioXML(diagramData); - case DiagramFormat.DRAWIO_SVG: - return convertToDrawioSVG(diagramData); + case DiagramFormat.DRAWIO_SVG: + return convertToDrawioSVG(diagramData); - case DiagramFormat.DRAWIO_PNG: - return convertToDrawioPNG(diagramData); + case DiagramFormat.DRAWIO_PNG: + return convertToDrawioPNG(diagramData); - default: - return convertToDrawioXML(diagramData); + default: + return convertToDrawioXML(diagramData); } }; @@ -209,8 +209,8 @@ const convertToDrawioSVG = (diagramData: DiagramData): string => { // Calculate approximate SVG dimensions based on elements const { width, height } = calculateDiagramDimensions(diagramData.elements); - let svg = `\n`; - svg += `\n`; + let svg = '\n'; + svg += '\n'; svg += `\n`; svg += ' \n'; svg += ' \n'; @@ -261,7 +261,7 @@ const calculateDiagramDimensions = (elements: DiagramElement[]): { width: number return { width: Math.max(maxX + 50, 800), // Add padding and minimum width - height: Math.max(maxY + 50, 600) // Add padding and minimum height + height: Math.max(maxY + 50, 600), // Add padding and minimum height }; }; @@ -367,7 +367,7 @@ export const getDefaultStyle = (elementType: string): string => { 'rectangle': 'rounded=0;whiteSpace=wrap;html=1;', 'ellipse': 'ellipse;whiteSpace=wrap;html=1;', 'diamond': 'rhombus;whiteSpace=wrap;html=1;', - 'triangle': 'triangle;whiteSpace=wrap;html=1;' + 'triangle': 'triangle;whiteSpace=wrap;html=1;', }; return styleMap[elementType] || styleMap['rectangle']; @@ -400,40 +400,40 @@ const extractStrokeColor = (style?: string): string | null => { export const createDiagramFile = ( diagramData: DiagramData, format: DiagramFormat, - filename?: string + filename?: string, ): { content: string; filename: string; mimeType: string } => { const content = convertDiagramToFormat(diagramData, format); const baseFilename = filename || `diagram-${Date.now()}`; switch (format) { - case DiagramFormat.DRAWIO: - case DiagramFormat.XML: - return { - content, - filename: `${baseFilename}.drawio`, - mimeType: 'application/xml' - }; + case DiagramFormat.DRAWIO: + case DiagramFormat.XML: + return { + content, + filename: `${baseFilename}.drawio`, + mimeType: 'application/xml', + }; - case DiagramFormat.DRAWIO_SVG: - return { - content, - filename: `${baseFilename}.svg`, - mimeType: 'image/svg+xml' - }; + case DiagramFormat.DRAWIO_SVG: + return { + content, + filename: `${baseFilename}.svg`, + mimeType: 'image/svg+xml', + }; - case DiagramFormat.DRAWIO_PNG: - return { - content, - filename: `${baseFilename}.png`, - mimeType: 'image/png' - }; + case DiagramFormat.DRAWIO_PNG: + return { + content, + filename: `${baseFilename}.png`, + mimeType: 'image/png', + }; - default: - return { - content, - filename: `${baseFilename}.drawio`, - mimeType: 'application/xml' - }; + default: + return { + content, + filename: `${baseFilename}.drawio`, + mimeType: 'application/xml', + }; } }; @@ -442,15 +442,15 @@ export const createDiagramFile = ( */ export const streamDiagramContent = ( diagramData: DiagramData, - format: DiagramFormat = DiagramFormat.DRAWIO + format: DiagramFormat = DiagramFormat.DRAWIO, ): ReadableStream => { - const { content, mimeType } = createDiagramFile(diagramData, format); + const { content } = createDiagramFile(diagramData, format); return new ReadableStream({ start(controller) { const encoder = new TextEncoder(); controller.enqueue(encoder.encode(content)); controller.close(); - } + }, }); };