Fixing eslint issues
Some checks failed
Main Workflow / Test and Build (20.x) (push) Successful in 3m58s
Main Workflow / Security Audit (push) Successful in 4m10s
Main Workflow / Test and Build (18.x) (push) Failing after 2m46s
Main Workflow / Build Release Artifacts (push) Has been skipped
Main Workflow / Code Quality Check (push) Failing after 1m51s
Main Workflow / Notification (push) Failing after 21s
Some checks failed
Main Workflow / Test and Build (20.x) (push) Successful in 3m58s
Main Workflow / Security Audit (push) Successful in 4m10s
Main Workflow / Test and Build (18.x) (push) Failing after 2m46s
Main Workflow / Build Release Artifacts (push) Has been skipped
Main Workflow / Code Quality Check (push) Failing after 1m51s
Main Workflow / Notification (push) Failing after 21s
This commit is contained in:
126
eslint.config.js
Normal file
126
eslint.config.js
Normal file
@ -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',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
2
src/@types/custom.d.ts
vendored
2
src/@types/custom.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
// index.d.ts
|
// index.d.ts
|
||||||
declare module "*.gql" {
|
declare module '*.gql' {
|
||||||
const content: any;
|
const content: any;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
@ -6,98 +6,98 @@ const DIAGRAM_KEYWORDS: Record<DiagramType, string[]> = {
|
|||||||
'proceso', 'flujo', 'workflow', 'aprobación', 'tarea', 'decisión', 'gateway', 'evento',
|
'proceso', 'flujo', 'workflow', 'aprobación', 'tarea', 'decisión', 'gateway', 'evento',
|
||||||
'actividad', 'secuencia', 'paralelo', 'exclusivo', 'inicio', 'fin', 'subprocess',
|
'actividad', 'secuencia', 'paralelo', 'exclusivo', 'inicio', 'fin', 'subprocess',
|
||||||
'process', 'flow', 'approval', 'task', 'decision', 'activity', 'sequence', 'parallel',
|
'process', 'flow', 'approval', 'task', 'decision', 'activity', 'sequence', 'parallel',
|
||||||
'exclusive', 'start', 'end', 'business process'
|
'exclusive', 'start', 'end', 'business process',
|
||||||
],
|
],
|
||||||
[DiagramType.BPMN_COLLABORATION]: [
|
[DiagramType.BPMN_COLLABORATION]: [
|
||||||
'colaboración', 'participante', 'pool', 'lane', 'mensaje', 'collaboration',
|
'colaboración', 'participante', 'pool', 'lane', 'mensaje', 'collaboration',
|
||||||
'participant', 'message', 'proceso', 'flujo', 'workflow'
|
'participant', 'message', 'proceso', 'flujo', 'workflow',
|
||||||
],
|
],
|
||||||
[DiagramType.BPMN_CHOREOGRAPHY]: [
|
[DiagramType.BPMN_CHOREOGRAPHY]: [
|
||||||
'coreografía', 'intercambio', 'mensaje', 'choreography', 'exchange', 'message',
|
'coreografía', 'intercambio', 'mensaje', 'choreography', 'exchange', 'message',
|
||||||
'comunicación', 'communication', 'protocolo', 'protocol'
|
'comunicación', 'communication', 'protocolo', 'protocol',
|
||||||
],
|
],
|
||||||
[DiagramType.ER_DIAGRAM]: [
|
[DiagramType.ER_DIAGRAM]: [
|
||||||
'base de datos', 'tabla', 'entidad', 'relación', 'campo', 'clave', 'foreign key',
|
'base de datos', 'tabla', 'entidad', 'relación', 'campo', 'clave', 'foreign key',
|
||||||
'primary key', 'atributo', 'cardinalidad', 'normalización', 'esquema',
|
'primary key', 'atributo', 'cardinalidad', 'normalización', 'esquema',
|
||||||
'database', 'table', 'entity', 'relationship', 'field', 'key', 'attribute',
|
'database', 'table', 'entity', 'relationship', 'field', 'key', 'attribute',
|
||||||
'cardinality', 'normalization', 'schema', 'sql', 'modelo de datos'
|
'cardinality', 'normalization', 'schema', 'sql', 'modelo de datos',
|
||||||
],
|
],
|
||||||
[DiagramType.DATABASE_SCHEMA]: [
|
[DiagramType.DATABASE_SCHEMA]: [
|
||||||
'esquema', 'schema', 'base de datos', 'database', 'tabla', 'table', 'sql'
|
'esquema', 'schema', 'base de datos', 'database', 'tabla', 'table', 'sql',
|
||||||
],
|
],
|
||||||
[DiagramType.CONCEPTUAL_MODEL]: [
|
[DiagramType.CONCEPTUAL_MODEL]: [
|
||||||
'modelo conceptual', 'conceptual model', 'concepto', 'concept', 'dominio', 'domain'
|
'modelo conceptual', 'conceptual model', 'concepto', 'concept', 'dominio', 'domain',
|
||||||
],
|
],
|
||||||
[DiagramType.SYSTEM_ARCHITECTURE]: [
|
[DiagramType.SYSTEM_ARCHITECTURE]: [
|
||||||
'arquitectura', 'sistema', 'componente', 'servicio', 'api', 'microservicio', 'capa',
|
'arquitectura', 'sistema', 'componente', 'servicio', 'api', 'microservicio', 'capa',
|
||||||
'módulo', 'interfaz', 'dependencia', 'infraestructura', 'servidor',
|
'módulo', 'interfaz', 'dependencia', 'infraestructura', 'servidor',
|
||||||
'architecture', 'system', 'component', 'service', 'microservice', 'layer',
|
'architecture', 'system', 'component', 'service', 'microservice', 'layer',
|
||||||
'module', 'interface', 'dependency', 'infrastructure', 'server', 'backend', 'frontend'
|
'module', 'interface', 'dependency', 'infrastructure', 'server', 'backend', 'frontend',
|
||||||
],
|
],
|
||||||
[DiagramType.MICROSERVICES]: [
|
[DiagramType.MICROSERVICES]: [
|
||||||
'microservicio', 'microservice', 'servicio', 'service', 'api', 'contenedor', 'container'
|
'microservicio', 'microservice', 'servicio', 'service', 'api', 'contenedor', 'container',
|
||||||
],
|
],
|
||||||
[DiagramType.LAYERED_ARCHITECTURE]: [
|
[DiagramType.LAYERED_ARCHITECTURE]: [
|
||||||
'capas', 'layers', 'arquitectura por capas', 'layered architecture', 'nivel', 'tier'
|
'capas', 'layers', 'arquitectura por capas', 'layered architecture', 'nivel', 'tier',
|
||||||
],
|
],
|
||||||
[DiagramType.C4_CONTEXT]: [
|
[DiagramType.C4_CONTEXT]: [
|
||||||
'c4', 'contexto', 'context', 'sistema', 'system', 'usuario', 'user', 'actor'
|
'c4', 'contexto', 'context', 'sistema', 'system', 'usuario', 'user', 'actor',
|
||||||
],
|
],
|
||||||
[DiagramType.C4_CONTAINER]: [
|
[DiagramType.C4_CONTAINER]: [
|
||||||
'c4', 'contenedor', 'container', 'aplicación', 'application', 'servicio', 'service'
|
'c4', 'contenedor', 'container', 'aplicación', 'application', 'servicio', 'service',
|
||||||
],
|
],
|
||||||
[DiagramType.C4_COMPONENT]: [
|
[DiagramType.C4_COMPONENT]: [
|
||||||
'c4', 'componente', 'component', 'módulo', 'module', 'clase', 'class'
|
'c4', 'componente', 'component', 'módulo', 'module', 'clase', 'class',
|
||||||
],
|
],
|
||||||
[DiagramType.UML_CLASS]: [
|
[DiagramType.UML_CLASS]: [
|
||||||
'clase', 'objeto', 'herencia', 'polimorfismo', 'encapsulación', 'método', 'atributo',
|
'clase', 'objeto', 'herencia', 'polimorfismo', 'encapsulación', 'método', 'atributo',
|
||||||
'class', 'object', 'inheritance', 'polymorphism', 'encapsulation', 'method',
|
'class', 'object', 'inheritance', 'polymorphism', 'encapsulation', 'method',
|
||||||
'uml', 'orientado a objetos', 'oop'
|
'uml', 'orientado a objetos', 'oop',
|
||||||
],
|
],
|
||||||
[DiagramType.UML_SEQUENCE]: [
|
[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]: [
|
[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]: [
|
[DiagramType.UML_ACTIVITY]: [
|
||||||
'actividad', 'activity', 'flujo', 'flow', 'acción', 'action', 'proceso', 'process'
|
'actividad', 'activity', 'flujo', 'flow', 'acción', 'action', 'proceso', 'process',
|
||||||
],
|
],
|
||||||
[DiagramType.UML_STATE]: [
|
[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]: [
|
[DiagramType.UML_COMPONENT]: [
|
||||||
'componente', 'component', 'interfaz', 'interface', 'dependencia', 'dependency'
|
'componente', 'component', 'interfaz', 'interface', 'dependencia', 'dependency',
|
||||||
],
|
],
|
||||||
[DiagramType.UML_DEPLOYMENT]: [
|
[DiagramType.UML_DEPLOYMENT]: [
|
||||||
'despliegue', 'deployment', 'nodo', 'node', 'artefacto', 'artifact', 'hardware'
|
'despliegue', 'deployment', 'nodo', 'node', 'artefacto', 'artifact', 'hardware',
|
||||||
],
|
],
|
||||||
[DiagramType.NETWORK_TOPOLOGY]: [
|
[DiagramType.NETWORK_TOPOLOGY]: [
|
||||||
'red', 'router', 'switch', 'firewall', 'vlan', 'subred', 'ip', 'tcp', 'protocolo',
|
'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]: [
|
[DiagramType.INFRASTRUCTURE]: [
|
||||||
'infraestructura', 'infrastructure', 'servidor', 'server', 'hardware', 'datacenter'
|
'infraestructura', 'infrastructure', 'servidor', 'server', 'hardware', 'datacenter',
|
||||||
],
|
],
|
||||||
[DiagramType.CLOUD_ARCHITECTURE]: [
|
[DiagramType.CLOUD_ARCHITECTURE]: [
|
||||||
'nube', 'cloud', 'aws', 'azure', 'gcp', 'kubernetes', 'docker', 'contenedor'
|
'nube', 'cloud', 'aws', 'azure', 'gcp', 'kubernetes', 'docker', 'contenedor',
|
||||||
],
|
],
|
||||||
[DiagramType.FLOWCHART]: [
|
[DiagramType.FLOWCHART]: [
|
||||||
'diagrama de flujo', 'flowchart', 'algoritmo', 'lógica', 'condición', 'bucle',
|
'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]: [
|
[DiagramType.ORGCHART]: [
|
||||||
'organigrama', 'orgchart', 'jerarquía', 'hierarchy', 'organización', 'organization'
|
'organigrama', 'orgchart', 'jerarquía', 'hierarchy', 'organización', 'organization',
|
||||||
],
|
],
|
||||||
[DiagramType.MINDMAP]: [
|
[DiagramType.MINDMAP]: [
|
||||||
'mapa mental', 'mindmap', 'idea', 'concepto', 'brainstorming', 'lluvia de ideas'
|
'mapa mental', 'mindmap', 'idea', 'concepto', 'brainstorming', 'lluvia de ideas',
|
||||||
],
|
],
|
||||||
[DiagramType.WIREFRAME]: [
|
[DiagramType.WIREFRAME]: [
|
||||||
'wireframe', 'mockup', 'prototipo', 'prototype', 'interfaz', 'interface', 'ui', 'ux'
|
'wireframe', 'mockup', 'prototipo', 'prototype', 'interfaz', 'interface', 'ui', 'ux',
|
||||||
],
|
],
|
||||||
[DiagramType.GANTT]: [
|
[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
|
// Entity extraction patterns for different diagram types
|
||||||
@ -105,18 +105,18 @@ const ENTITY_PATTERNS: Partial<Record<DiagramType, Record<string, RegExp>>> = {
|
|||||||
[DiagramType.BPMN_PROCESS]: {
|
[DiagramType.BPMN_PROCESS]: {
|
||||||
tasks: /(?:tarea|task|actividad|activity|paso|step)\s*:?\s*([^,.\n]+)/gi,
|
tasks: /(?:tarea|task|actividad|activity|paso|step)\s*:?\s*([^,.\n]+)/gi,
|
||||||
events: /(?:evento|event|inicio|start|fin|end)\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]: {
|
[DiagramType.ER_DIAGRAM]: {
|
||||||
entities: /(?:tabla|table|entidad|entity)\s*:?\s*([^,.\n]+)/gi,
|
entities: /(?:tabla|table|entidad|entity)\s*:?\s*([^,.\n]+)/gi,
|
||||||
attributes: /(?:campo|field|atributo|attribute|columna|column)\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]: {
|
[DiagramType.SYSTEM_ARCHITECTURE]: {
|
||||||
components: /(?:componente|component|servicio|service|módulo|module)\s*:?\s*([^,.\n]+)/gi,
|
components: /(?:componente|component|servicio|service|módulo|module)\s*:?\s*([^,.\n]+)/gi,
|
||||||
layers: /(?:capa|layer|nivel|tier)\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<Di
|
|||||||
entities,
|
entities,
|
||||||
relationships,
|
relationships,
|
||||||
keywords,
|
keywords,
|
||||||
reasoning: generateReasoning(bestMatch, keywords, entities)
|
reasoning: generateReasoning(bestMatch, keywords, entities),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ const getBestDiagramType = (scores: Record<DiagramType, number>): { type: Diagra
|
|||||||
return { type: DiagramType.FLOWCHART, confidence: 0.3 };
|
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 totalKeywords = DIAGRAM_KEYWORDS[bestType]?.length || 1;
|
||||||
const confidence = Math.min(maxScore / totalKeywords, 1);
|
const confidence = Math.min(maxScore / totalKeywords, 1);
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ const extractEntities = (description: string, diagramType: DiagramType): string[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract entities using specific patterns
|
// 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)];
|
const matches = [...description.matchAll(pattern)];
|
||||||
entities.push(...matches.map(match => match[1]?.trim()).filter(Boolean));
|
entities.push(...matches.map(match => match[1]?.trim()).filter(Boolean));
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ const extractGenericEntities = (description: string): string[] => {
|
|||||||
|
|
||||||
const entities = [
|
const entities = [
|
||||||
...capitalizedWords,
|
...capitalizedWords,
|
||||||
...quotedStrings.map(s => s.replace(/"/g, ''))
|
...quotedStrings.map(s => s.replace(/"/g, '')),
|
||||||
];
|
];
|
||||||
|
|
||||||
return [...new Set(entities)].slice(0, 10); // Limit to 10 entities
|
return [...new Set(entities)].slice(0, 10); // Limit to 10 entities
|
||||||
@ -233,7 +233,7 @@ const extractGenericEntities = (description: string): string[] => {
|
|||||||
const extractRelationships = (
|
const extractRelationships = (
|
||||||
description: string,
|
description: string,
|
||||||
entities: string[],
|
entities: string[],
|
||||||
diagramType: DiagramType
|
diagramType: DiagramType,
|
||||||
): Array<{ from: string; to: string; type: string; label?: string }> => {
|
): Array<{ from: string; to: string; type: string; label?: string }> => {
|
||||||
const relationships: 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 = [
|
const relationshipKeywords = [
|
||||||
'conecta', 'connects', 'relaciona', 'relates', 'depende', 'depends',
|
'conecta', 'connects', 'relaciona', 'relates', 'depende', 'depends',
|
||||||
'hereda', 'inherits', 'implementa', 'implements', 'usa', 'uses',
|
'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
|
// Simple relationship extraction based on proximity and keywords
|
||||||
@ -254,7 +254,7 @@ const extractRelationships = (
|
|||||||
const pattern = new RegExp(
|
const pattern = new RegExp(
|
||||||
`${entity1}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity2}|` +
|
`${entity1}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity2}|` +
|
||||||
`${entity2}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity1}`,
|
`${entity2}.{0,50}(?:${relationshipKeywords.join('|')}).{0,50}${entity1}`,
|
||||||
'i'
|
'i',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pattern.test(description)) {
|
if (pattern.test(description)) {
|
||||||
@ -262,7 +262,7 @@ const extractRelationships = (
|
|||||||
from: entity1,
|
from: entity1,
|
||||||
to: entity2,
|
to: entity2,
|
||||||
type: getRelationshipType(diagramType),
|
type: getRelationshipType(diagramType),
|
||||||
label: extractRelationshipLabel(description, entity1, entity2)
|
label: extractRelationshipLabel(description, entity1, entity2),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,16 +276,16 @@ const extractRelationships = (
|
|||||||
*/
|
*/
|
||||||
const getRelationshipType = (diagramType: DiagramType): string => {
|
const getRelationshipType = (diagramType: DiagramType): string => {
|
||||||
switch (diagramType) {
|
switch (diagramType) {
|
||||||
case DiagramType.BPMN_PROCESS:
|
case DiagramType.BPMN_PROCESS:
|
||||||
return 'sequence';
|
return 'sequence';
|
||||||
case DiagramType.ER_DIAGRAM:
|
case DiagramType.ER_DIAGRAM:
|
||||||
return 'relationship';
|
return 'relationship';
|
||||||
case DiagramType.SYSTEM_ARCHITECTURE:
|
case DiagramType.SYSTEM_ARCHITECTURE:
|
||||||
return 'dependency';
|
return 'dependency';
|
||||||
case DiagramType.UML_CLASS:
|
case DiagramType.UML_CLASS:
|
||||||
return 'association';
|
return 'association';
|
||||||
default:
|
default:
|
||||||
return 'connection';
|
return 'connection';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ const getMatchedKeywords = (text: string, diagramType: DiagramType): string[] =>
|
|||||||
const generateReasoning = (
|
const generateReasoning = (
|
||||||
bestMatch: { type: DiagramType; confidence: number },
|
bestMatch: { type: DiagramType; confidence: number },
|
||||||
keywords: string[],
|
keywords: string[],
|
||||||
entities: string[]
|
entities: string[],
|
||||||
): string => {
|
): string => {
|
||||||
return `Detected ${bestMatch.type} with ${Math.round(bestMatch.confidence * 100)}% confidence. ` +
|
return `Detected ${bestMatch.type} with ${Math.round(bestMatch.confidence * 100)}% confidence. ` +
|
||||||
`Found ${keywords.length} relevant keywords: ${keywords.slice(0, 5).join(', ')}. ` +
|
`Found ${keywords.length} relevant keywords: ${keywords.slice(0, 5).join(', ')}. ` +
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js';
|
import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js';
|
||||||
import { DiagramAnalysis } from '../types/diagram-types.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate intelligent system architecture diagram based on analysis
|
* Generate intelligent system architecture diagram based on analysis
|
||||||
@ -7,7 +6,7 @@ import { DiagramAnalysis } from '../types/diagram-types.js';
|
|||||||
export const generateSmartArchitectureDiagram = (
|
export const generateSmartArchitectureDiagram = (
|
||||||
description: string,
|
description: string,
|
||||||
analysis: DiagramAnalysis,
|
analysis: DiagramAnalysis,
|
||||||
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}
|
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {},
|
||||||
): DiagramData => {
|
): DiagramData => {
|
||||||
const { entities, relationships } = analysis;
|
const { entities, relationships } = analysis;
|
||||||
const complexity = preferences.complexity || 'detailed';
|
const complexity = preferences.complexity || 'detailed';
|
||||||
@ -55,7 +54,7 @@ export const generateSmartArchitectureDiagram = (
|
|||||||
`component-${sourceComponent.name}`,
|
`component-${sourceComponent.name}`,
|
||||||
`component-${targetComponent.name}`,
|
`component-${targetComponent.name}`,
|
||||||
connection.type,
|
connection.type,
|
||||||
connection.label
|
connection.label,
|
||||||
);
|
);
|
||||||
connectionElements.push(connectionElement);
|
connectionElements.push(connectionElement);
|
||||||
}
|
}
|
||||||
@ -69,8 +68,8 @@ export const generateSmartArchitectureDiagram = (
|
|||||||
format: 'drawio' as any,
|
format: 'drawio' as any,
|
||||||
created: new Date().toISOString(),
|
created: new Date().toISOString(),
|
||||||
modified: 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'],
|
frontend: ['frontend', 'cliente', 'client', 'ui', 'interfaz', 'react', 'vue', 'angular'],
|
||||||
backend: ['backend', 'servidor', 'server', 'node', 'express', 'spring'],
|
backend: ['backend', 'servidor', 'server', 'node', 'express', 'spring'],
|
||||||
middleware: ['middleware', 'proxy', 'gateway', 'balanceador', 'load balancer'],
|
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
|
// Extract components from entities
|
||||||
@ -110,7 +109,7 @@ const extractArchitectureComponents = (entities: string[], description: string):
|
|||||||
name: cleanComponentName(entity),
|
name: cleanComponentName(entity),
|
||||||
type: componentType,
|
type: componentType,
|
||||||
description: extractComponentDescription(entity, description),
|
description: extractComponentDescription(entity, description),
|
||||||
technologies
|
technologies,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ const extractArchitectureComponents = (entities: string[], description: string):
|
|||||||
{ name: 'Frontend', type: 'frontend', technologies: ['React'] },
|
{ name: 'Frontend', type: 'frontend', technologies: ['React'] },
|
||||||
{ name: 'API Gateway', type: 'middleware', technologies: ['Express'] },
|
{ name: 'API Gateway', type: 'middleware', technologies: ['Express'] },
|
||||||
{ name: 'Backend Service', type: 'backend', technologies: ['Node.js'] },
|
{ 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 = (
|
const inferComponentType = (
|
||||||
componentName: string,
|
componentName: string,
|
||||||
typeMap: Record<string, string[]>
|
typeMap: Record<string, string[]>,
|
||||||
): 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external' => {
|
): 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external' => {
|
||||||
const name = componentName.toLowerCase();
|
const name = componentName.toLowerCase();
|
||||||
|
|
||||||
@ -157,7 +156,7 @@ const extractTechnologies = (componentName: string, description: string): string
|
|||||||
const techKeywords = [
|
const techKeywords = [
|
||||||
'react', 'vue', 'angular', 'node', 'express', 'spring', 'django',
|
'react', 'vue', 'angular', 'node', 'express', 'spring', 'django',
|
||||||
'mysql', 'postgresql', 'mongodb', 'redis', 'docker', 'kubernetes',
|
'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();
|
const text = description.toLowerCase();
|
||||||
@ -177,7 +176,7 @@ const extractComponentDescription = (componentName: string, description: string)
|
|||||||
// Look for descriptions near the component name
|
// Look for descriptions near the component name
|
||||||
const patterns = [
|
const patterns = [
|
||||||
new RegExp(`${componentName}\\s+(?:es|is|se encarga de|handles?)\\s+([^.]{1,100})`, 'gi'),
|
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) {
|
for (const pattern of patterns) {
|
||||||
@ -195,7 +194,7 @@ const extractComponentDescription = (componentName: string, description: string)
|
|||||||
*/
|
*/
|
||||||
const extractComponentsFromDescription = (
|
const extractComponentsFromDescription = (
|
||||||
description: string,
|
description: string,
|
||||||
typeMap: Record<string, string[]>
|
typeMap: Record<string, string[]>,
|
||||||
): Array<{
|
): Array<{
|
||||||
name: string;
|
name: string;
|
||||||
type: 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external';
|
type: 'service' | 'database' | 'api' | 'frontend' | 'backend' | 'middleware' | 'external';
|
||||||
@ -212,7 +211,7 @@ const extractComponentsFromDescription = (
|
|||||||
// Look for component patterns
|
// Look for component patterns
|
||||||
const componentPatterns = [
|
const componentPatterns = [
|
||||||
/(?:componente|component|servicio|service|módulo|module)\s*:?\s*([^,.\n]+)/gi,
|
/(?: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) {
|
for (const pattern of componentPatterns) {
|
||||||
@ -226,7 +225,7 @@ const extractComponentsFromDescription = (
|
|||||||
components.push({
|
components.push({
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
technologies
|
technologies,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +239,7 @@ const extractComponentsFromDescription = (
|
|||||||
*/
|
*/
|
||||||
const extractArchitectureLayers = (
|
const extractArchitectureLayers = (
|
||||||
description: string,
|
description: string,
|
||||||
components: Array<{ name: string; type: string }>
|
components: Array<{ name: string; type: string }>,
|
||||||
): Array<{
|
): Array<{
|
||||||
name: string;
|
name: string;
|
||||||
type: 'presentation' | 'business' | 'data' | 'integration';
|
type: 'presentation' | 'business' | 'data' | 'integration';
|
||||||
@ -256,33 +255,33 @@ const extractArchitectureLayers = (
|
|||||||
const layerMappings = {
|
const layerMappings = {
|
||||||
presentation: {
|
presentation: {
|
||||||
name: 'Capa de Presentación',
|
name: 'Capa de Presentación',
|
||||||
types: ['frontend', 'ui', 'client']
|
types: ['frontend', 'ui', 'client'],
|
||||||
},
|
},
|
||||||
business: {
|
business: {
|
||||||
name: 'Capa de Negocio',
|
name: 'Capa de Negocio',
|
||||||
types: ['service', 'backend', 'api']
|
types: ['service', 'backend', 'api'],
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
name: 'Capa de Datos',
|
name: 'Capa de Datos',
|
||||||
types: ['database', 'storage']
|
types: ['database', 'storage'],
|
||||||
},
|
},
|
||||||
integration: {
|
integration: {
|
||||||
name: 'Capa de Integración',
|
name: 'Capa de Integración',
|
||||||
types: ['middleware', 'external', 'gateway']
|
types: ['middleware', 'external', 'gateway'],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Organize components into layers
|
// Organize components into layers
|
||||||
Object.entries(layerMappings).forEach(([layerType, layerConfig]) => {
|
Object.entries(layerMappings).forEach(([layerType, layerConfig]) => {
|
||||||
const layerComponents = components.filter(component =>
|
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) {
|
if (layerComponents.length > 0) {
|
||||||
layers.push({
|
layers.push({
|
||||||
name: layerConfig.name,
|
name: layerConfig.name,
|
||||||
type: layerType as any,
|
type: layerType as any,
|
||||||
components: layerComponents
|
components: layerComponents,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -293,18 +292,18 @@ const extractArchitectureLayers = (
|
|||||||
{
|
{
|
||||||
name: 'Capa de Presentación',
|
name: 'Capa de Presentación',
|
||||||
type: 'presentation',
|
type: 'presentation',
|
||||||
components: components.filter(c => c.type === 'frontend')
|
components: components.filter(c => c.type === 'frontend'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Capa de Negocio',
|
name: 'Capa de Negocio',
|
||||||
type: 'business',
|
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',
|
name: 'Capa de Datos',
|
||||||
type: 'data',
|
type: 'data',
|
||||||
components: components.filter(c => c.type === 'database')
|
components: components.filter(c => c.type === 'database'),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +316,7 @@ const extractArchitectureLayers = (
|
|||||||
const extractArchitectureConnections = (
|
const extractArchitectureConnections = (
|
||||||
relationships: Array<{ from: string; to: string; type: string; label?: string }>,
|
relationships: Array<{ from: string; to: string; type: string; label?: string }>,
|
||||||
description: string,
|
description: string,
|
||||||
components: Array<{ name: string; type: string }>
|
components: Array<{ name: string; type: string }>,
|
||||||
): Array<{ from: string; to: string; type: string; label?: string }> => {
|
): Array<{ from: string; to: string; type: string; label?: string }> => {
|
||||||
const connections: 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 => {
|
relationships.forEach(rel => {
|
||||||
connections.push({
|
connections.push({
|
||||||
...rel,
|
...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
|
// Check if connection already exists
|
||||||
const exists = connections.some(conn =>
|
const exists = connections.some(conn =>
|
||||||
(conn.from === comp1.name && conn.to === comp2.name) ||
|
(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) {
|
if (!exists) {
|
||||||
@ -359,7 +358,7 @@ const extractArchitectureConnections = (
|
|||||||
const inferConnectionType = (
|
const inferConnectionType = (
|
||||||
from: string,
|
from: string,
|
||||||
to: string,
|
to: string,
|
||||||
components: Array<{ name: string; type: string }>
|
components: Array<{ name: string; type: string }>,
|
||||||
): string => {
|
): string => {
|
||||||
const fromComp = components.find(c => c.name === from);
|
const fromComp = components.find(c => c.name === from);
|
||||||
const toComp = components.find(c => c.name === to);
|
const toComp = components.find(c => c.name === to);
|
||||||
@ -381,7 +380,7 @@ const inferConnectionType = (
|
|||||||
const inferImplicitConnection = (
|
const inferImplicitConnection = (
|
||||||
comp1: { name: string; type: string },
|
comp1: { name: string; type: string },
|
||||||
comp2: { name: string; type: string },
|
comp2: { name: string; type: string },
|
||||||
description: string
|
_description: string,
|
||||||
): { from: string; to: string; type: string; label?: string } | null => {
|
): { from: string; to: string; type: string; label?: string } | null => {
|
||||||
// Common architecture patterns
|
// Common architecture patterns
|
||||||
const patterns = [
|
const patterns = [
|
||||||
@ -389,7 +388,7 @@ const inferImplicitConnection = (
|
|||||||
{ from: 'api', to: 'database', label: 'Query' },
|
{ from: 'api', to: 'database', label: 'Query' },
|
||||||
{ from: 'service', to: 'database', label: 'Data Access' },
|
{ from: 'service', to: 'database', label: 'Data Access' },
|
||||||
{ from: 'middleware', to: 'service', label: 'Route' },
|
{ 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) {
|
for (const pattern of patterns) {
|
||||||
@ -399,7 +398,7 @@ const inferImplicitConnection = (
|
|||||||
from: comp1.name,
|
from: comp1.name,
|
||||||
to: comp2.name,
|
to: comp2.name,
|
||||||
type: inferConnectionType(comp1.name, comp2.name, [comp1, comp2]),
|
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 = (
|
const findComponentInLayers = (
|
||||||
layers: Array<{ components: Array<{ name: string; type: string }> }>,
|
layers: Array<{ components: Array<{ name: string; type: string }> }>,
|
||||||
componentName: string
|
componentName: string,
|
||||||
): { name: string; type: string } | null => {
|
): { name: string; type: string } | null => {
|
||||||
for (const layer of layers) {
|
for (const layer of layers) {
|
||||||
const component = layer.components.find(c => c.name === componentName);
|
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 }> },
|
layer: { name: string; type: string; components: Array<{ name: string; type: string }> },
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
complexity: 'simple' | 'detailed'
|
_complexity: 'simple' | 'detailed',
|
||||||
): DiagramElement => {
|
): DiagramElement => {
|
||||||
const width = 800;
|
const width = 800;
|
||||||
const height = 150;
|
const height = 150;
|
||||||
@ -449,7 +448,7 @@ const createLayer = (
|
|||||||
label: layer.name,
|
label: layer.name,
|
||||||
geometry: { x, y, width, height },
|
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;',
|
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[] },
|
component: { name: string; type: string; description?: string; technologies?: string[] },
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
complexity: 'simple' | 'detailed'
|
complexity: 'simple' | 'detailed',
|
||||||
): DiagramElement => {
|
): DiagramElement => {
|
||||||
const width = complexity === 'detailed' ? 180 : 120;
|
const width = complexity === 'detailed' ? 180 : 120;
|
||||||
const height = complexity === 'detailed' ? 100 : 80;
|
const height = complexity === 'detailed' ? 100 : 80;
|
||||||
@ -483,7 +482,7 @@ const createComponent = (
|
|||||||
api: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;',
|
api: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;',
|
||||||
service: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;',
|
service: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;',
|
||||||
middleware: 'rhombus;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;',
|
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;
|
const style = styleMap[component.type as keyof typeof styleMap] || styleMap.service;
|
||||||
@ -498,8 +497,8 @@ const createComponent = (
|
|||||||
componentType: component.type,
|
componentType: component.type,
|
||||||
componentName: component.name,
|
componentName: component.name,
|
||||||
technologies: component.technologies || [],
|
technologies: component.technologies || [],
|
||||||
description: component.description || ''
|
description: component.description || '',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -510,7 +509,7 @@ const createArchitectureConnection = (
|
|||||||
sourceId: string,
|
sourceId: string,
|
||||||
targetId: string,
|
targetId: string,
|
||||||
connectionType: string,
|
connectionType: string,
|
||||||
label?: string
|
label?: string,
|
||||||
): DiagramConnection => {
|
): DiagramConnection => {
|
||||||
// Choose style based on connection type
|
// Choose style based on connection type
|
||||||
const styleMap = {
|
const styleMap = {
|
||||||
@ -518,7 +517,7 @@ const createArchitectureConnection = (
|
|||||||
data_access: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;strokeColor=#82b366;',
|
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;',
|
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;',
|
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;
|
const style = styleMap[connectionType as keyof typeof styleMap] || styleMap.dependency;
|
||||||
@ -529,6 +528,6 @@ const createArchitectureConnection = (
|
|||||||
target: targetId,
|
target: targetId,
|
||||||
label: label || '',
|
label: label || '',
|
||||||
style,
|
style,
|
||||||
properties: { connectionType }
|
properties: { connectionType },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js';
|
import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js';
|
||||||
import { DiagramAnalysis } from '../types/diagram-types.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate intelligent BPMN diagram based on analysis
|
* Generate intelligent BPMN diagram based on analysis
|
||||||
@ -7,9 +6,9 @@ import { DiagramAnalysis } from '../types/diagram-types.js';
|
|||||||
export const generateSmartBPMNDiagram = (
|
export const generateSmartBPMNDiagram = (
|
||||||
description: string,
|
description: string,
|
||||||
analysis: DiagramAnalysis,
|
analysis: DiagramAnalysis,
|
||||||
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}
|
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {},
|
||||||
): DiagramData => {
|
): DiagramData => {
|
||||||
const { entities, relationships } = analysis;
|
const { entities } = analysis;
|
||||||
const complexity = preferences.complexity || 'detailed';
|
const complexity = preferences.complexity || 'detailed';
|
||||||
|
|
||||||
// Identify different types of BPMN elements from entities
|
// Identify different types of BPMN elements from entities
|
||||||
@ -22,7 +21,7 @@ export const generateSmartBPMNDiagram = (
|
|||||||
const connections: DiagramConnection[] = [];
|
const connections: DiagramConnection[] = [];
|
||||||
|
|
||||||
let currentX = 100;
|
let currentX = 100;
|
||||||
let currentY = 150;
|
const currentY = 150;
|
||||||
const spacing = 200;
|
const spacing = 200;
|
||||||
|
|
||||||
// Add start event
|
// Add start event
|
||||||
@ -39,17 +38,17 @@ export const generateSmartBPMNDiagram = (
|
|||||||
let element: DiagramElement;
|
let element: DiagramElement;
|
||||||
|
|
||||||
switch (flowElement.type) {
|
switch (flowElement.type) {
|
||||||
case 'task':
|
case 'task':
|
||||||
element = createTask(flowElement.name, currentX, currentY, complexity);
|
element = createTask(flowElement.name, currentX, currentY, complexity);
|
||||||
break;
|
break;
|
||||||
case 'gateway':
|
case 'gateway':
|
||||||
element = createGateway(flowElement.name, currentX, currentY, flowElement.gatewayType || 'exclusive');
|
element = createGateway(flowElement.name, currentX, currentY, flowElement.gatewayType || 'exclusive');
|
||||||
break;
|
break;
|
||||||
case 'event':
|
case 'event':
|
||||||
element = createIntermediateEvent(flowElement.name, currentX, currentY);
|
element = createIntermediateEvent(flowElement.name, currentX, currentY);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
element = createTask(flowElement.name, currentX, currentY, complexity);
|
element = createTask(flowElement.name, currentX, currentY, complexity);
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.push(element);
|
elements.push(element);
|
||||||
@ -68,7 +67,7 @@ export const generateSmartBPMNDiagram = (
|
|||||||
currentX,
|
currentX,
|
||||||
currentY,
|
currentY,
|
||||||
spacing,
|
spacing,
|
||||||
complexity
|
complexity,
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.push(...branchResults.elements);
|
elements.push(...branchResults.elements);
|
||||||
@ -93,8 +92,8 @@ export const generateSmartBPMNDiagram = (
|
|||||||
format: 'drawio' as any,
|
format: 'drawio' as any,
|
||||||
created: new Date().toISOString(),
|
created: new Date().toISOString(),
|
||||||
modified: 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
|
* 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 eventKeywords = ['evento', 'event', 'inicio', 'start', 'fin', 'end', 'trigger', 'disparador'];
|
||||||
const events: string[] = [];
|
const events: string[] = [];
|
||||||
|
|
||||||
@ -175,7 +174,7 @@ const extractGateways = (entities: string[], description: string): Array<{ name:
|
|||||||
/si\s+(.+?)\s+entonces/gi,
|
/si\s+(.+?)\s+entonces/gi,
|
||||||
/if\s+(.+?)\s+then/gi,
|
/if\s+(.+?)\s+then/gi,
|
||||||
/cuando\s+(.+?)\s+[,.]?/gi,
|
/cuando\s+(.+?)\s+[,.]?/gi,
|
||||||
/en caso de\s+(.+?)\s+[,.]?/gi
|
/en caso de\s+(.+?)\s+[,.]?/gi,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const pattern of decisionPatterns) {
|
for (const pattern of decisionPatterns) {
|
||||||
@ -196,7 +195,7 @@ const extractGateways = (entities: string[], description: string): Array<{ name:
|
|||||||
const buildProcessFlow = (
|
const buildProcessFlow = (
|
||||||
tasks: string[],
|
tasks: string[],
|
||||||
gateways: Array<{ name: string; type: 'exclusive' | 'parallel' | 'inclusive' }>,
|
gateways: Array<{ name: string; type: 'exclusive' | 'parallel' | 'inclusive' }>,
|
||||||
events: string[]
|
_events: string[],
|
||||||
): Array<{ type: string; name: string; gatewayType?: string; branches?: string[][] }> => {
|
): Array<{ type: string; name: string; gatewayType?: string; branches?: string[][] }> => {
|
||||||
const flow: 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',
|
type: 'gateway',
|
||||||
name: gateway.name,
|
name: gateway.name,
|
||||||
gatewayType: gateway.type,
|
gatewayType: gateway.type,
|
||||||
branches: branches
|
branches: branches,
|
||||||
});
|
});
|
||||||
|
|
||||||
gatewayIndex++;
|
gatewayIndex++;
|
||||||
@ -265,7 +264,7 @@ const createStartEvent = (x: number, y: number): DiagramElement => ({
|
|||||||
label: 'Inicio',
|
label: 'Inicio',
|
||||||
geometry: { x, y, width: 36, height: 36 },
|
geometry: { x, y, width: 36, height: 36 },
|
||||||
style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;',
|
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 => ({
|
const createEndEvent = (x: number, y: number): DiagramElement => ({
|
||||||
@ -274,7 +273,7 @@ const createEndEvent = (x: number, y: number): DiagramElement => ({
|
|||||||
label: 'Fin',
|
label: 'Fin',
|
||||||
geometry: { x, y, width: 36, height: 36 },
|
geometry: { x, y, width: 36, height: 36 },
|
||||||
style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;strokeWidth=3;',
|
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 => {
|
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,
|
label: name,
|
||||||
geometry: { x, y, width, height },
|
geometry: { x, y, width, height },
|
||||||
style: 'rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
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,
|
label: name,
|
||||||
geometry: { x, y, width: 50, height: 50 },
|
geometry: { x, y, width: 50, height: 50 },
|
||||||
style: 'rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;',
|
style: 'rhombus;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;',
|
||||||
properties: { gatewayType }
|
properties: { gatewayType },
|
||||||
});
|
});
|
||||||
|
|
||||||
const createIntermediateEvent = (name: string, x: number, y: number): DiagramElement => ({
|
const createIntermediateEvent = (name: string, x: number, y: number): DiagramElement => ({
|
||||||
@ -306,7 +305,7 @@ const createIntermediateEvent = (name: string, x: number, y: number): DiagramEle
|
|||||||
label: name,
|
label: name,
|
||||||
geometry: { x, y, width: 36, height: 36 },
|
geometry: { x, y, width: 36, height: 36 },
|
||||||
style: 'ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#e1d5e7;strokeColor=#9673a6;',
|
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 => ({
|
const createSequenceFlow = (sourceId: string, targetId: string, label?: string): DiagramConnection => ({
|
||||||
@ -315,7 +314,7 @@ const createSequenceFlow = (sourceId: string, targetId: string, label?: string):
|
|||||||
target: targetId,
|
target: targetId,
|
||||||
label: label || '',
|
label: label || '',
|
||||||
style: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;',
|
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,
|
startX: number,
|
||||||
startY: number,
|
startY: number,
|
||||||
spacing: number,
|
spacing: number,
|
||||||
complexity: 'simple' | 'detailed'
|
complexity: 'simple' | 'detailed',
|
||||||
): {
|
): {
|
||||||
elements: DiagramElement[];
|
elements: DiagramElement[];
|
||||||
connections: DiagramConnection[];
|
connections: DiagramConnection[];
|
||||||
@ -350,7 +349,7 @@ const createGatewayBranchElements = (
|
|||||||
let branchX = startX;
|
let branchX = startX;
|
||||||
let previousId = gatewayId;
|
let previousId = gatewayId;
|
||||||
|
|
||||||
branch.forEach((taskName, taskIndex) => {
|
branch.forEach((taskName, _taskIndex) => {
|
||||||
branchX += spacing;
|
branchX += spacing;
|
||||||
const task = createTask(taskName, branchX, branchY, complexity);
|
const task = createTask(taskName, branchX, branchY, complexity);
|
||||||
elements.push(task);
|
elements.push(task);
|
||||||
@ -370,6 +369,6 @@ const createGatewayBranchElements = (
|
|||||||
elements,
|
elements,
|
||||||
connections,
|
connections,
|
||||||
nextX: maxX + spacing,
|
nextX: maxX + spacing,
|
||||||
convergenceGatewayId: convergenceGateway.id
|
convergenceGatewayId: convergenceGateway.id,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { DiagramType, DiagramData, DiagramElement, DiagramConnection } from '../types/diagram-types.js';
|
import { DiagramType, DiagramData, DiagramElement, DiagramConnection, DiagramAnalysis } from '../types/diagram-types.js';
|
||||||
import { DiagramAnalysis } from '../types/diagram-types.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate intelligent ER diagram based on analysis
|
* Generate intelligent ER diagram based on analysis
|
||||||
@ -7,7 +6,7 @@ import { DiagramAnalysis } from '../types/diagram-types.js';
|
|||||||
export const generateSmartERDiagram = (
|
export const generateSmartERDiagram = (
|
||||||
description: string,
|
description: string,
|
||||||
analysis: DiagramAnalysis,
|
analysis: DiagramAnalysis,
|
||||||
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {}
|
preferences: { complexity?: 'simple' | 'detailed'; language?: string } = {},
|
||||||
): DiagramData => {
|
): DiagramData => {
|
||||||
const { entities, relationships } = analysis;
|
const { entities, relationships } = analysis;
|
||||||
const complexity = preferences.complexity || 'detailed';
|
const complexity = preferences.complexity || 'detailed';
|
||||||
@ -47,7 +46,7 @@ export const generateSmartERDiagram = (
|
|||||||
`entity-${sourceEntity.name}`,
|
`entity-${sourceEntity.name}`,
|
||||||
`entity-${targetEntity.name}`,
|
`entity-${targetEntity.name}`,
|
||||||
relationship.type,
|
relationship.type,
|
||||||
relationship.label
|
relationship.label,
|
||||||
);
|
);
|
||||||
connections.push(connection);
|
connections.push(connection);
|
||||||
}
|
}
|
||||||
@ -61,8 +60,8 @@ export const generateSmartERDiagram = (
|
|||||||
format: 'drawio' as any,
|
format: 'drawio' as any,
|
||||||
created: new Date().toISOString(),
|
created: new Date().toISOString(),
|
||||||
modified: 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);
|
const attributes = generateEntityAttributes(entityName, description);
|
||||||
dbEntities.push({
|
dbEntities.push({
|
||||||
name: entityName,
|
name: entityName,
|
||||||
attributes
|
attributes,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -119,7 +118,7 @@ const extractDatabaseEntities = (entities: string[], description: string): Array
|
|||||||
*/
|
*/
|
||||||
const generateEntityAttributes = (
|
const generateEntityAttributes = (
|
||||||
entityName: string,
|
entityName: string,
|
||||||
description: string
|
description: string,
|
||||||
): Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> => {
|
): Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> => {
|
||||||
const attributes: 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({
|
attributes.push({
|
||||||
name: 'id',
|
name: 'id',
|
||||||
type: 'INT',
|
type: 'INT',
|
||||||
isPrimaryKey: true
|
isPrimaryKey: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Generate context-specific attributes based on entity name
|
// Generate context-specific attributes based on entity name
|
||||||
@ -141,7 +140,7 @@ const generateEntityAttributes = (
|
|||||||
// Add common attributes
|
// Add common attributes
|
||||||
attributes.push(
|
attributes.push(
|
||||||
{ name: 'created_at', type: 'TIMESTAMP' },
|
{ 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
|
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: 'nombre', type: 'VARCHAR(100)' },
|
||||||
{ name: 'email', type: 'VARCHAR(255)' },
|
{ name: 'email', type: 'VARCHAR(255)' },
|
||||||
{ name: 'password', type: 'VARCHAR(255)' },
|
{ name: 'password', type: 'VARCHAR(255)' },
|
||||||
{ name: 'telefono', type: 'VARCHAR(20)' }
|
{ name: 'telefono', type: 'VARCHAR(20)' },
|
||||||
],
|
],
|
||||||
user: [
|
user: [
|
||||||
{ name: 'name', type: 'VARCHAR(100)' },
|
{ name: 'name', type: 'VARCHAR(100)' },
|
||||||
{ name: 'email', type: 'VARCHAR(255)' },
|
{ name: 'email', type: 'VARCHAR(255)' },
|
||||||
{ name: 'password', type: 'VARCHAR(255)' },
|
{ name: 'password', type: 'VARCHAR(255)' },
|
||||||
{ name: 'phone', type: 'VARCHAR(20)' }
|
{ name: 'phone', type: 'VARCHAR(20)' },
|
||||||
],
|
],
|
||||||
producto: [
|
producto: [
|
||||||
{ name: 'nombre', type: 'VARCHAR(200)' },
|
{ name: 'nombre', type: 'VARCHAR(200)' },
|
||||||
{ name: 'descripcion', type: 'TEXT' },
|
{ name: 'descripcion', type: 'TEXT' },
|
||||||
{ name: 'precio', type: 'DECIMAL(10,2)' },
|
{ name: 'precio', type: 'DECIMAL(10,2)' },
|
||||||
{ name: 'stock', type: 'INT' }
|
{ name: 'stock', type: 'INT' },
|
||||||
],
|
],
|
||||||
product: [
|
product: [
|
||||||
{ name: 'name', type: 'VARCHAR(200)' },
|
{ name: 'name', type: 'VARCHAR(200)' },
|
||||||
{ name: 'description', type: 'TEXT' },
|
{ name: 'description', type: 'TEXT' },
|
||||||
{ name: 'price', type: 'DECIMAL(10,2)' },
|
{ name: 'price', type: 'DECIMAL(10,2)' },
|
||||||
{ name: 'stock', type: 'INT' }
|
{ name: 'stock', type: 'INT' },
|
||||||
],
|
],
|
||||||
pedido: [
|
pedido: [
|
||||||
{ name: 'numero', type: 'VARCHAR(50)' },
|
{ name: 'numero', type: 'VARCHAR(50)' },
|
||||||
{ name: 'total', type: 'DECIMAL(10,2)' },
|
{ name: 'total', type: 'DECIMAL(10,2)' },
|
||||||
{ name: 'estado', type: 'VARCHAR(50)' },
|
{ name: 'estado', type: 'VARCHAR(50)' },
|
||||||
{ name: 'fecha', type: 'DATE' }
|
{ name: 'fecha', type: 'DATE' },
|
||||||
],
|
],
|
||||||
order: [
|
order: [
|
||||||
{ name: 'number', type: 'VARCHAR(50)' },
|
{ name: 'number', type: 'VARCHAR(50)' },
|
||||||
{ name: 'total', type: 'DECIMAL(10,2)' },
|
{ name: 'total', type: 'DECIMAL(10,2)' },
|
||||||
{ name: 'status', type: 'VARCHAR(50)' },
|
{ name: 'status', type: 'VARCHAR(50)' },
|
||||||
{ name: 'date', type: 'DATE' }
|
{ name: 'date', type: 'DATE' },
|
||||||
],
|
],
|
||||||
cliente: [
|
cliente: [
|
||||||
{ name: 'nombre', type: 'VARCHAR(100)' },
|
{ name: 'nombre', type: 'VARCHAR(100)' },
|
||||||
{ name: 'email', type: 'VARCHAR(255)' },
|
{ name: 'email', type: 'VARCHAR(255)' },
|
||||||
{ name: 'direccion', type: 'TEXT' },
|
{ name: 'direccion', type: 'TEXT' },
|
||||||
{ name: 'telefono', type: 'VARCHAR(20)' }
|
{ name: 'telefono', type: 'VARCHAR(20)' },
|
||||||
],
|
],
|
||||||
customer: [
|
customer: [
|
||||||
{ name: 'name', type: 'VARCHAR(100)' },
|
{ name: 'name', type: 'VARCHAR(100)' },
|
||||||
{ name: 'email', type: 'VARCHAR(255)' },
|
{ name: 'email', type: 'VARCHAR(255)' },
|
||||||
{ name: 'address', type: 'TEXT' },
|
{ name: 'address', type: 'TEXT' },
|
||||||
{ name: 'phone', type: 'VARCHAR(20)' }
|
{ name: 'phone', type: 'VARCHAR(20)' },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
return attributeMap[entityName] || [
|
return attributeMap[entityName] || [
|
||||||
{ name: 'name', type: 'VARCHAR(100)' },
|
{ 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 = (
|
const extractAttributesFromDescription = (
|
||||||
description: string,
|
description: string,
|
||||||
entityName: string
|
entityName: string,
|
||||||
): Array<{ name: string; type: string }> => {
|
): Array<{ name: string; type: string }> => {
|
||||||
const attributes: Array<{ name: string; type: string }> = [];
|
const attributes: Array<{ name: string; type: string }> = [];
|
||||||
|
|
||||||
// Look for attribute patterns
|
// Look for attribute patterns
|
||||||
const attributePatterns = [
|
const attributePatterns = [
|
||||||
/(?:campo|field|atributo|attribute|columna|column)\s*:?\s*([^,.\n]+)/gi,
|
/(?: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) {
|
for (const pattern of attributePatterns) {
|
||||||
@ -271,7 +270,7 @@ const inferAttributeType = (attributeName: string): string => {
|
|||||||
const extractDatabaseRelationships = (
|
const extractDatabaseRelationships = (
|
||||||
relationships: Array<{ from: string; to: string; type: string; label?: string }>,
|
relationships: Array<{ from: string; to: string; type: string; label?: string }>,
|
||||||
description: 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 }> => {
|
): Array<{ from: string; to: string; type: string; label?: string; cardinality?: string }> => {
|
||||||
const dbRelationships: 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);
|
const cardinality = inferCardinality(rel.from, rel.to, description);
|
||||||
dbRelationships.push({
|
dbRelationships.push({
|
||||||
...rel,
|
...rel,
|
||||||
cardinality
|
cardinality,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -293,7 +292,7 @@ const extractDatabaseRelationships = (
|
|||||||
// Check if relationship already exists
|
// Check if relationship already exists
|
||||||
const existingRel = dbRelationships.find(
|
const existingRel = dbRelationships.find(
|
||||||
rel => (rel.from === entity1.name && rel.to === entity2.name) ||
|
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) {
|
if (!existingRel) {
|
||||||
@ -331,13 +330,13 @@ const inferCardinality = (entity1: string, entity2: string, description: string)
|
|||||||
const commonOneToMany = [
|
const commonOneToMany = [
|
||||||
['usuario', 'pedido'], ['user', 'order'],
|
['usuario', 'pedido'], ['user', 'order'],
|
||||||
['cliente', 'pedido'], ['customer', 'order'],
|
['cliente', 'pedido'], ['customer', 'order'],
|
||||||
['categoria', 'producto'], ['category', 'product']
|
['categoria', 'producto'], ['category', 'product'],
|
||||||
];
|
];
|
||||||
|
|
||||||
const commonManyToMany = [
|
const commonManyToMany = [
|
||||||
['producto', 'categoria'], ['product', 'category'],
|
['producto', 'categoria'], ['product', 'category'],
|
||||||
['usuario', 'rol'], ['user', 'role'],
|
['usuario', 'rol'], ['user', 'role'],
|
||||||
['estudiante', 'curso'], ['student', 'course']
|
['estudiante', 'curso'], ['student', 'course'],
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const [first, second] of commonOneToMany) {
|
for (const [first, second] of commonOneToMany) {
|
||||||
@ -363,7 +362,7 @@ const inferCardinality = (entity1: string, entity2: string, description: string)
|
|||||||
const inferImplicitRelationship = (
|
const inferImplicitRelationship = (
|
||||||
entity1: string,
|
entity1: string,
|
||||||
entity2: string,
|
entity2: string,
|
||||||
description: string
|
_description: string,
|
||||||
): { from: string; to: string; type: string; label?: string; cardinality?: string } | null => {
|
): { from: string; to: string; type: string; label?: string; cardinality?: string } | null => {
|
||||||
const e1 = entity1.toLowerCase();
|
const e1 = entity1.toLowerCase();
|
||||||
const e2 = entity2.toLowerCase();
|
const e2 = entity2.toLowerCase();
|
||||||
@ -375,7 +374,7 @@ const inferImplicitRelationship = (
|
|||||||
{ entities: ['cliente', 'pedido'], label: 'hace', cardinality: '1:N' },
|
{ entities: ['cliente', 'pedido'], label: 'hace', cardinality: '1:N' },
|
||||||
{ entities: ['customer', 'order'], label: 'makes', cardinality: '1:N' },
|
{ entities: ['customer', 'order'], label: 'makes', cardinality: '1:N' },
|
||||||
{ entities: ['producto', 'categoria'], label: 'pertenece a', cardinality: 'N:M' },
|
{ 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) {
|
for (const pattern of relationshipPatterns) {
|
||||||
@ -387,7 +386,7 @@ const inferImplicitRelationship = (
|
|||||||
to: entity2,
|
to: entity2,
|
||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
label: pattern.label,
|
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 }> },
|
entity: { name: string; attributes: Array<{ name: string; type: string; isPrimaryKey?: boolean; isForeignKey?: boolean }> },
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
complexity: 'simple' | 'detailed'
|
complexity: 'simple' | 'detailed',
|
||||||
): DiagramElement => {
|
): DiagramElement => {
|
||||||
const width = complexity === 'detailed' ? 200 : 150;
|
const width = complexity === 'detailed' ? 200 : 150;
|
||||||
const attributeHeight = complexity === 'detailed' ? 20 : 16;
|
const attributeHeight = complexity === 'detailed' ? 20 : 16;
|
||||||
@ -457,7 +456,7 @@ const createEntity = (
|
|||||||
label,
|
label,
|
||||||
geometry: { x, y, width, height },
|
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;',
|
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,
|
sourceId: string,
|
||||||
targetId: string,
|
targetId: string,
|
||||||
relationshipType: string,
|
relationshipType: string,
|
||||||
label?: string
|
label?: string,
|
||||||
): DiagramConnection => {
|
): DiagramConnection => {
|
||||||
return {
|
return {
|
||||||
id: `rel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
id: `rel-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||||
@ -476,6 +475,6 @@ const createRelationship = (
|
|||||||
target: targetId,
|
target: targetId,
|
||||||
label: label || '',
|
label: label || '',
|
||||||
style: 'edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERoneToMany;startArrow=ERmandOne;',
|
style: 'edgeStyle=entityRelationEdgeStyle;fontSize=12;html=1;endArrow=ERoneToMany;startArrow=ERmandOne;',
|
||||||
properties: { relationshipType, cardinality: '1:N' }
|
properties: { relationshipType, cardinality: '1:N' },
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
105
src/index.ts
105
src/index.ts
@ -11,7 +11,6 @@ import {
|
|||||||
ListToolsRequestSchema,
|
ListToolsRequestSchema,
|
||||||
McpError,
|
McpError,
|
||||||
ReadResourceRequestSchema,
|
ReadResourceRequestSchema,
|
||||||
InitializeRequestSchema,
|
|
||||||
} from '@modelcontextprotocol/sdk/types.js';
|
} from '@modelcontextprotocol/sdk/types.js';
|
||||||
|
|
||||||
// Import tools and utilities
|
// Import tools and utilities
|
||||||
@ -64,103 +63,103 @@ const createToolDefinitions = () => [
|
|||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Name of the diagram file (without extension)'
|
description: 'Name of the diagram file (without extension)',
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: Object.values(DiagramType),
|
enum: Object.values(DiagramType),
|
||||||
description: 'Type of diagram to create'
|
description: 'Type of diagram to create',
|
||||||
},
|
},
|
||||||
format: {
|
format: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: Object.values(DiagramFormat),
|
enum: Object.values(DiagramFormat),
|
||||||
default: 'drawio',
|
default: 'drawio',
|
||||||
description: 'File format for the diagram'
|
description: 'File format for the diagram',
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: 'string',
|
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: {
|
outputPath: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Output directory path (relative to workspace)'
|
description: 'Output directory path (relative to workspace)',
|
||||||
},
|
},
|
||||||
complexity: {
|
complexity: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: ['simple', 'detailed'],
|
enum: ['simple', 'detailed'],
|
||||||
default: 'detailed',
|
default: 'detailed',
|
||||||
description: 'Complexity level of the generated diagram'
|
description: 'Complexity level of the generated diagram',
|
||||||
},
|
},
|
||||||
language: {
|
language: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'es',
|
default: 'es',
|
||||||
description: 'Language for diagram labels and text'
|
description: 'Language for diagram labels and text',
|
||||||
},
|
},
|
||||||
// Legacy parameters for backward compatibility
|
// Legacy parameters for backward compatibility
|
||||||
processName: {
|
processName: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Name of the BPMN process (legacy)'
|
description: 'Name of the BPMN process (legacy)',
|
||||||
},
|
},
|
||||||
tasks: {
|
tasks: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'List of tasks for BPMN process (legacy)'
|
description: 'List of tasks for BPMN process (legacy)',
|
||||||
},
|
},
|
||||||
gatewayType: {
|
gatewayType: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: ['exclusive', 'parallel'],
|
enum: ['exclusive', 'parallel'],
|
||||||
description: 'Type of gateway for BPMN process (legacy)'
|
description: 'Type of gateway for BPMN process (legacy)',
|
||||||
},
|
},
|
||||||
branches: {
|
branches: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' }
|
items: { type: 'string' },
|
||||||
},
|
},
|
||||||
description: 'Branches for BPMN gateway (legacy)'
|
description: 'Branches for BPMN gateway (legacy)',
|
||||||
},
|
},
|
||||||
beforeGateway: {
|
beforeGateway: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'Tasks before gateway (legacy)'
|
description: 'Tasks before gateway (legacy)',
|
||||||
},
|
},
|
||||||
afterGateway: {
|
afterGateway: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'Tasks after gateway (legacy)'
|
description: 'Tasks after gateway (legacy)',
|
||||||
},
|
},
|
||||||
classes: {
|
classes: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'List of classes for UML class diagram (legacy)'
|
description: 'List of classes for UML class diagram (legacy)',
|
||||||
},
|
},
|
||||||
entities: {
|
entities: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'List of entities for ER diagram (legacy)'
|
description: 'List of entities for ER diagram (legacy)',
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
items: { type: 'string' },
|
||||||
description: 'List of components for network or architecture diagram (legacy)'
|
description: 'List of components for network or architecture diagram (legacy)',
|
||||||
},
|
},
|
||||||
processes: {
|
processes: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: { type: 'string' },
|
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',
|
name: 'get_diagram_types',
|
||||||
description: 'Get list of supported diagram types with descriptions',
|
description: 'Get list of supported diagram types with descriptions',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {}
|
properties: {},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Pure function to create resource definitions
|
// Pure function to create resource definitions
|
||||||
@ -169,8 +168,8 @@ const createResourceDefinitions = () => [
|
|||||||
uri: 'diagrams://types/supported',
|
uri: 'diagrams://types/supported',
|
||||||
name: 'Supported Diagram Types',
|
name: 'Supported Diagram Types',
|
||||||
mimeType: 'application/json',
|
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
|
// Pure function to create tool handlers
|
||||||
@ -179,22 +178,22 @@ const createToolHandlers = (config: ServerConfig): ToolHandlers => ({
|
|||||||
if (!validateCreateDiagramInput(args)) {
|
if (!validateCreateDiagramInput(args)) {
|
||||||
throw new McpError(
|
throw new McpError(
|
||||||
ErrorCode.InvalidParams,
|
ErrorCode.InvalidParams,
|
||||||
'Invalid create_diagram arguments'
|
'Invalid create_diagram arguments',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await createDiagram({
|
const result = await createDiagram({
|
||||||
...args,
|
...args,
|
||||||
workspaceRoot: args.workspaceRoot || config.workspaceRoot
|
workspaceRoot: args.workspaceRoot || config.workspaceRoot,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
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 types = getSupportedDiagramTypes();
|
||||||
const typesWithDescriptions = types.map(type => ({
|
const typesWithDescriptions = types.map(type => ({
|
||||||
type,
|
type,
|
||||||
description: getDiagramTypeDescription(type)
|
description: getDiagramTypeDescription(type),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -211,21 +210,21 @@ const createToolHandlers = (config: ServerConfig): ToolHandlers => ({
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
text: JSON.stringify({
|
text: JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
supportedTypes: typesWithDescriptions
|
supportedTypes: typesWithDescriptions,
|
||||||
}, null, 2)
|
}, null, 2),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pure function to create resource handlers
|
// Pure function to create resource handlers
|
||||||
const createResourceHandlers = (config: ServerConfig): ResourceHandlers => ({
|
const createResourceHandlers = (_config: ServerConfig): ResourceHandlers => ({
|
||||||
'diagrams://types/supported': async () => {
|
'diagrams://types/supported': async () => {
|
||||||
const types = getSupportedDiagramTypes();
|
const types = getSupportedDiagramTypes();
|
||||||
const typesWithDescriptions = types.map(type => ({
|
const typesWithDescriptions = types.map(type => ({
|
||||||
type,
|
type,
|
||||||
description: getDiagramTypeDescription(type)
|
description: getDiagramTypeDescription(type),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -233,18 +232,18 @@ const createResourceHandlers = (config: ServerConfig): ResourceHandlers => ({
|
|||||||
{
|
{
|
||||||
uri: 'diagrams://types/supported',
|
uri: 'diagrams://types/supported',
|
||||||
mimeType: 'application/json',
|
mimeType: 'application/json',
|
||||||
text: JSON.stringify(typesWithDescriptions, null, 2)
|
text: JSON.stringify(typesWithDescriptions, null, 2),
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pure function to setup tool request handlers
|
// Pure function to setup tool request handlers
|
||||||
const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) => {
|
const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) => {
|
||||||
// List available tools
|
// List available tools
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||||
tools: createToolDefinitions()
|
tools: createToolDefinitions(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Handle tool calls
|
// Handle tool calls
|
||||||
@ -254,7 +253,7 @@ const setupToolRequestHandlers = (server: Server, toolHandlers: ToolHandlers) =>
|
|||||||
if (!handler) {
|
if (!handler) {
|
||||||
throw new McpError(
|
throw new McpError(
|
||||||
ErrorCode.MethodNotFound,
|
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) => {
|
const setupResourceRequestHandlers = (server: Server, resourceHandlers: ResourceHandlers) => {
|
||||||
// List available resources
|
// List available resources
|
||||||
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
||||||
resources: createResourceDefinitions()
|
resources: createResourceDefinitions(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Handle resource requests
|
// Handle resource requests
|
||||||
@ -292,7 +291,7 @@ const setupResourceRequestHandlers = (server: Server, resourceHandlers: Resource
|
|||||||
if (!handler) {
|
if (!handler) {
|
||||||
throw new McpError(
|
throw new McpError(
|
||||||
ErrorCode.InvalidRequest,
|
ErrorCode.InvalidRequest,
|
||||||
`Unknown resource URI: ${uri}`
|
`Unknown resource URI: ${uri}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +299,7 @@ const setupResourceRequestHandlers = (server: Server, resourceHandlers: Resource
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new McpError(
|
throw new McpError(
|
||||||
ErrorCode.InternalError,
|
ErrorCode.InternalError,
|
||||||
`Failed to read resource: ${error}`
|
`Failed to read resource: ${error}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -323,7 +322,7 @@ const createMCPServer = (config: ServerConfig): Server => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
capabilities: config.capabilities,
|
capabilities: config.capabilities,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const toolHandlers = createToolHandlers(config);
|
const toolHandlers = createToolHandlers(config);
|
||||||
@ -368,7 +367,7 @@ const isInitializeRequest = (body: any): boolean => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Pure function to create session transport
|
// Pure function to create session transport
|
||||||
const createSessionTransport = (config: ServerConfig): StreamableHTTPServerTransport => {
|
const createSessionTransport = (_config: ServerConfig): StreamableHTTPServerTransport => {
|
||||||
return new StreamableHTTPServerTransport({
|
return new StreamableHTTPServerTransport({
|
||||||
sessionIdGenerator: () => randomUUID(),
|
sessionIdGenerator: () => randomUUID(),
|
||||||
onsessioninitialized: (sessionId) => {
|
onsessioninitialized: (sessionId) => {
|
||||||
@ -484,8 +483,8 @@ const setupAPIRoutes = (app: express.Application, config: ServerConfig) => {
|
|||||||
services: {
|
services: {
|
||||||
mcp: 'operational',
|
mcp: 'operational',
|
||||||
diagramGeneration: 'operational',
|
diagramGeneration: 'operational',
|
||||||
sessions: Object.keys(transports).length
|
sessions: Object.keys(transports).length,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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<string, any> = {};
|
|
||||||
|
|
||||||
// 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<string, any> = {
|
|
||||||
[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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,44 +1,6 @@
|
|||||||
// Test setup file for Jest
|
// Test setup file for Jest
|
||||||
import { beforeEach, afterEach } from '@jest/globals';
|
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
|
// Setup test environment
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Clear any previous test state
|
// Clear any previous test state
|
||||||
|
@ -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 { createDiagram, validateCreateDiagramInput, getSupportedDiagramTypes, getDiagramTypeDescription } from '../../tools/create-diagram.js';
|
||||||
import { DiagramType, DiagramFormat } from '../../types/diagram-types.js';
|
import { DiagramType, DiagramFormat } from '../../types/diagram-types.js';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
// Mock file system operations
|
// Mock file system operations
|
||||||
jest.mock('fs', () => ({
|
jest.mock('fs', () => ({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DiagramType, DiagramFormat, DiagramData } from '../types/diagram-types.js';
|
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 { analyzeDiagramDescription } from '../ai/diagram-analyzer.js';
|
||||||
import { generateSmartBPMNDiagram } from '../generators/smart-bpmn-generator.js';
|
import { generateSmartBPMNDiagram } from '../generators/smart-bpmn-generator.js';
|
||||||
import { generateSmartERDiagram } from '../generators/smart-er-generator.js';
|
import { generateSmartERDiagram } from '../generators/smart-er-generator.js';
|
||||||
@ -45,26 +45,26 @@ const createSuccessResult = (
|
|||||||
content: string,
|
content: string,
|
||||||
diagramType: DiagramType,
|
diagramType: DiagramType,
|
||||||
format: DiagramFormat,
|
format: DiagramFormat,
|
||||||
name: string
|
name: string,
|
||||||
): CreateDiagramResult => ({
|
): CreateDiagramResult => ({
|
||||||
success: true,
|
success: true,
|
||||||
filePath,
|
filePath,
|
||||||
content,
|
content,
|
||||||
message: `Successfully created ${diagramType} diagram: ${name}`,
|
message: `Successfully created ${diagramType} diagram: ${name}`,
|
||||||
diagramType,
|
diagramType,
|
||||||
format
|
format,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pure function to create error result
|
// Pure function to create error result
|
||||||
const createErrorResult = (
|
const createErrorResult = (
|
||||||
error: unknown,
|
error: unknown,
|
||||||
diagramType: DiagramType,
|
diagramType: DiagramType,
|
||||||
format: DiagramFormat
|
format: DiagramFormat,
|
||||||
): CreateDiagramResult => ({
|
): CreateDiagramResult => ({
|
||||||
success: false,
|
success: false,
|
||||||
message: `Failed to create diagram: ${error instanceof Error ? error.message : String(error)}`,
|
message: `Failed to create diagram: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
diagramType,
|
diagramType,
|
||||||
format
|
format,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pure function to ensure directory exists
|
// Pure function to ensure directory exists
|
||||||
@ -93,33 +93,33 @@ const generateAIDiagram = async (input: CreateDiagramInput): Promise<DiagramData
|
|||||||
// Generate diagram based on type
|
// Generate diagram based on type
|
||||||
const preferences = {
|
const preferences = {
|
||||||
complexity: input.complexity || 'detailed',
|
complexity: input.complexity || 'detailed',
|
||||||
language: input.language || 'es'
|
language: input.language || 'es',
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case DiagramType.BPMN_PROCESS:
|
case DiagramType.BPMN_PROCESS:
|
||||||
case DiagramType.BPMN_COLLABORATION:
|
case DiagramType.BPMN_COLLABORATION:
|
||||||
case DiagramType.BPMN_CHOREOGRAPHY:
|
case DiagramType.BPMN_CHOREOGRAPHY:
|
||||||
return generateSmartBPMNDiagram(input.description, analysis, preferences);
|
return generateSmartBPMNDiagram(input.description, analysis, preferences);
|
||||||
|
|
||||||
case DiagramType.ER_DIAGRAM:
|
case DiagramType.ER_DIAGRAM:
|
||||||
case DiagramType.DATABASE_SCHEMA:
|
case DiagramType.DATABASE_SCHEMA:
|
||||||
case DiagramType.CONCEPTUAL_MODEL:
|
case DiagramType.CONCEPTUAL_MODEL:
|
||||||
return generateSmartERDiagram(input.description, analysis, preferences);
|
return generateSmartERDiagram(input.description, analysis, preferences);
|
||||||
|
|
||||||
case DiagramType.SYSTEM_ARCHITECTURE:
|
case DiagramType.SYSTEM_ARCHITECTURE:
|
||||||
case DiagramType.MICROSERVICES:
|
case DiagramType.MICROSERVICES:
|
||||||
case DiagramType.LAYERED_ARCHITECTURE:
|
case DiagramType.LAYERED_ARCHITECTURE:
|
||||||
case DiagramType.C4_CONTEXT:
|
case DiagramType.C4_CONTEXT:
|
||||||
case DiagramType.C4_CONTAINER:
|
case DiagramType.C4_CONTAINER:
|
||||||
case DiagramType.C4_COMPONENT:
|
case DiagramType.C4_COMPONENT:
|
||||||
case DiagramType.CLOUD_ARCHITECTURE:
|
case DiagramType.CLOUD_ARCHITECTURE:
|
||||||
case DiagramType.INFRASTRUCTURE:
|
case DiagramType.INFRASTRUCTURE:
|
||||||
return generateSmartArchitectureDiagram(input.description, analysis, preferences);
|
return generateSmartArchitectureDiagram(input.description, analysis, preferences);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Fallback to basic diagram generation
|
// Fallback to basic diagram generation
|
||||||
return generateBasicDiagram(input);
|
return generateBasicDiagram(input);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,10 +134,10 @@ const generateBasicDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
x: 100,
|
x: 100,
|
||||||
y: 100,
|
y: 100,
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 100
|
height: 100,
|
||||||
},
|
},
|
||||||
style: 'rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
style: 'rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
||||||
properties: {}
|
properties: {},
|
||||||
}],
|
}],
|
||||||
connections: [],
|
connections: [],
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -145,8 +145,8 @@ const generateBasicDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
format: input.format || DiagramFormat.DRAWIO,
|
format: input.format || DiagramFormat.DRAWIO,
|
||||||
created: new Date().toISOString(),
|
created: new Date().toISOString(),
|
||||||
modified: new Date().toISOString(),
|
modified: new Date().toISOString(),
|
||||||
version: '1.0'
|
version: '1.0',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,10 +167,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
x: 100,
|
x: 100,
|
||||||
y: 100 + (index * 120),
|
y: 100 + (index * 120),
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 80
|
height: 80,
|
||||||
},
|
},
|
||||||
style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
||||||
properties: { isTask: true }
|
properties: { isTask: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
@ -180,7 +180,7 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
target: `task-${index}`,
|
target: `task-${index}`,
|
||||||
label: '',
|
label: '',
|
||||||
style: 'edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;',
|
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),
|
x: 100 + (index * 200),
|
||||||
y: 100,
|
y: 100,
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 80
|
height: 80,
|
||||||
},
|
},
|
||||||
style: 'whiteSpace=wrap;html=1;align=center;fillColor=#e1d5e7;strokeColor=#9673a6;',
|
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) {
|
} else if (input.classes && input.classes.length > 0) {
|
||||||
@ -212,10 +212,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
x: 100 + (index * 200),
|
x: 100 + (index * 200),
|
||||||
y: 100,
|
y: 100,
|
||||||
width: 160,
|
width: 160,
|
||||||
height: 120
|
height: 120,
|
||||||
},
|
},
|
||||||
style: 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
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) {
|
} else if (input.components && input.components.length > 0) {
|
||||||
@ -229,10 +229,10 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
x: 100 + (index % 2) * 300,
|
x: 100 + (index % 2) * 300,
|
||||||
y: 100 + Math.floor(index / 2) * 150,
|
y: 100 + Math.floor(index / 2) * 150,
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 100
|
height: 100,
|
||||||
},
|
},
|
||||||
style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
style: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
|
||||||
properties: { isComponent: true }
|
properties: { isComponent: true },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -248,8 +248,8 @@ const generateLegacyDiagram = (input: CreateDiagramInput): DiagramData => {
|
|||||||
format: input.format || DiagramFormat.DRAWIO,
|
format: input.format || DiagramFormat.DRAWIO,
|
||||||
created: new Date().toISOString(),
|
created: new Date().toISOString(),
|
||||||
modified: new Date().toISOString(),
|
modified: new Date().toISOString(),
|
||||||
version: '1.0'
|
version: '1.0',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -333,7 +333,7 @@ const getDiagramTypeDescriptions = (): Readonly<Record<DiagramType, string>> =>
|
|||||||
[DiagramType.ORGCHART]: 'Organizational chart showing hierarchy',
|
[DiagramType.ORGCHART]: 'Organizational chart showing hierarchy',
|
||||||
[DiagramType.MINDMAP]: 'Mind map diagram for brainstorming and organizing ideas',
|
[DiagramType.MINDMAP]: 'Mind map diagram for brainstorming and organizing ideas',
|
||||||
[DiagramType.WIREFRAME]: 'Wireframe diagram for UI/UX design',
|
[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
|
// Pure function to get description for a specific diagram type
|
||||||
|
@ -13,13 +13,13 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => {
|
|||||||
agent: 'Cline MCP Server',
|
agent: 'Cline MCP Server',
|
||||||
version: '24.7.17',
|
version: '24.7.17',
|
||||||
etag: generateEtag(),
|
etag: generateEtag(),
|
||||||
type: 'device'
|
type: 'device',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create diagram structure
|
// Create diagram structure
|
||||||
const diagram = {
|
const diagram = {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
name: `${metadata.type}-diagram`
|
name: `${metadata.type}-diagram`,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create mxGraphModel
|
// Create mxGraphModel
|
||||||
@ -38,7 +38,7 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => {
|
|||||||
pageWidth: '827',
|
pageWidth: '827',
|
||||||
pageHeight: '1169',
|
pageHeight: '1169',
|
||||||
math: '0',
|
math: '0',
|
||||||
shadow: '0'
|
shadow: '0',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create root cell
|
// Create root cell
|
||||||
@ -68,7 +68,7 @@ export const convertToDrawioXML = (diagramData: DiagramData): string => {
|
|||||||
* Convert diagram element to mxCell
|
* Convert diagram element to mxCell
|
||||||
*/
|
*/
|
||||||
const convertElementToMxCell = (element: DiagramElement, id: number): any => {
|
const convertElementToMxCell = (element: DiagramElement, id: number): any => {
|
||||||
const { geometry, style, label, properties } = element;
|
const { geometry, style, label } = element;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id.toString(),
|
id: id.toString(),
|
||||||
@ -81,8 +81,8 @@ const convertElementToMxCell = (element: DiagramElement, id: number): any => {
|
|||||||
y: geometry?.y?.toString() || '0',
|
y: geometry?.y?.toString() || '0',
|
||||||
width: geometry?.width?.toString() || '120',
|
width: geometry?.width?.toString() || '120',
|
||||||
height: geometry?.height?.toString() || '80',
|
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
|
* Convert diagram connection to mxCell
|
||||||
*/
|
*/
|
||||||
const convertConnectionToMxCell = (connection: DiagramConnection, id: number): any => {
|
const convertConnectionToMxCell = (connection: DiagramConnection, id: number): any => {
|
||||||
const { style, label, source, target, properties } = connection;
|
const { style, label, source, target } = connection;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: id.toString(),
|
id: id.toString(),
|
||||||
@ -102,8 +102,8 @@ const convertConnectionToMxCell = (connection: DiagramConnection, id: number): a
|
|||||||
target: findElementIdByName(target),
|
target: findElementIdByName(target),
|
||||||
geometry: {
|
geometry: {
|
||||||
relative: '1',
|
relative: '1',
|
||||||
as: 'geometry'
|
as: 'geometry',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ const buildDrawioXML = (
|
|||||||
mxfile: any,
|
mxfile: any,
|
||||||
diagram: any,
|
diagram: any,
|
||||||
mxGraphModel: any,
|
mxGraphModel: any,
|
||||||
mxCells: any[]
|
mxCells: any[],
|
||||||
): string => {
|
): string => {
|
||||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||||
xml += `<mxfile host="${mxfile.host}" modified="${mxfile.modified}" agent="${mxfile.agent}" version="${mxfile.version}" etag="${mxfile.etag}" type="${mxfile.type}">\n`;
|
xml += `<mxfile host="${mxfile.host}" modified="${mxfile.modified}" agent="${mxfile.agent}" version="${mxfile.version}" etag="${mxfile.etag}" type="${mxfile.type}">\n`;
|
||||||
@ -181,21 +181,21 @@ const buildMxCellXML = (cell: any): string => {
|
|||||||
*/
|
*/
|
||||||
export const convertDiagramToFormat = (
|
export const convertDiagramToFormat = (
|
||||||
diagramData: DiagramData,
|
diagramData: DiagramData,
|
||||||
format: DiagramFormat
|
format: DiagramFormat,
|
||||||
): string => {
|
): string => {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case DiagramFormat.DRAWIO:
|
case DiagramFormat.DRAWIO:
|
||||||
case DiagramFormat.XML:
|
case DiagramFormat.XML:
|
||||||
return convertToDrawioXML(diagramData);
|
return convertToDrawioXML(diagramData);
|
||||||
|
|
||||||
case DiagramFormat.DRAWIO_SVG:
|
case DiagramFormat.DRAWIO_SVG:
|
||||||
return convertToDrawioSVG(diagramData);
|
return convertToDrawioSVG(diagramData);
|
||||||
|
|
||||||
case DiagramFormat.DRAWIO_PNG:
|
case DiagramFormat.DRAWIO_PNG:
|
||||||
return convertToDrawioPNG(diagramData);
|
return convertToDrawioPNG(diagramData);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return convertToDrawioXML(diagramData);
|
return convertToDrawioXML(diagramData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -209,8 +209,8 @@ const convertToDrawioSVG = (diagramData: DiagramData): string => {
|
|||||||
// Calculate approximate SVG dimensions based on elements
|
// Calculate approximate SVG dimensions based on elements
|
||||||
const { width, height } = calculateDiagramDimensions(diagramData.elements);
|
const { width, height } = calculateDiagramDimensions(diagramData.elements);
|
||||||
|
|
||||||
let svg = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
let svg = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||||
svg += `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n`;
|
svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n';
|
||||||
svg += `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="${width}px" height="${height}px" viewBox="-0.5 -0.5 ${width} ${height}" content="${encodedXML}">\n`;
|
svg += `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="${width}px" height="${height}px" viewBox="-0.5 -0.5 ${width} ${height}" content="${encodedXML}">\n`;
|
||||||
svg += ' <defs/>\n';
|
svg += ' <defs/>\n';
|
||||||
svg += ' <g>\n';
|
svg += ' <g>\n';
|
||||||
@ -261,7 +261,7 @@ const calculateDiagramDimensions = (elements: DiagramElement[]): { width: number
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
width: Math.max(maxX + 50, 800), // Add padding and minimum width
|
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;',
|
'rectangle': 'rounded=0;whiteSpace=wrap;html=1;',
|
||||||
'ellipse': 'ellipse;whiteSpace=wrap;html=1;',
|
'ellipse': 'ellipse;whiteSpace=wrap;html=1;',
|
||||||
'diamond': 'rhombus;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'];
|
return styleMap[elementType] || styleMap['rectangle'];
|
||||||
@ -400,40 +400,40 @@ const extractStrokeColor = (style?: string): string | null => {
|
|||||||
export const createDiagramFile = (
|
export const createDiagramFile = (
|
||||||
diagramData: DiagramData,
|
diagramData: DiagramData,
|
||||||
format: DiagramFormat,
|
format: DiagramFormat,
|
||||||
filename?: string
|
filename?: string,
|
||||||
): { content: string; filename: string; mimeType: string } => {
|
): { content: string; filename: string; mimeType: string } => {
|
||||||
const content = convertDiagramToFormat(diagramData, format);
|
const content = convertDiagramToFormat(diagramData, format);
|
||||||
const baseFilename = filename || `diagram-${Date.now()}`;
|
const baseFilename = filename || `diagram-${Date.now()}`;
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case DiagramFormat.DRAWIO:
|
case DiagramFormat.DRAWIO:
|
||||||
case DiagramFormat.XML:
|
case DiagramFormat.XML:
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
filename: `${baseFilename}.drawio`,
|
filename: `${baseFilename}.drawio`,
|
||||||
mimeType: 'application/xml'
|
mimeType: 'application/xml',
|
||||||
};
|
};
|
||||||
|
|
||||||
case DiagramFormat.DRAWIO_SVG:
|
case DiagramFormat.DRAWIO_SVG:
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
filename: `${baseFilename}.svg`,
|
filename: `${baseFilename}.svg`,
|
||||||
mimeType: 'image/svg+xml'
|
mimeType: 'image/svg+xml',
|
||||||
};
|
};
|
||||||
|
|
||||||
case DiagramFormat.DRAWIO_PNG:
|
case DiagramFormat.DRAWIO_PNG:
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
filename: `${baseFilename}.png`,
|
filename: `${baseFilename}.png`,
|
||||||
mimeType: 'image/png'
|
mimeType: 'image/png',
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
content,
|
content,
|
||||||
filename: `${baseFilename}.drawio`,
|
filename: `${baseFilename}.drawio`,
|
||||||
mimeType: 'application/xml'
|
mimeType: 'application/xml',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -442,15 +442,15 @@ export const createDiagramFile = (
|
|||||||
*/
|
*/
|
||||||
export const streamDiagramContent = (
|
export const streamDiagramContent = (
|
||||||
diagramData: DiagramData,
|
diagramData: DiagramData,
|
||||||
format: DiagramFormat = DiagramFormat.DRAWIO
|
format: DiagramFormat = DiagramFormat.DRAWIO,
|
||||||
): ReadableStream<Uint8Array> => {
|
): ReadableStream<Uint8Array> => {
|
||||||
const { content, mimeType } = createDiagramFile(diagramData, format);
|
const { content } = createDiagramFile(diagramData, format);
|
||||||
|
|
||||||
return new ReadableStream({
|
return new ReadableStream({
|
||||||
start(controller) {
|
start(controller) {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
controller.enqueue(encoder.encode(content));
|
controller.enqueue(encoder.encode(content));
|
||||||
controller.close();
|
controller.close();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user