UNPKG

@diagramers/cli

Version:

Diagramers CLI - Command-line tools for managing Diagramers projects

1,286 lines (1,196 loc) 88.4 kB
"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.ProjectExtender = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const chalk_1 = __importDefault(require("chalk")); const api_generator_1 = require("./api-generator"); class ProjectExtender { constructor() { this.features = [ { name: 'auth', description: 'Authentication system with JWT', files: ['src/features/auth/**/*'], dependencies: ['jsonwebtoken', 'bcryptjs'] }, { name: 'email', description: 'Modular, user-configurable email provider system', files: ['src/modules/email/**/*'] }, { name: 'socket', description: 'Real-time WebSocket communication', files: ['src/features/socket/**/*'], dependencies: ['socket.io'] }, { name: 'cron', description: 'Scheduled task system', files: ['src/features/cron/**/*'], dependencies: ['node-cron'] }, { name: 'audit', description: 'Audit logging system', files: ['src/features/audit/**/*'] } ]; } async listFeatures() { console.log(chalk_1.default.blue('�� Available features and templates:')); console.log(''); console.log(chalk_1.default.green('🔧 Features:')); this.features.forEach(feature => { console.log(chalk_1.default.green(` ${feature.name}`)); console.log(chalk_1.default.gray(` ${feature.description}`)); if (feature.dependencies && feature.dependencies.length > 0) { console.log(chalk_1.default.yellow(` Dependencies: ${feature.dependencies.join(', ')}`)); } console.log(''); }); console.log(chalk_1.default.blue('📦 Module Templates:')); console.log(chalk_1.default.green(' --module <name> --crud --fields field1,field2')); console.log(chalk_1.default.gray(' Generate complete module with CRUD operations')); console.log(''); console.log(chalk_1.default.blue('🗄️ Database Templates:')); console.log(chalk_1.default.green(' --table <name> --fields field1,field2 --type mongodb')); console.log(chalk_1.default.gray(' Generate database table/collection schema')); console.log(''); console.log(chalk_1.default.blue('🔗 Relation Templates:')); console.log(chalk_1.default.green(' --relation <name> --type mongodb')); console.log(chalk_1.default.gray(' Generate database relations and references')); console.log(''); } async addFeature(featureName) { const feature = this.features.find(f => f.name === featureName); if (!feature) { throw new Error(`Feature '${featureName}' not found. Use --list to see available features.`); } const projectPath = process.cwd(); // Check if feature already exists const featurePath = path.join(projectPath, 'src', 'features', featureName); if (await fs.pathExists(featurePath)) { throw new Error(`Feature '${featureName}' already exists in this project.`); } // Create feature directory structure await this.createFeatureStructure(projectPath, feature); // Add dependencies if any if (feature.dependencies && feature.dependencies.length > 0) { await this.addDependencies(feature.dependencies); } // Update main configuration to use the extended feature await this.updateMainConfiguration(projectPath, feature); console.log(chalk_1.default.green(`✅ Feature '${featureName}' added successfully!`)); console.log(chalk_1.default.blue(`📝 Main configuration updated to include ${featureName} feature`)); } async generateModule(moduleName, options = {}) { const projectPath = process.cwd(); // Check if module already exists const modulePath = path.join(projectPath, 'src', 'modules', moduleName); if (await fs.pathExists(modulePath) && !options.force) { throw new Error(`Module '${moduleName}' already exists. Use --force to override.`); } // Parse options const fields = options.fields || ['name', 'description', 'status']; const generateRoutes = options.generateRoutes !== false; const generateDatabase = options.generateDatabase !== false; const crud = options.crud || false; console.log(chalk_1.default.blue(`📦 Generating module '${moduleName}'...`)); // Create module structure await this.createModuleStructure(projectPath, moduleName, { fields, crud, generateRoutes, generateDatabase }); // Generate CRUD operations if requested if (crud) { console.log(chalk_1.default.blue('🔧 Generating CRUD operations...')); await this.generateCRUDOperations(projectPath, moduleName, { fields }); } // Register module await this.registerModule(projectPath, moduleName); // Update configuration await this.updateDiagramersConfig(projectPath, moduleName); console.log(chalk_1.default.green(`✅ Module '${moduleName}' generated successfully!`)); console.log(chalk_1.default.blue(`📁 Location: src/modules/${moduleName}/`)); if (generateRoutes) { console.log(chalk_1.default.blue(`🔗 Routes: /api/${moduleName}s`)); } if (generateDatabase) { console.log(chalk_1.default.blue(`🗄️ Database: ${moduleName} collection`)); } if (crud) { console.log(chalk_1.default.blue(`🔧 CRUD: Full CRUD operations included`)); } } async generateTable(tableName, options = {}) { const projectPath = process.cwd(); // Parse options const fields = options.fields || ['name', 'description', 'status']; const databaseType = options.type || 'mongodb'; const generateSeeder = options.seeder !== false; const force = options.force || false; console.log(chalk_1.default.blue(`🗄️ Generating table '${tableName}'...`)); // Create table schema await this.createTableSchema(projectPath, tableName, { fields, type: databaseType, force }); // Add to seeder if requested if (generateSeeder) { console.log(chalk_1.default.blue('🌱 Adding to database seeder...')); await this.updateSeeder(projectPath, tableName); } console.log(chalk_1.default.green(`✅ Table '${tableName}' generated successfully!`)); console.log(chalk_1.default.blue(`📁 Schema: src/modules/${tableName}/schemas/${tableName}.schema.ts`)); console.log(chalk_1.default.blue(`🗄️ Database Type: ${databaseType}`)); if (generateSeeder) { console.log(chalk_1.default.blue(`🌱 Seeder: Added to src/core/database/seeder.ts`)); } } async generateRelations(relationName, options = {}) { console.log(chalk_1.default.blue(`🔗 Generating relations for '${relationName}'...`)); // Parse options const relationType = options.type || 'one-to-many'; const moduleName = options.module; const force = options.force || false; // For now, we'll use a simple approach - the user needs to specify both tables if (!options.table1 || !options.table2) { throw new Error('Both table1 and table2 must be specified for relation generation. Use: --table1 <name> --table2 <name>'); } // Import and use the relation-generator service const { generateRelation } = await Promise.resolve().then(() => __importStar(require('./relation-generator'))); await generateRelation(moduleName, options.table1, options.table2, relationType, { force }); console.log(chalk_1.default.green(`✅ Relations generated successfully for '${relationName}'!`)); console.log(chalk_1.default.blue(`🔗 Type: ${relationType}`)); console.log(chalk_1.default.blue(`📦 Module: ${moduleName}`)); console.log(chalk_1.default.blue(`🗄️ Tables: ${options.table1} ↔ ${options.table2}`)); } async generateEndpoint(moduleName, endpointName, options = {}) { console.log(chalk_1.default.blue(`🔧 Adding endpoint '${endpointName}' to module '${moduleName}'...`)); // Parse options const method = options.method || 'GET'; const customPath = options.path || endpointName; const description = options.description || `${endpointName} endpoint for ${moduleName}`; const generateService = options.generateService !== false; const generateController = options.generateController !== false; const generateRoute = options.generateRoute !== false; const force = options.force || false; // Pass options to the api-generator service await (0, api_generator_1.generateEndpoint)(moduleName, endpointName, { method, path: customPath, description, generateService, generateController, generateRoute, force }); console.log(chalk_1.default.green(`✅ Endpoint '${endpointName}' added to module '${moduleName}' successfully!`)); if (generateService) { console.log(chalk_1.default.blue(`📝 Service method generated`)); } if (generateController) { console.log(chalk_1.default.blue(`🎮 Controller method generated`)); } if (generateRoute) { console.log(chalk_1.default.blue(`🛣️ Route generated`)); } } async addServiceMethod(modulePath, moduleName, endpointName, method, description) { const singularName = moduleName; // Use exact name provided by developer const servicePath = path.join(modulePath, 'services', `${singularName}.service.ts`); if (!await fs.pathExists(servicePath)) { throw new Error(`Service file not found: ${servicePath}`); } let serviceContent = await fs.readFile(servicePath, 'utf8'); const moduleNameCapitalized = this.capitalizeFirst(moduleName); const endpointNameCapitalized = this.capitalizeFirst(endpointName); // Generate service method based on HTTP method let serviceMethod = ''; switch (method) { case 'GET': serviceMethod = ` async ${endpointName}(params?: any) { // TODO: Implement ${endpointName} logic // Example: return await ${moduleNameCapitalized}Model.find(params); return { message: '${endpointNameCapitalized} endpoint - implement your logic here', params }; }`; break; case 'POST': serviceMethod = ` async ${endpointName}(data: any) { // TODO: Implement ${endpointName} logic // Example: return await ${moduleNameCapitalized}Model.create(data); return { message: '${endpointNameCapitalized} endpoint - implement your logic here', data }; }`; break; case 'PUT': serviceMethod = ` async ${endpointName}(id: string, data: any) { // TODO: Implement ${endpointName} logic // Example: return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, data, { new: true }); return { message: '${endpointNameCapitalized} endpoint - implement your logic here', id, data }; }`; break; case 'DELETE': serviceMethod = ` async ${endpointName}(id: string) { // TODO: Implement ${endpointName} logic // Example: return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { status: 0 }, { new: true }); return { message: '${endpointNameCapitalized} endpoint - implement your logic here', id }; }`; break; default: serviceMethod = ` async ${endpointName}(params?: any) { // TODO: Implement ${endpointName} logic return { message: '${endpointNameCapitalized} endpoint - implement your logic here', params }; }`; } // Add method before the closing brace const lastBraceIndex = serviceContent.lastIndexOf('}'); serviceContent = serviceContent.slice(0, lastBraceIndex) + serviceMethod + '\n' + serviceContent.slice(lastBraceIndex); await fs.writeFile(servicePath, serviceContent); } async addControllerMethod(modulePath, moduleName, endpointName, method, customPath, description) { const singularName = moduleName; // Use exact name provided by developer const controllerPath = path.join(modulePath, 'controllers', `${singularName}.controller.ts`); if (!await fs.pathExists(controllerPath)) { throw new Error(`Controller file not found: ${controllerPath}`); } let controllerContent = await fs.readFile(controllerPath, 'utf8'); const moduleNameCapitalized = this.capitalizeFirst(moduleName); const endpointNameCapitalized = this.capitalizeFirst(endpointName); // Generate Swagger documentation and controller method let swaggerDoc = ''; let controllerMethod = ''; switch (method) { case 'GET': swaggerDoc = ` /** * @openapi * /api/${moduleName}/${customPath}: * get: * summary: ${endpointNameCapitalized} ${moduleName} * description: ${description} * tags: [${moduleNameCapitalized}s] * responses: * 200: * description: ${endpointNameCapitalized} retrieved successfully * content: * application/json: * schema: * type: object * properties: * Data: * type: object * StatusCode: * type: integer * example: 1000 * 400: * description: Bad request */`; controllerMethod = ` async ${endpointName}(req, res) { const data = await this.service.${endpointName}(req.query); res.json(data); }`; break; case 'POST': swaggerDoc = ` /** * @openapi * /api/${moduleName}/${customPath}: * post: * summary: ${endpointNameCapitalized} ${moduleName} * description: ${description} * tags: [${moduleNameCapitalized}s] * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * # Add your request body properties here * responses: * 201: * description: ${endpointNameCapitalized} created successfully * content: * application/json: * schema: * type: object * properties: * Data: * type: object * StatusCode: * type: integer * example: 1000 * 400: * description: Bad request */`; controllerMethod = ` async ${endpointName}(req, res) { const data = await this.service.${endpointName}(req.body); res.json(data); }`; break; case 'PUT': swaggerDoc = ` /** * @openapi * /api/${moduleName}/${customPath}/{id}: * put: * summary: ${endpointNameCapitalized} ${moduleName} * description: ${description} * tags: [${moduleNameCapitalized}s] * parameters: * - in: path * name: id * required: true * schema: * type: string * description: ${moduleNameCapitalized} ID * requestBody: * required: true * content: * application/json: * schema: * type: object * properties: * # Add your request body properties here * responses: * 200: * description: ${endpointNameCapitalized} updated successfully * content: * application/json: * schema: * type: object * properties: * Data: * type: object * StatusCode: * type: integer * example: 1000 * 404: * description: ${moduleNameCapitalized} not found */`; controllerMethod = ` async ${endpointName}(req, res) { const data = await this.service.${endpointName}(req.params.id, req.body); res.json(data); }`; break; case 'DELETE': swaggerDoc = ` /** * @openapi * /api/${moduleName}/${customPath}/{id}: * delete: * summary: ${endpointNameCapitalized} ${moduleName} * description: ${description} * tags: [${moduleNameCapitalized}s] * parameters: * - in: path * name: id * required: true * schema: * type: string * description: ${moduleNameCapitalized} ID * responses: * 200: * description: ${endpointNameCapitalized} successful * content: * application/json: * schema: * type: object * properties: * Data: * type: object * StatusCode: * type: integer * example: 1000 * 404: * description: ${moduleNameCapitalized} not found */`; controllerMethod = ` async ${endpointName}(req, res) { const data = await this.service.${endpointName}(req.params.id); res.json(data); }`; break; default: swaggerDoc = ` /** * @openapi * /api/${moduleName}/${customPath}: * ${method.toLowerCase()}: * summary: ${endpointNameCapitalized} ${moduleName} * description: ${description} * tags: [${moduleNameCapitalized}s] * responses: * 200: * description: ${endpointNameCapitalized} successful * content: * application/json: * schema: * type: object * properties: * Data: * type: object * StatusCode: * type: integer * example: 1000 */`; controllerMethod = ` async ${endpointName}(req, res) { const data = await this.service.${endpointName}(req.query); res.json(data); }`; } // Add method before the closing brace const lastBraceIndex = controllerContent.lastIndexOf('}'); const methodWithDoc = swaggerDoc + '\n' + controllerMethod; controllerContent = controllerContent.slice(0, lastBraceIndex) + methodWithDoc + '\n' + controllerContent.slice(lastBraceIndex); await fs.writeFile(controllerPath, controllerContent); } async addRoute(modulePath, moduleName, endpointName, method, customPath) { const singularName = moduleName; // Use exact name provided by developer const routePath = path.join(modulePath, 'routes', `${singularName}.routes.ts`); if (!await fs.pathExists(routePath)) { throw new Error(`Route file not found: ${routePath}`); } let routeContent = await fs.readFile(routePath, 'utf8'); // Generate route based on HTTP method let routeLine = ''; switch (method) { case 'GET': routeLine = `router.get('/${customPath}', (req, res) => controller.${endpointName}(req, res));`; break; case 'POST': routeLine = `router.post('/${customPath}', (req, res) => controller.${endpointName}(req, res));`; break; case 'PUT': routeLine = `router.put('/${customPath}/:id', (req, res) => controller.${endpointName}(req, res));`; break; case 'DELETE': routeLine = `router.delete('/${customPath}/:id', (req, res) => controller.${endpointName}(req, res));`; break; default: routeLine = `router.${method.toLowerCase()}('/${customPath}', (req, res) => controller.${endpointName}(req, res));`; } // Add route before the export statement const exportIndex = routeContent.indexOf('export default router;'); if (exportIndex !== -1) { routeContent = routeContent.slice(0, exportIndex) + routeLine + '\n\n' + routeContent.slice(exportIndex); } else { // If no export found, add at the end routeContent += '\n' + routeLine + '\n'; } await fs.writeFile(routePath, routeContent); } async createFeatureStructure(projectPath, feature) { const featurePath = path.join(projectPath, 'src', 'features', feature.name); // Create basic feature structure const structure = { 'controllers': 'Feature controllers', 'services': 'Feature business logic', 'schemas': 'Feature validation schemas', 'routes': 'Feature API routes', 'types': 'Feature TypeScript types', 'providers': 'Feature providers and implementations' }; for (const [dir, description] of Object.entries(structure)) { const dirPath = path.join(featurePath, dir); await fs.ensureDir(dirPath); // Create index file const indexContent = `// ${description} for ${feature.name} feature export * from './${feature.name}-${dir.slice(0, -1)}'; `; await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent); } // Create main feature file with core functions const mainFeatureContent = await this.generateFeatureCore(feature.name); await fs.writeFile(path.join(featurePath, `${feature.name}.feature.ts`), mainFeatureContent); // Create feature configuration const configContent = this.generateFeatureConfig(feature.name); await fs.writeFile(path.join(featurePath, 'config.ts'), configContent); // Create controllers, services, and other core files await this.createFeatureCoreFiles(featurePath, feature.name); // Track path refinements await this.trackPathRefinements(projectPath, feature.name, 'feature'); } async createFeatureCoreFiles(featurePath, featureName) { const capitalizedName = this.capitalizeFirst(featureName); // Create controller const controllerContent = this.generateFeatureController(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'controllers', `${featureName}-controller.ts`), controllerContent); // Create service const serviceContent = this.generateFeatureService(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'services', `${featureName}-service.ts`), serviceContent); // Create types const typesContent = this.generateFeatureTypes(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'types', `${featureName}-types.ts`), typesContent); // Create schemas const schemasContent = this.generateFeatureSchemas(featureName, capitalizedName); await fs.writeFile(path.join(featurePath, 'schemas', `${featureName}-schemas.ts`), schemasContent); } generateFeatureController(featureName, capitalizedName) { switch (featureName) { case 'auth': return `import { Request, Response } from 'express'; import { AuthService } from '../services/auth-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class AuthController { constructor(private authService: AuthService) {} async login(req: Request, res: Response): Promise<void> { try { const { email, password } = req.body; const result = await this.authService.login(email, password); res.json(result); } catch (error) { logger.error('Login error:', error); res.status(500).json(Result.error('Login failed')); } } async register(req: Request, res: Response): Promise<void> { try { const userData = req.body; const result = await this.authService.register(userData); res.json(result); } catch (error) { logger.error('Registration error:', error); res.status(500).json(Result.error('Registration failed')); } } async logout(req: Request, res: Response): Promise<void> { try { const token = req.headers.authorization?.replace('Bearer ', ''); const result = await this.authService.logout(token); res.json(result); } catch (error) { logger.error('Logout error:', error); res.status(500).json(Result.error('Logout failed')); } } async refreshToken(req: Request, res: Response): Promise<void> { try { const { refreshToken } = req.body; const result = await this.authService.refreshToken(refreshToken); res.json(result); } catch (error) { logger.error('Token refresh error:', error); res.status(500).json(Result.error('Token refresh failed')); } } async forgotPassword(req: Request, res: Response): Promise<void> { try { const { email } = req.body; const result = await this.authService.forgotPassword(email); res.json(result); } catch (error) { logger.error('Forgot password error:', error); res.status(500).json(Result.error('Password reset failed')); } } async resetPassword(req: Request, res: Response): Promise<void> { try { const { token, newPassword } = req.body; const result = await this.authService.resetPassword(token, newPassword); res.json(result); } catch (error) { logger.error('Reset password error:', error); res.status(500).json(Result.error('Password reset failed')); } } async verifyEmail(req: Request, res: Response): Promise<void> { try { const { token } = req.body; const result = await this.authService.verifyEmail(token); res.json(result); } catch (error) { logger.error('Email verification error:', error); res.status(500).json(Result.error('Email verification failed')); } } }`; case 'email': return `import { Request, Response } from 'express'; import { EmailService } from '../services/email-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class EmailController { constructor(private emailService: EmailService) {} async sendEmail(req: Request, res: Response): Promise<void> { try { const { to, subject, content, template } = req.body; const result = await this.emailService.sendEmail({ to, subject, content, template }); res.json(result); } catch (error) { logger.error('Send email error:', error); res.status(500).json(Result.error('Email sending failed')); } } async sendTemplateEmail(req: Request, res: Response): Promise<void> { try { const { to, templateName, data } = req.body; const result = await this.emailService.sendTemplateEmail(to, templateName, data); res.json(result); } catch (error) { logger.error('Template email error:', error); res.status(500).json(Result.error('Template email failed')); } } async getTemplates(req: Request, res: Response): Promise<void> { try { const result = await this.emailService.getTemplates(); res.json(result); } catch (error) { logger.error('Get templates error:', error); res.status(500).json(Result.error('Failed to get templates')); } } async createTemplate(req: Request, res: Response): Promise<void> { try { const templateData = req.body; const result = await this.emailService.createTemplate(templateData); res.json(result); } catch (error) { logger.error('Create template error:', error); res.status(500).json(Result.error('Template creation failed')); } } }`; default: return `import { Request, Response } from 'express'; import { ${capitalizedName}Service } from '../services/${featureName}-service'; import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; export class ${capitalizedName}Controller { constructor(private ${featureName}Service: ${capitalizedName}Service) {} async index(req: Request, res: Response): Promise<void> { try { const result = await this.${featureName}Service.getStatus(); res.json(result); } catch (error) { logger.error('${capitalizedName} index error:', error); res.status(500).json(Result.error('${capitalizedName} service error')); } } // Add your custom controller methods here async customMethod(req: Request, res: Response): Promise<void> { try { const result = await this.${featureName}Service.customMethod(req.body); res.json(result); } catch (error) { logger.error('${capitalizedName} custom method error:', error); res.status(500).json(Result.error('Custom method failed')); } } }`; } } generateFeatureService(featureName, capitalizedName) { switch (featureName) { case 'auth': return `import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; import { AuthConfig } from '../config'; export class AuthService { constructor(private config: AuthConfig) {} async login(email: string, password: string): Promise<Result> { try { // Implement login logic here logger.info('Login attempt for:', email); // Example implementation if (!email || !password) { return Result.badRequest('Email and password are required'); } // Add your authentication logic here // - Validate credentials // - Generate JWT tokens // - Return user data and tokens return Result.success({ message: 'Login successful' }); } catch (error) { logger.error('Login service error:', error); return Result.error('Login failed'); } } async register(userData: any): Promise<Result> { try { logger.info('Registration attempt for:', userData.email); // Add your registration logic here // - Validate user data // - Hash password // - Create user record // - Send verification email return Result.success({ message: 'Registration successful' }); } catch (error) { logger.error('Registration service error:', error); return Result.error('Registration failed'); } } async logout(token: string): Promise<Result> { try { // Add your logout logic here // - Invalidate token // - Clear session return Result.success({ message: 'Logout successful' }); } catch (error) { logger.error('Logout service error:', error); return Result.error('Logout failed'); } } async refreshToken(refreshToken: string): Promise<Result> { try { // Add your token refresh logic here // - Validate refresh token // - Generate new access token return Result.success({ message: 'Token refreshed' }); } catch (error) { logger.error('Token refresh service error:', error); return Result.error('Token refresh failed'); } } async forgotPassword(email: string): Promise<Result> { try { // Add your forgot password logic here // - Validate email // - Generate reset token // - Send reset email return Result.success({ message: 'Password reset email sent' }); } catch (error) { logger.error('Forgot password service error:', error); return Result.error('Password reset failed'); } } async resetPassword(token: string, newPassword: string): Promise<Result> { try { // Add your password reset logic here // - Validate token // - Update password return Result.success({ message: 'Password reset successful' }); } catch (error) { logger.error('Reset password service error:', error); return Result.error('Password reset failed'); } } async verifyEmail(token: string): Promise<Result> { try { // Add your email verification logic here // - Validate token // - Mark email as verified return Result.success({ message: 'Email verified' }); } catch (error) { logger.error('Email verification service error:', error); return Result.error('Email verification failed'); } } }`; default: return `import { Result } from '../../../shared/types/result'; import { logger } from '../../../core/logging'; import { ${capitalizedName}Config } from '../config'; export class ${capitalizedName}Service { constructor(private config: ${capitalizedName}Config) {} async getStatus(): Promise<Result> { try { return Result.success({ status: 'ok', feature: '${featureName}', enabled: this.config.enabled }); } catch (error) { logger.error('${capitalizedName} service error:', error); return Result.error('${capitalizedName} service error'); } } async customMethod(data: any): Promise<Result> { try { // Add your custom business logic here logger.info('${capitalizedName} custom method called with:', data); return Result.success({ message: 'Custom method executed' }); } catch (error) { logger.error('${capitalizedName} custom method error:', error); return Result.error('Custom method failed'); } } // Add more service methods as needed }`; } } generateFeatureTypes(featureName, capitalizedName) { return `// TypeScript types for ${featureName} feature export interface ${capitalizedName}Config { enabled: boolean; // Add your configuration interface properties here } export interface ${capitalizedName}Data { // Add your data interface properties here } export interface ${capitalizedName}Options { // Add your options interface properties here } // Add more type definitions as needed `; } generateFeatureSchemas(featureName, capitalizedName) { return `// Validation schemas for ${featureName} feature import Joi from 'joi'; export const ${featureName}ValidationSchema = Joi.object({ // Add your validation schema here // Example: // name: Joi.string().required(), // email: Joi.string().email().required(), }); export const ${featureName}UpdateSchema = Joi.object({ // Add your update validation schema here }); // Add more validation schemas as needed `; } async generateFeatureCore(featureName) { const capitalizedName = this.capitalizeFirst(featureName); switch (featureName) { case 'auth': return `import { Router } from 'express'; import { AuthController } from './controllers'; import { AuthService } from './services'; import { AuthConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: AuthController; private service: AuthService; private config: AuthConfig; constructor() { this.router = Router(); this.config = new AuthConfig(); this.service = new AuthService(); this.controller = new AuthController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Authentication routes this.router.post('/login', this.controller.login.bind(this.controller)); this.router.post('/register', this.controller.register.bind(this.controller)); this.router.post('/logout', this.controller.logout.bind(this.controller)); this.router.post('/refresh', this.controller.refreshToken.bind(this.controller)); this.router.post('/forgot-password', this.controller.forgotPassword.bind(this.controller)); this.router.post('/reset-password', this.controller.resetPassword.bind(this.controller)); this.router.post('/verify-email', this.controller.verifyEmail.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): AuthService { return this.service; } getConfig(): AuthConfig { return this.config; } }`; case 'email': return `import { Router } from 'express'; import { EmailController } from './controllers'; import { EmailService } from './services'; import { EmailConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: EmailController; private service: EmailService; private config: EmailConfig; constructor() { this.router = Router(); this.config = new EmailConfig(); this.service = new EmailService(this.config); this.controller = new EmailController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Email routes this.router.post('/send', this.controller.sendEmail.bind(this.controller)); this.router.post('/send-template', this.controller.sendTemplateEmail.bind(this.controller)); this.router.get('/templates', this.controller.getTemplates.bind(this.controller)); this.router.post('/templates', this.controller.createTemplate.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): EmailService { return this.service; } getConfig(): EmailConfig { return this.config; } }`; case 'socket': return `import { Server as SocketIOServer } from 'socket.io'; import { SocketController } from './controllers'; import { SocketService } from './services'; import { SocketConfig } from './config'; export class ${capitalizedName}Feature { private io: SocketIOServer; private controller: SocketController; private service: SocketService; private config: SocketConfig; constructor(io: SocketIOServer) { this.io = io; this.config = new SocketConfig(); this.service = new SocketService(this.io, this.config); this.controller = new SocketController(this.service); this.setupEvents(); } private setupEvents(): void { this.io.on('connection', (socket) => { this.controller.handleConnection(socket); // Register event handlers socket.on('join-room', this.controller.handleJoinRoom.bind(this.controller, socket)); socket.on('leave-room', this.controller.handleLeaveRoom.bind(this.controller, socket)); socket.on('send-message', this.controller.handleSendMessage.bind(this.controller, socket)); socket.on('disconnect', this.controller.handleDisconnect.bind(this.controller, socket)); }); } getService(): SocketService { return this.service; } getConfig(): SocketConfig { return this.config; } }`; case 'cron': return `import { CronController } from './controllers'; import { CronService } from './services'; import { CronConfig } from './config'; export class ${capitalizedName}Feature { private controller: CronController; private service: CronService; private config: CronConfig; constructor() { this.config = new CronConfig(); this.service = new CronService(this.config); this.controller = new CronController(this.service); this.initializeJobs(); } private initializeJobs(): void { // Initialize scheduled jobs this.service.initializeJobs(); } getService(): CronService { return this.service; } getConfig(): CronConfig { return this.config; } // Method to manually trigger jobs async triggerJob(jobName: string): Promise<void> { await this.service.triggerJob(jobName); } }`; case 'audit': return `import { Router } from 'express'; import { AuditController } from './controllers'; import { AuditService } from './services'; import { AuditConfig } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: AuditController; private service: AuditService; private config: AuditConfig; constructor() { this.router = Router(); this.config = new AuditConfig(); this.service = new AuditService(this.config); this.controller = new AuditController(this.service); this.setupRoutes(); } private setupRoutes(): void { // Audit routes this.router.get('/logs', this.controller.getLogs.bind(this.controller)); this.router.post('/logs', this.controller.createLog.bind(this.controller)); this.router.get('/logs/:id', this.controller.getLogById.bind(this.controller)); this.router.delete('/logs/:id', this.controller.deleteLog.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): AuditService { return this.service; } getConfig(): AuditConfig { return this.config; } // Method to log audit events async logEvent(event: any): Promise<void> { await this.service.logEvent(event); } }`; default: return `import { Router } from 'express'; import { ${capitalizedName}Controller } from './controllers'; import { ${capitalizedName}Service } from './services'; import { ${capitalizedName}Config } from './config'; export class ${capitalizedName}Feature { private router: Router; private controller: ${capitalizedName}Controller; private service: ${capitalizedName}Service; private config: ${capitalizedName}Config; constructor() { this.router = Router(); this.config = new ${capitalizedName}Config(); this.service = new ${capitalizedName}Service(this.config); this.controller = new ${capitalizedName}Controller(this.service); this.setupRoutes(); } private setupRoutes(): void { // Add your feature routes here this.router.get('/', this.controller.index.bind(this.controller)); } getRouter(): Router { return this.router; } getService(): ${capitalizedName}Service { return this.service; } getConfig(): ${capitalizedName}Config { return this.config; } }`; } } generateFeatureConfig(featureName) { const capitalizedName = this.capitalizeFirst(featureName); return `export class ${capitalizedName}Config { constructor() { // Initialize configuration from environment variables this.loadFromEnv(); } private loadFromEnv(): void { // Load configuration from environment variables // Override this method to add specific configuration loading } // Add getter methods for configuration values get enabled(): boolean { return process.env.${featureName.toUpperCase()}_ENABLED === 'true'; } }`; } async trackPathRefinements(projectPath, featureName, type) { const refinementsPath = path.join(projectPath, '.diagramers-refinements.json'); let refinements = {}; if (await fs.pathExists(refinementsPath)) { refinements = JSON.parse(await fs.readFile(refinementsPath, 'utf8')); } if (!refinements.paths) { refinements.paths = []; } refinements.paths.push({ type: type, name: featureName, path: `src/features/${featureName}`, addedAt: new Date().toISOString(), version: '1.0.0' }); await fs.writeFile(refinementsPath, JSON.stringify(refinements, null, 2)); } async createModuleStructure(projectPath, moduleName, options) { const modulePath = path.join(projectPath, 'src', 'modules', moduleName); // Convert module name to valid TypeScript identifiers const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase()); const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1); // Create module directory structure const structure = { 'entities': 'Data models and interfaces', 'schemas': 'Database schemas and validation', 'services': 'Business logic and data access', 'controllers': 'HTTP request handlers', 'routes': 'API route definitions' }; for (const [dir, description] of Object.entries(structure)) { const dirPath = path.join(modulePath, dir); await fs.ensureDir(dirPath); // Create index file with correct file extensions - use camelCase for file names let fileName = ''; switch (dir) { case 'entities': fileName = `${moduleNameCamelCase}.entity`; break; case 'schemas': fileName = `${moduleNameCamelCase}.schema`; break; case 'services': fileName = `${moduleNameCamelCase}.service`; break; case 'controllers': fileName = `${moduleNameCamelCase}.controller`; break; case 'routes': fileName = `${moduleNameCamelCase}.routes`; break; default: fileName = `${moduleNameCamelCase}.${dir.slice(0, -1)}`; } const indexContent = `// ${description} for ${moduleName} module export * from './${fileName}'; `; await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent); } // Create main entity await this.createEntity(modulePath, moduleNameCamelCase, options); // Create schema await this.createSchema(modulePath, moduleNameCamelCase, options); // Create service await this.createService(modulePath, moduleNameCamelCase, options); // Create controller await this.createController(modulePath, moduleNameCamelCase, options); // Create routes await this.createRoutes(modulePath, moduleNameCamelCase, options); } async createEntity(modulePath, moduleNameCamelCase, options) { // Convert module name to valid TypeScript identifiers const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1); const entityContent = `import { BaseEntity } from '../../../shared/types/base-entity'; export interface ${moduleNameCapitalized} extends BaseEntity { _id?: string; name: string; description?: string; } export interface Create${moduleNameCapitalized}Data { name: string; description?: string; } export interface Update${moduleNameCapitalized}Data { name?: string; description?: string; }`; await fs.writeFile(path.join(modulePath, 'entities', `${moduleNameCamelCase}.entity.ts`), entityContent); } async createSchema(modulePath, moduleNameCamelCase, options) { // Convert module name to valid TypeScript identifiers const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1); const schemaContent = `import mongoose, { Document, Schema } from 'mongoose'; import { ${moduleNameCapitalized} } from '../entities/${moduleNameCamelCase}.entity'; export interface I${moduleNameCapitalized}Document extends Omit<${moduleNameCapitalized}, '_id'>, Document {} const ${moduleNameCapitalized}Schema = new Schema<I${moduleNameCapitalized}Document>({ name: { type: String, required: true }, description: { type: String }, status: { type: Number, default: 1 }, createdAt: { type: Date,