UNPKG

weaver-frontend-cli

Version:

🕷️ Weaver CLI - Generador completo de arquitectura Clean Architecture para entidades CRUD y flujos de negocio desde OpenAPI/Swagger

1,058 lines 47.6 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.showMainMenu = showMainMenu; const inquirer_1 = __importDefault(require("inquirer")); const chalk_1 = __importDefault(require("chalk")); const correct_entity_flow_generator_1 = require("./generators/correct-entity-flow-generator"); const business_flow_generator_1 = require("./generators/business-flow-generator"); const cleanup_generator_1 = require("./generators/cleanup-generator"); const swagger_parser_1 = require("./parsers/swagger-parser"); const project_validator_1 = require("./validators/project-validator"); const auth_manager_1 = require("./auth/auth-manager"); const directory_detector_1 = require("./utils/directory-detector"); const path = __importStar(require("path")); const fs = __importStar(require("fs-extra")); const menuChoices = [ { name: '🏗️ Crear flujo entity', value: 'create-entity-flow' }, { name: '💼 Crear flujo de negocio', value: 'create-business-flow' }, { name: '🧹 Limpiar/Eliminar código generado', value: 'cleanup' }, { name: '📊 Ver información de sesión', value: 'session-info' }, { name: '🚪 Cerrar sesión y salir', value: 'logout' }, { name: '🚪 Salir', value: 'exit' } ]; async function showMainMenu(isLocalMode = false) { console.log(chalk_1.default.blue.bold('\n🕷️ WEAVER CLI')); console.log(chalk_1.default.gray('Teje la estructura perfecta de tu código frontend\n')); const { action } = await inquirer_1.default.prompt([ { type: 'list', name: 'action', message: '¿Qué deseas generar?', choices: menuChoices, pageSize: 10 } ]); switch (action) { case 'create-entity-flow': await handleCreateEntityFlow(isLocalMode); break; case 'create-business-flow': await handleCreateBusinessFlow(isLocalMode); break; case 'cleanup': await handleCleanup(isLocalMode); break; case 'session-info': await auth_manager_1.AuthManager.showSessionInfo(); await showMainMenu(isLocalMode); break; case 'logout': await auth_manager_1.AuthManager.logout(); console.log(chalk_1.default.green('\n👋 ¡Hasta luego!')); process.exit(0); break; case 'exit': console.log(chalk_1.default.green('\n👋 ¡Hasta luego!')); process.exit(0); break; default: console.log(chalk_1.default.red('Opción no válida')); await showMainMenu(isLocalMode); } } async function handleCreateEntityFlow(isLocalMode = false) { try { console.log(chalk_1.default.yellow('\n📋 Configurando flujo entity...')); // 🔍 DETECTAR DIRECTORIO ACTUAL Y APIs DISPONIBLES console.log(chalk_1.default.blue('🔍 Analizando estructura del directorio...')); const directoryInfo = await directory_detector_1.DirectoryDetector.detectCurrentApi(); if (directoryInfo.currentApiName) { console.log(chalk_1.default.green(`✅ API detectada en directorio actual: ${directoryInfo.currentApiName}`)); } else { console.log(chalk_1.default.yellow('⚠️ No se detectó estructura de API en el directorio actual')); } if (directoryInfo.possibleApiNames.length > 0) { console.log(chalk_1.default.gray(`📁 APIs disponibles: ${directoryInfo.possibleApiNames.join(', ')}`)); } // 1. Solicitar URL del OpenAPI/Swagger const { swaggerUrl } = await inquirer_1.default.prompt([ { type: 'input', name: 'swaggerUrl', message: 'URL del OpenAPI/Swagger JSON:', default: 'http://backend-platform-prod-env.eba-dddmvypu.us-east-1.elasticbeanstalk.com/openapi.json', validate: (input) => { if (!input.trim()) { return 'La URL del swagger es requerida'; } try { new URL(input.trim()); return true; } catch { return 'Por favor ingresa una URL válida'; } } } ]); // Cargar y analizar el swagger console.log(chalk_1.default.blue('\n🔍 Analizando OpenAPI...')); const swaggerAnalyzer = new swagger_parser_1.SwaggerAnalyzer(); try { await swaggerAnalyzer.loadFromUrl(swaggerUrl.trim()); } catch (error) { console.error(chalk_1.default.red('\n❌ Error cargando el swagger:'), error); return await handleCreateEntityFlow(isLocalMode); } const availableEntities = swaggerAnalyzer.getAvailableEntities(); if (availableEntities.length === 0) { console.log(chalk_1.default.yellow('\n⚠️ No se encontraron entidades en el swagger')); return await showMainMenu(isLocalMode); } console.log(chalk_1.default.green(`\n✅ Se encontraron ${availableEntities.length} entidades disponibles`)); // 🔗 CONFIGURAR NOMBRE DE LA API const detectedApiName = swaggerAnalyzer.getDetectedApiName(); const suggestedNames = swaggerAnalyzer.suggestApiNames(); let apiName; if (detectedApiName) { const { useDetectedApi } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'useDetectedApi', message: `¿Usar la API detectada "${detectedApiName}"?`, default: true } ]); if (useDetectedApi) { apiName = detectedApiName; } else { const { selectedApiName } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedApiName', message: 'Selecciona el nombre de la API:', choices: [ ...suggestedNames.map(name => ({ name, value: name })), { name: '📝 Ingresar nombre personalizado', value: 'custom' } ] } ]); if (selectedApiName === 'custom') { const { customApiName } = await inquirer_1.default.prompt([ { type: 'input', name: 'customApiName', message: 'Nombre de la API:', validate: (input) => { if (!input.trim()) { return 'El nombre de la API es requerido'; } if (!/^[a-z][a-z0-9-]*$/.test(input.trim())) { return 'El nombre debe empezar con minúscula y solo contener letras, números y guiones'; } return true; } } ]); apiName = customApiName.trim(); } else { apiName = selectedApiName; } } } else { const { selectedApiName } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedApiName', message: 'No se pudo detectar automáticamente. Selecciona el nombre de la API:', choices: [ ...suggestedNames.map(name => ({ name, value: name })), { name: '📝 Ingresar nombre personalizado', value: 'custom' } ] } ]); if (selectedApiName === 'custom') { const { customApiName } = await inquirer_1.default.prompt([ { type: 'input', name: 'customApiName', message: 'Nombre de la API:', validate: (input) => { if (!input.trim()) { return 'El nombre de la API es requerido'; } if (!/^[a-z][a-z0-9-]*$/.test(input.trim())) { return 'El nombre debe empezar con minúscula y solo contener letras, números y guiones'; } return true; } } ]); apiName = customApiName.trim(); } else { apiName = selectedApiName; } } console.log(chalk_1.default.blue(`🔗 API configurada: ${apiName}`)); // 📁 SELECCIONAR DIRECTORIO DE DESTINO (dónde crear físicamente) console.log(chalk_1.default.yellow('\n📁 Configurando directorio de destino...')); let targetBasePath; if (isLocalMode) { // En modo local, permitir seleccionar carpeta existente o crear nueva const testOutputPath = path.resolve('./test-output'); await fs.ensureDir(testOutputPath); // Buscar carpetas existentes en test-output const existingDirs = []; if (await fs.pathExists(testOutputPath)) { const contents = await fs.readdir(testOutputPath); for (const item of contents) { const itemPath = path.join(testOutputPath, item); const stat = await fs.stat(itemPath); if (stat.isDirectory()) { existingDirs.push(item); } } } const directoryChoices = []; // Agregar carpetas existentes for (const dir of existingDirs) { directoryChoices.push({ name: `${dir} (existente)`, value: path.join(testOutputPath, dir), short: dir }); } // Agregar opción para crear nueva carpeta directoryChoices.push({ name: `Crear nueva carpeta: ${apiName}`, value: 'create_new', short: `nuevo: ${apiName}` }); const { selectedDirectory } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedDirectory', message: '¿En qué directorio crear la entidad?', choices: directoryChoices, pageSize: 10 } ]); if (selectedDirectory === 'create_new') { targetBasePath = path.resolve(`./test-output/${apiName}`); await fs.ensureDir(targetBasePath); } else { targetBasePath = selectedDirectory; } console.log(chalk_1.default.green(`✅ Directorio target válido: ${targetBasePath}`)); } else { // En proyecto real, crear opciones de directorio const directoryChoices = []; // Opción 1: Directorio actual (si tiene estructura de API) if (directoryInfo.currentApiName) { directoryChoices.push({ name: `${directoryInfo.currentApiName} (directorio actual)`, value: directoryInfo.baseDirectory, short: directoryInfo.currentApiName }); } // Opción 2: APIs hermanas disponibles for (const siblingApi of directoryInfo.possibleApiNames) { if (siblingApi !== directoryInfo.currentApiName) { const siblingPath = path.join(path.dirname(directoryInfo.baseDirectory), siblingApi); directoryChoices.push({ name: `${siblingApi} (API hermana)`, value: siblingPath, short: siblingApi }); } } const { selectedDirectory } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedDirectory', message: '¿En qué directorio crear la entidad?', choices: directoryChoices, pageSize: 10 } ]); targetBasePath = selectedDirectory; // Verificar que el directorio target sea válido const validation = await directory_detector_1.DirectoryDetector.validateTargetPath(targetBasePath); if (!validation.isValid) { console.log(chalk_1.default.red(`\n❌ ${validation.message}`)); return await showMainMenu(isLocalMode); } console.log(chalk_1.default.green(`✅ ${validation.message}`)); } console.log(chalk_1.default.blue(`🎯 Generando entidad en API: ${apiName}`)); console.log(chalk_1.default.gray(`📁 Estructura: ${targetBasePath}/domain/models/apis/${apiName}/entities/...`)); // Mostrar entidades disponibles para selección const entityChoices = availableEntities.map(entity => ({ name: entity, value: entity })); entityChoices.push({ name: chalk_1.default.gray('📝 Ingresar nombre personalizado'), value: 'custom' }); const { selectedEntity } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedEntity', message: 'Selecciona la entidad a generar:', choices: entityChoices, pageSize: 15 } ]); let entityName = selectedEntity; // Si seleccionó custom, pedir el nombre if (selectedEntity === 'custom') { const { customEntityName } = await inquirer_1.default.prompt([ { type: 'input', name: 'customEntityName', message: 'Nombre de la entity personalizada:', validate: (input) => { if (!input.trim()) { return 'El nombre de la entity es requerido'; } if (!/^[A-Z][a-zA-Z0-9]*$/.test(input.trim())) { return 'El nombre debe empezar con mayúscula y solo contener letras y números'; } return true; } } ]); entityName = customEntityName.trim(); } // Mostrar información de la entidad seleccionada if (selectedEntity !== 'custom') { swaggerAnalyzer.printEntityInfo(entityName); } const { confirmGeneration } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'confirmGeneration', message: `¿Generar flujo completo para la entity "${entityName}"?`, default: true } ]); if (confirmGeneration) { const entitySchema = selectedEntity !== 'custom' ? swaggerAnalyzer.getEntitySchema(entityName) : null; // 🔍 VALIDACIONES PRE-GENERACIÓN (usar variables target) const validation = await project_validator_1.ProjectValidator.validateBeforeGeneration(entityName, targetBasePath, apiName); if (!validation.canProceed) { console.log(chalk_1.default.red('\n❌ No se puede continuar debido a problemas de validación')); if (validation.recommendations.length > 0) { console.log(chalk_1.default.yellow('\n💡 Recomendaciones:')); validation.recommendations.forEach(rec => console.log(chalk_1.default.gray(` • ${rec}`))); } return await showMainMenu(isLocalMode); } // Mostrar advertencias si existen if (validation.recommendations.length > 0) { console.log(chalk_1.default.yellow('\n⚠️ Advertencias:')); validation.recommendations.forEach(rec => console.log(chalk_1.default.gray(` • ${rec}`))); } // Mostrar resumen de lo que se va a generar project_validator_1.ProjectValidator.showGenerationSummary(entityName, targetBasePath, validation.entityResult.exists); // Confirmación final si hay conflictos if (validation.entityResult.exists) { const { proceedWithOverwrite } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'proceedWithOverwrite', message: chalk_1.default.yellow('⚠️ ¿Continuar y sobrescribir los archivos existentes?'), default: false } ]); if (!proceedWithOverwrite) { console.log(chalk_1.default.yellow('\n❌ Generación cancelada por el usuario')); return await showMainMenu(isLocalMode); } } console.log(chalk_1.default.blue(`\n🔧 Generando flujo para ${entityName}...`)); await (0, correct_entity_flow_generator_1.createCorrectEntityFlow)(entityName, targetBasePath, entitySchema, apiName); console.log(chalk_1.default.green(`\n✅ Flujo ${entityName} generado exitosamente!`)); if (isLocalMode) { console.log(chalk_1.default.blue(`📁 Archivos generados en: ${targetBasePath}`)); console.log(chalk_1.default.gray('💡 Puedes revisar los archivos en la carpeta test-output/')); } else { console.log(chalk_1.default.blue(`📁 Archivos generados en el proyecto real: ${targetBasePath}`)); } } else { console.log(chalk_1.default.yellow('\n❌ Generación cancelada')); } // Volver al menú principal const { backToMenu } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'backToMenu', message: '¿Volver al menú principal?', default: true } ]); if (backToMenu) { await showMainMenu(isLocalMode); } else { console.log(chalk_1.default.green('\n👋 ¡Hasta luego!')); process.exit(0); } } catch (error) { console.error(chalk_1.default.red('\n❌ Error al generar el flujo:'), error); await showMainMenu(isLocalMode); } } /** * Maneja la creación de un flujo de negocio completo basado en swagger */ async function handleCreateBusinessFlow(isLocalMode = false) { try { console.log(chalk_1.default.yellow('\n📋 Configurando flujo de negocio...')); // 🔍 DETECTAR DIRECTORIO ACTUAL Y APIs DISPONIBLES console.log(chalk_1.default.blue('🔍 Analizando estructura del directorio...')); const directoryInfo = await directory_detector_1.DirectoryDetector.detectCurrentApi(); if (directoryInfo.currentApiName) { console.log(chalk_1.default.green(`✅ API detectada en directorio actual: ${directoryInfo.currentApiName}`)); } else { console.log(chalk_1.default.yellow('⚠️ No se detectó estructura de API en el directorio actual')); } if (directoryInfo.possibleApiNames.length > 0) { console.log(chalk_1.default.gray(`📁 APIs disponibles: ${directoryInfo.possibleApiNames.join(', ')}`)); } // 1. Solicitar URL del OpenAPI/Swagger const { swaggerUrl } = await inquirer_1.default.prompt([ { type: 'input', name: 'swaggerUrl', message: 'URL del OpenAPI/Swagger JSON:', default: 'http://backend-platform-prod-env.eba-dddmvypu.us-east-1.elasticbeanstalk.com/openapi.json', validate: (input) => { if (!input.trim()) { return 'La URL del swagger es requerida'; } try { new URL(input.trim()); return true; } catch { return 'Por favor ingresa una URL válida'; } } } ]); // Cargar y analizar el swagger console.log(chalk_1.default.blue('\n🔍 Analizando OpenAPI...')); const swaggerAnalyzer = new swagger_parser_1.SwaggerAnalyzer(); try { await swaggerAnalyzer.loadFromUrl(swaggerUrl.trim()); } catch (error) { console.error(chalk_1.default.red('\n❌ Error cargando el swagger:'), error); return await handleCreateBusinessFlow(isLocalMode); } const availableBusinessServices = swaggerAnalyzer.getAvailableBusinessServices(); if (availableBusinessServices.length === 0) { console.log(chalk_1.default.yellow('\n⚠️ No se encontraron servicios de negocio en el swagger')); return await showMainMenu(isLocalMode); } console.log(chalk_1.default.green(`\n✅ Se encontraron ${availableBusinessServices.length} servicios de negocio disponibles`)); // 🔗 CONFIGURAR NOMBRE DE LA API const detectedApiName = swaggerAnalyzer.getDetectedApiName(); const suggestedNames = swaggerAnalyzer.suggestApiNames(); let apiName; if (detectedApiName) { const { useDetectedApi } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'useDetectedApi', message: `¿Usar la API detectada "${detectedApiName}"?`, default: true } ]); if (useDetectedApi) { apiName = detectedApiName; } else { const { selectedApiName } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedApiName', message: 'Selecciona el nombre de la API:', choices: [ ...suggestedNames.map(name => ({ name, value: name })), { name: '📝 Ingresar nombre personalizado', value: 'custom' } ] } ]); if (selectedApiName === 'custom') { const { customApiName } = await inquirer_1.default.prompt([ { type: 'input', name: 'customApiName', message: 'Nombre de la API:', validate: (input) => { if (!input.trim()) { return 'El nombre de la API es requerido'; } return true; } } ]); apiName = customApiName.trim(); } else { apiName = selectedApiName; } } } else { // No se detectó API, solicitar nombre const choices = [ ...suggestedNames.map(name => ({ name, value: name })), { name: '📝 Ingresar nombre personalizado', value: 'custom' } ]; const { selectedApiName } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedApiName', message: 'Selecciona el nombre de la API:', choices } ]); if (selectedApiName === 'custom') { const { customApiName } = await inquirer_1.default.prompt([ { type: 'input', name: 'customApiName', message: 'Nombre de la API:', validate: (input) => { if (!input.trim()) { return 'El nombre de la API es requerido'; } return true; } } ]); apiName = customApiName.trim(); } else { apiName = selectedApiName; } } console.log(chalk_1.default.green(`🔗 API configurada: ${apiName}`)); // 📁 CONFIGURAR DIRECTORIO DE DESTINO console.log(chalk_1.default.blue('\n📁 Configurando directorio de destino...')); let targetDirectory; if (isLocalMode) { // En modo local, usar test-output con opciones más flexibles const localTestPath = './test-output'; // Crear test-output si no existe await fs.ensureDir(localTestPath); // Obtener carpetas existentes en test-output const existingFolders = (await fs.readdir(localTestPath, { withFileTypes: true })) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); const folderChoices = [ ...existingFolders.map(folder => ({ name: `📁 ${folder} (existente)`, value: folder })), { name: '✨ Crear nueva carpeta', value: 'new-folder' } ]; if (folderChoices.length === 1) { // Solo la opción de crear nueva carpeta const { newFolderName } = await inquirer_1.default.prompt([ { type: 'input', name: 'newFolderName', message: '¿En qué directorio crear la entidad?', default: apiName, validate: (input) => { if (!input.trim()) { return 'El nombre del directorio es requerido'; } return true; } } ]); targetDirectory = path.join(localTestPath, newFolderName.trim()); } else { const { selectedFolder } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedFolder', message: '¿En qué directorio crear la entidad?', choices: folderChoices } ]); if (selectedFolder === 'new-folder') { const { newFolderName } = await inquirer_1.default.prompt([ { type: 'input', name: 'newFolderName', message: 'Nombre del nuevo directorio:', default: apiName, validate: (input) => { if (!input.trim()) { return 'El nombre del directorio es requerido'; } return true; } } ]); targetDirectory = path.join(localTestPath, newFolderName.trim()); } else { targetDirectory = path.join(localTestPath, selectedFolder); } } } else { // Modo producción: detectar APIs hermanas const currentDir = process.cwd(); // Buscar directorios hermanos que puedan ser APIs const parentDir = path.dirname(currentDir); const siblingDirs = (await fs.readdir(parentDir, { withFileTypes: true })) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name) .filter(name => !name.startsWith('.')); if (siblingDirs.length > 0) { const dirChoices = siblingDirs.map(dir => ({ name: `📁 ${dir}`, value: path.join(parentDir, dir) })); const { selectedDir } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedDir', message: 'Selecciona el directorio de destino:', choices: dirChoices } ]); targetDirectory = selectedDir; } else { targetDirectory = currentDir; } } // Validar que el directorio target es válido try { await fs.ensureDir(targetDirectory); console.log(chalk_1.default.green(`✅ Directorio target válido: ${targetDirectory}`)); } catch (error) { console.error(chalk_1.default.red(`❌ Error accediendo al directorio target: ${targetDirectory}`)); return await showMainMenu(isLocalMode); } console.log(chalk_1.default.cyan(`🎯 Generando servicio de negocio en API: ${apiName}`)); console.log(chalk_1.default.gray(`📁 Estructura: ${targetDirectory}/domain/models/apis/${apiName}/business/...`)); // 4. Seleccionar servicio de negocio a generar const serviceChoices = availableBusinessServices.map(service => ({ name: service, value: service })); const { selectedService } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedService', message: 'Selecciona el servicio de negocio a generar:', choices: serviceChoices, pageSize: 15 } ]); // 5. Obtener schema del servicio de negocio seleccionado const serviceSchema = swaggerAnalyzer.getBusinessServiceSchema(selectedService); if (!serviceSchema) { console.log(chalk_1.default.red(`❌ No se pudo obtener el schema para el servicio: ${selectedService}`)); return await showMainMenu(isLocalMode); } // 6. Mostrar información del servicio de negocio console.log(chalk_1.default.cyan(`\n📋 Información del servicio: ${selectedService}`)); if (serviceSchema.businessOperations && serviceSchema.businessOperations.length > 0) { console.log(chalk_1.default.blue('\n🔧 Operaciones de negocio:')); serviceSchema.businessOperations.forEach(op => { console.log(` • ${op.method} ${op.path} - ${op.summary || 'Sin descripción'}`); if (op.requestSchema) { console.log(` 📥 Request: ${op.requestSchema}`); } if (op.responseSchema) { console.log(` 📤 Response: ${op.responseSchema}`); } if (op.fields.length > 0) { console.log(chalk_1.default.gray(` 📊 Campos (${op.fields.length}):`)); op.fields.forEach(field => { const required = field.required ? '🔴' : '🔵'; console.log(chalk_1.default.gray(` ${required} ${field.name}: ${field.type}`)); }); } console.log(''); }); } else { console.log(chalk_1.default.blue('\n🔧 Operaciones disponibles:')); console.log(` • Crear: ${serviceSchema.operations.create ? '✅' : '❌'}`); console.log(` • Leer: ${serviceSchema.operations.read ? '✅' : '❌'}`); console.log(` • Actualizar: ${serviceSchema.operations.update ? '✅' : '❌'}`); console.log(` • Eliminar: ${serviceSchema.operations.delete ? '✅' : '❌'}`); console.log(` • Listar: ${serviceSchema.operations.list ? '✅' : '❌'}`); console.log(chalk_1.default.blue(`\n📊 Campos (${serviceSchema.fields.length}):`)); serviceSchema.fields.forEach(field => { const required = field.required ? '🔴' : '🔵'; console.log(` ${required} ${field.name}: ${field.type}`); }); } // 7. Confirmar generación const { shouldGenerate } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'shouldGenerate', message: `¿Generar flujo de negocio completo para el servicio "${selectedService}"?`, default: true } ]); if (!shouldGenerate) { return await showMainMenu(isLocalMode); } // 8. Ejecutar validaciones pre-generación console.log(chalk_1.default.blue('\n🔍 Ejecutando validaciones pre-generación...')); const validation = await project_validator_1.ProjectValidator.validateProjectStructure(targetDirectory); console.log(chalk_1.default.blue('\n🔍 Validando estructura del proyecto...')); console.log(chalk_1.default.blue(`🔍 Verificando si el servicio "${selectedService}" ya existe...`)); // Validar que el servicio no existe en business (similar a entities) const businessEntityExists = await project_validator_1.ProjectValidator.checkBusinessEntityExists(selectedService, targetDirectory, apiName); if (validation.isValid) { console.log(chalk_1.default.green('✅ Estructura del proyecto válida')); } // Mostrar warnings si los hay if (validation.warnings && validation.warnings.length > 0) { validation.warnings.forEach((warning) => { console.log(chalk_1.default.yellow(`⚠️ ${warning}`)); }); } if (businessEntityExists.exists) { console.log(chalk_1.default.yellow(`⚠️ El servicio "${selectedService}" ya existe parcial o completamente en business`)); if (businessEntityExists.conflictingFiles.length > 0) { console.log(chalk_1.default.yellow('\nArchivos/directorios existentes:')); businessEntityExists.conflictingFiles.forEach((detail) => { console.log(chalk_1.default.gray(` ${detail}`)); }); } } // Mostrar warnings de validación if ((validation.warnings && validation.warnings.length > 0) || businessEntityExists.exists) { console.log(chalk_1.default.yellow('\n⚠️ Advertencias:')); if (businessEntityExists.exists) { console.log(chalk_1.default.yellow(' • La entidad ya existe. Si continúas, se sobrescribirán los archivos existentes')); console.log(chalk_1.default.yellow(' • Considera hacer un backup antes de continuar')); } if (validation.warnings) { validation.warnings.forEach((warning) => { console.log(chalk_1.default.yellow(` • ${warning}`)); }); } } // 9. Mostrar resumen antes de generar console.log(chalk_1.default.blue('\n📋 Resumen de generación:')); console.log(chalk_1.default.white(`Servicio: ${selectedService}`)); console.log(chalk_1.default.white(`Ubicación: ${targetDirectory}`)); console.log(chalk_1.default.white(`Archivos a generar: ~29 archivos TypeScript`)); if (businessEntityExists.exists) { console.log(chalk_1.default.yellow('⚠️ Se sobrescribirán archivos existentes')); } console.log(chalk_1.default.gray('\n📁 Directorios que se crearán/utilizarán:')); const serviceNameLower = selectedService.toLowerCase(); console.log(chalk_1.default.gray(` 📂 domain/models/apis/${apiName}/business/${serviceNameLower}/`)); console.log(chalk_1.default.gray(` 📂 domain/services/use_cases/apis/${apiName}/business/${serviceNameLower}/`)); console.log(chalk_1.default.gray(` 📂 infrastructure/entities/apis/${apiName}/business/${serviceNameLower}/`)); console.log(chalk_1.default.gray(` 📂 infrastructure/mappers/apis/${apiName}/business/${serviceNameLower}/`)); console.log(chalk_1.default.gray(` 📂 infrastructure/repositories/.../business/${serviceNameLower}/`)); console.log(chalk_1.default.gray(` 📂 facade/apis/${apiName}/business/`)); console.log(chalk_1.default.gray(` 📂 injection folders...`)); // Confirmación final si hay advertencias if ((validation.warnings && validation.warnings.length > 0) || businessEntityExists.exists) { const { shouldContinue } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'shouldContinue', message: '⚠️ ¿Continuar y sobrescribir los archivos existentes?', default: false } ]); if (!shouldContinue) { console.log(chalk_1.default.yellow('Operación cancelada por el usuario')); return await showMainMenu(isLocalMode); } } // 10. Generar el flujo de negocio console.log(chalk_1.default.green(`\n🔧 Generando flujo de negocio para ${selectedService}...`)); await (0, business_flow_generator_1.createBusinessFlow)(selectedService, targetDirectory, serviceSchema, apiName); console.log(chalk_1.default.green(`\n✅ Flujo de negocio ${selectedService} generado exitosamente!`)); console.log(chalk_1.default.cyan(`📁 Archivos generados en: ${targetDirectory}`)); console.log(chalk_1.default.gray('💡 Puedes revisar los archivos en la carpeta especificada')); // Preguntar si quiere continuar const { continueWorking } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'continueWorking', message: '¿Volver al menú principal?', default: true } ]); if (continueWorking) { await showMainMenu(isLocalMode); } else { console.log(chalk_1.default.blue('\n👋 ¡Hasta luego!')); } } catch (error) { console.error(chalk_1.default.red('\n❌ Error en el flujo de negocio:'), error); const { retry } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'retry', message: '¿Intentar nuevamente?', default: true } ]); if (retry) { await handleCreateBusinessFlow(isLocalMode); } else { await showMainMenu(isLocalMode); } } } /** * Maneja el flujo de limpieza/eliminación de código generado */ async function handleCleanup(isLocalMode) { console.log(chalk_1.default.blue.bold('\n🧹 LIMPIEZA DE CÓDIGO GENERADO')); console.log(chalk_1.default.gray('Elimina entidades, APIs o todo el contenido generado\n')); // Determinar la ruta base let basePath; if (isLocalMode) { // Si ya estamos en test-output, usar el directorio actual if (process.cwd().includes('test-output')) { basePath = process.cwd(); } else { basePath = './test-output'; } } else { basePath = process.cwd(); } try { // Detectar qué está disponible para limpiar const entities = await (0, cleanup_generator_1.detectGeneratedEntities)(basePath); console.log(chalk_1.default.cyan('📊 Estado actual:')); console.log(chalk_1.default.gray(` Entidades detectadas: ${entities.length}`)); console.log(chalk_1.default.gray(` Directorio base: ${basePath}`)); if (entities.length > 0) { console.log(chalk_1.default.gray(` APIs encontradas: ${[...new Set(entities.map(e => e.apiName))].join(', ')}`)); } console.log(''); if (entities.length === 0) { console.log(chalk_1.default.yellow('⚠️ No se encontraron entidades para eliminar')); await showMainMenu(isLocalMode); return; } // Ir directamente a la selección de entidad await handleEntityCleanup(basePath, entities, isLocalMode); } catch (error) { console.error(chalk_1.default.red('❌ Error en la limpieza:'), error); await showMainMenu(isLocalMode); } } /** * Maneja la limpieza de una entidad específica */ async function handleEntityCleanup(basePath, entities, isLocalMode) { console.log(chalk_1.default.blue('\n📋 Seleccionar entidad a eliminar')); // Crear choices organizadas por API const entityChoices = []; const groupedEntities = entities.reduce((acc, entity) => { if (!acc[entity.apiName]) { acc[entity.apiName] = []; } acc[entity.apiName].push(entity); return acc; }, {}); Object.keys(groupedEntities).forEach(apiName => { entityChoices.push(new inquirer_1.default.Separator(`--- API: ${apiName} ---`)); groupedEntities[apiName].forEach((entity) => { entityChoices.push({ name: `${entity.name} (${entity.paths.length} archivos)`, value: { apiName: entity.apiName, entityName: entity.name, paths: entity.paths } }); }); }); entityChoices.push({ name: '🔙 Volver', value: 'back' }); const { selectedEntity } = await inquirer_1.default.prompt([ { type: 'list', name: 'selectedEntity', message: 'Selecciona la entidad a eliminar:', choices: entityChoices, pageSize: 15 } ]); if (selectedEntity === 'back') { await handleCleanup(isLocalMode); return; } // Mostrar vista previa de lo que se eliminará console.log(chalk_1.default.yellow(`\n⚠️ Vista previa de eliminación:`)); console.log(chalk_1.default.gray(` Entidad: ${selectedEntity.entityName}`)); console.log(chalk_1.default.gray(` API: ${selectedEntity.apiName}`)); console.log(chalk_1.default.gray(` Archivos a eliminar: ${selectedEntity.paths.length}`)); const { confirmDelete } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'confirmDelete', message: `¿Estás seguro de eliminar la entidad "${selectedEntity.entityName}"?`, default: false } ]); if (confirmDelete) { await (0, cleanup_generator_1.cleanupEntity)(basePath, selectedEntity.apiName, selectedEntity.entityName); console.log(chalk_1.default.green('\n✅ Entidad eliminada exitosamente!')); } else { console.log(chalk_1.default.blue('\n🚫 Operación cancelada')); } const { goToMenu } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'goToMenu', message: '¿Volver al menú principal?', default: true } ]); if (goToMenu) { await showMainMenu(isLocalMode); } else { await handleCleanup(isLocalMode); } } // Función principal async function main() { try { // Verificar argumentos especiales if (process.argv.includes('--logout')) { await auth_manager_1.AuthManager.logout(); return; } if (process.argv.includes('--session-info')) { await auth_manager_1.AuthManager.showSessionInfo(); return; } // 🔐 AUTENTICACIÓN REQUERIDA const isAuthenticated = await auth_manager_1.AuthManager.authenticate(); if (!isAuthenticated) { console.log(chalk_1.default.red('❌ Acceso denegado')); process.exit(1); } // Detectar si es modo local const isLocalMode = process.argv.includes('--local'); if (isLocalMode) { console.log(chalk_1.default.blue('🧪 Modo LOCAL activado')); console.log(chalk_1.default.gray('Los archivos se generarán en: ./test-output/{api-name}/\n')); } else { console.log(chalk_1.default.blue('🚀 Modo PRODUCCIÓN activado')); console.log(chalk_1.default.gray(`Los archivos se generarán en: ${process.cwd()}/{api-name}/\n`)); } await showMainMenu(isLocalMode); } catch (error) { console.error(chalk_1.default.red('Error en la aplicación:'), error); process.exit(1); } } // Ejecutar solo si es el archivo principal if (require.main === module) { main(); } //# sourceMappingURL=cli.js.map