# 💻 Практические примеры реализации оптимизаций ## ⚠️ Status: Partial Implementation The following optimizations have been **moved to actual code implementation** via the Claude Code prompt and removed from this file: - ✅ Graceful Error Handling → `CLAUDE_CODE_PROMPT.md` Phase 1 - ✅ Performance Monitoring → `CLAUDE_CODE_PROMPT.md` Phase 4 This document now focuses on **remaining optimization examples** that are still in design/planning phase and waiting for implementation. --- ## 1. Incremental Generation с Caching ### 1.1 Система кэширования (cache-manager.ts) ```typescript // tools/cache-manager.ts import * as crypto from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; interface CacheEntry { hash: string; timestamp: number; value: any; } export class GenerationCache { private cacheDir = './.generation-cache'; constructor() { if (!fs.existsSync(this.cacheDir)) { fs.mkdirSync(this.cacheDir, { recursive: true }); } } /** * Вычислить хеш входных данных */ computeHash(input: any): string { const content = JSON.stringify(input, Object.keys(input).sort()); return crypto.createHash('sha256').update(content).digest('hex'); } /** * Получить значение из кэша */ get(key: string, input: any): T | null { const hash = this.computeHash(input); const cachePath = path.join(this.cacheDir, `${key}.json`); if (!fs.existsSync(cachePath)) { return null; } try { const cached: CacheEntry = JSON.parse(fs.readFileSync(cachePath, 'utf-8')); if (cached.hash === hash) { console.log(`✅ Cache hit for ${key}`); return cached.value; } } catch (e) { // Cache file corrupted, regenerate } return null; } /** * Сохранить значение в кэш */ set(key: string, input: any, value: any): void { const hash = this.computeHash(input); const cachePath = path.join(this.cacheDir, `${key}.json`); const entry: CacheEntry = { hash, timestamp: Date.now(), value, }; fs.writeFileSync(cachePath, JSON.stringify(entry, null, 2)); } /** * Очистить старые кэши (>7 дней) */ cleanup(maxAgeDays = 7): void { const maxAge = maxAgeDays * 24 * 60 * 60 * 1000; const now = Date.now(); fs.readdirSync(this.cacheDir).forEach(file => { const cachePath = path.join(this.cacheDir, file); const cached: CacheEntry = JSON.parse(fs.readFileSync(cachePath, 'utf-8')); if (now - cached.timestamp > maxAge) { fs.unlinkSync(cachePath); console.log(`🗑️ Deleted old cache: ${file}`); } }); } } export const cache = new GenerationCache(); ``` ### 1.2 Использование в orchestrator.ts ```typescript // tools/orchestrator.ts (updated) import { cache } from './cache-manager'; async function orchestrate(task: string): Promise { const dslContent = readDSL('domain/toir.api.dsl'); // Проверить кэш const cachedContract = cache.get('frozen-contract', dslContent); let contract = cachedContract || freezeContract(dslContent); if (!cachedContract) { cache.set('frozen-contract', dslContent, contract); } // Для Prisma: проверить кэш перед генерацией const cachedPrismaSchema = cache.get('prisma-schema', contract); let prismaOutput; if (cachedPrismaSchema) { prismaOutput = cachedPrismaSchema; } else { prismaOutput = await agent('generator_prisma', { contract, task: 'Generate Prisma schema' }); cache.set('prisma-schema', contract, prismaOutput); } // ... аналогично для других генераторов } ``` --- ## 2. Параллельная генерация ### 2.1 Параллельный orchestrator ```typescript // tools/orchestrator-parallel.ts async function orchestrateParallel(task: string): Promise { const contract = freezeContract(readDSL('domain/toir.api.dsl')); // Независимые генерации - запустить в параллель const [prismaOutput, nestOutput, reactOutput] = await Promise.all([ // Prisma не зависит ни от чего agent('generator_prisma', { contract, task: 'Generate Prisma schema' }), // NestJS зависит от Prisma (но не блокирующая зависимость) agent('generator_nest_resources', { contract, // Может получить Prisma результаты позже task: 'Generate NestJS resources' }), // React независим от NestJS на этапе генерации ресурсов agent('generator_react_admin_resources', { contract, task: 'Generate React Admin resources' }) ]); // ПОТОМ: когда все завершены, интегрировать и валидировать const dataAccessOutput = await agent('generator_data_access', { contract, nestModules: nestOutput, task: 'Integrate data access' }); // Финальная валидация const reviewed = await agent('reviewer', { outputs: { prisma: prismaOutput, nest: nestOutput, react: reactOutput, dataAccess: dataAccessOutput }, task: 'Validate all outputs' }); return reviewed; } ``` --- ## 3. Plugin System ### 3.1 Plugin Interface ```typescript // types/generator-plugin.ts export interface GeneratorHooks { /** * Вызывается перед началом всей генерации */ beforeGeneration?: (contract: FrozenContract) => Promise; /** * После фриза контракта, но перед первой генерацией */ afterContractFreeze?: (contract: FrozenContract) => Promise; /** * После генерации Prisma schema */ afterPrismaGeneration?: (schema: string) => Promise; /** * После генерации NestJS модулей */ afterNestResourcesGeneration?: (modules: GeneratedModule[]) => Promise; /** * После генерации React ресурсов */ afterReactResourcesGeneration?: (resources: GeneratedResource[]) => Promise; /** * Финальная интеграция перед валидацией */ beforeReview?: (outputs: AllOutputs) => Promise; /** * На ошибку в любом агенте */ onGenerationError?: (error: Error, stage: string) => Promise; } export interface GeneratorPlugin { name: string; version: string; description: string; hooks: GeneratorHooks; } ``` ### 3.2 Plugin Manager ```typescript // tools/plugin-manager.ts export class PluginManager { private plugins: GeneratorPlugin[] = []; loadPlugins(pluginDir: string): void { const files = fs.readdirSync(pluginDir); files.forEach(file => { if (file.endsWith('.plugin.ts') || file.endsWith('.plugin.js')) { const pluginPath = path.join(pluginDir, file); const plugin = require(pluginPath).default as GeneratorPlugin; this.plugins.push(plugin); console.log(`📦 Loaded plugin: ${plugin.name} v${plugin.version}`); } }); } async executeHook( hookName: keyof GeneratorHooks, input: any, context: { stage?: string; error?: Error } ): Promise { let result = input; for (const plugin of this.plugins) { const hook = plugin.hooks[hookName]; if (hook) { try { result = await hook(result); console.log(`✅ ${plugin.name}.${hookName}`); } catch (e) { console.error(`❌ ${plugin.name}.${hookName} failed:`, e); if (plugin.hooks.onGenerationError) { await plugin.hooks.onGenerationError(e as Error, context.stage || 'unknown'); } } } } return result; } } export const pluginManager = new PluginManager(); ``` ### 3.3 Пример plugin'а ```typescript // plugins/oauth2-plugin.plugin.ts import { GeneratorPlugin } from '../types/generator-plugin'; export default { name: 'oauth2-auth', version: '1.0.0', description: 'Add OAuth2 support to generated schema and auth', hooks: { afterPrismaGeneration: async (schema: string) => { // Добавить OAuth2 таблицы const oauthTables = ` model OAuth2Provider { id String @id @default(cuid()) name String @unique clientId String clientSecret String createdAt DateTime @default(now()) } model OAuth2Token { id String @id @default(cuid()) userId String providerId String accessToken String refreshToken String? expiresAt DateTime user User @relation(fields: [userId], references: [id]) } `; return schema + '\n' + oauthTables; }, afterNestResourcesGeneration: async (modules) => { // Добавить OAuth2 контроллер const oauth2Module = { name: 'oauth2', files: { 'oauth2.controller.ts': ` import { Controller, Get, Query } from '@nestjs/common'; @Controller('auth/oauth2') export class OAuth2Controller { @Get('callback') async handleCallback(@Query('code') code: string) { // OAuth2 callback logic } } ` } }; return [...modules, oauth2Module]; } } } as GeneratorPlugin; ``` --- ## 4. OpenAPI as Intermediate Representation ### 4.1 DSL → OpenAPI Generator ```typescript // tools/dsl-to-openapi.ts import { FrozenContract } from './types'; import { OpenAPI, Info, Paths } from 'openapi3-ts/oas30'; export function dslToOpenAPI(contract: FrozenContract): OpenAPI { const openapi: OpenAPI = { openapi: '3.1.0', info: { title: contract.appName || 'Generated API', version: contract.version || '1.0.0', description: contract.description || '' } as Info, paths: {} as Paths, components: { schemas: {} } }; // Для каждого entity - генерировать endpoints contract.entities.forEach(entity => { const basePath = `/${entity.plural || entity.name}`; // GET /entities openapi.paths![`${basePath}`] = { get: { operationId: `list${entity.name}`, tags: [entity.name], responses: { '200': { description: `List of ${entity.plural}`, content: { 'application/json': { schema: { type: 'array', items: { $ref: `#/components/schemas/${entity.name}` } } } } } } }, post: { operationId: `create${entity.name}`, tags: [entity.name], requestBody: { content: { 'application/json': { schema: { $ref: `#/components/schemas/${entity.name}CreateDTO` } } } }, responses: { '201': { description: `Created ${entity.name}`, content: { 'application/json': { schema: { $ref: `#/components/schemas/${entity.name}` } } } } } } }; // GET /entities/{id} openapi.paths![`${basePath}/{id}`] = { get: { operationId: `get${entity.name}`, parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'string' } } ], tags: [entity.name], responses: { '200': { description: `${entity.name} details`, content: { 'application/json': { schema: { $ref: `#/components/schemas/${entity.name}` } } } } } }, put: { operationId: `update${entity.name}`, parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'string' } } ], tags: [entity.name], requestBody: { content: { 'application/json': { schema: { $ref: `#/components/schemas/${entity.name}UpdateDTO` } } } }, responses: { '200': { description: `Updated ${entity.name}`, content: { 'application/json': { schema: { $ref: `#/components/schemas/${entity.name}` } } } } } }, delete: { operationId: `delete${entity.name}`, parameters: [ { name: 'id', in: 'path', required: true, schema: { type: 'string' } } ], tags: [entity.name], responses: { '204': { description: 'Deleted successfully' } } } }; // Добавить schema для entity openapi.components!.schemas![entity.name] = { type: 'object', properties: Object.fromEntries( entity.fields.map(field => [ field.name, { type: mapFieldTypeToOpenAPI(field.type) } ]) ), required: entity.fields.filter(f => f.required).map(f => f.name) }; }); return openapi; } function mapFieldTypeToOpenAPI(fieldType: string): string { const mapping: Record = { 'string': 'string', 'int': 'integer', 'float': 'number', 'boolean': 'boolean', 'date': 'string', // format: date 'datetime': 'string', // format: date-time }; return mapping[fieldType] || 'string'; } ``` ### 4.2 Usage в package.json ```json { "scripts": { "generate:api-summary": "ts-node tools/api-summary-generator.ts", "generate:openapi": "ts-node tools/dsl-to-openapi.ts", "generate:docs": "npm run generate:openapi && swagger-cli bundle openapi.json --outfile docs/api.html", "generate:client-sdk": "openapi-generator-cli generate -i openapi.json -g typescript-fetch -o client/src/generated" } } ``` --- ## 5. Type Safety с Zod ### 5.1 DSL Validation Schema ```typescript // tools/validation/dsl-schema.ts import { z } from 'zod'; export const FieldSchema = z.object({ name: z.string().min(1), type: z.enum(['string', 'int', 'float', 'boolean', 'date', 'datetime']), required: z.boolean().default(true), unique: z.boolean().default(false), indexed: z.boolean().default(false), }); export const EntitySchema = z.object({ name: z.string().min(1), plural: z.string().optional(), description: z.string().optional(), fields: z.array(FieldSchema).min(1), timestamps: z.boolean().default(true), }); export const RelationshipSchema = z.object({ from: z.string(), to: z.string(), type: z.enum(['one-to-one', 'one-to-many', 'many-to-many']), cascade: z.boolean().default(false), }); export const DSLSchema = z.object({ appName: z.string().min(1), version: z.string().default('1.0.0'), entities: z.array(EntitySchema), relationships: z.array(RelationshipSchema).optional(), auth: z.object({ enabled: z.boolean().default(true), providers: z.array(z.string()).optional(), }).optional(), }); export type DSL = z.infer; export type Entity = z.infer; export type Field = z.infer; ``` ### 5.2 Validation в orchestrator ```typescript // tools/orchestrator.ts (with validation) import { DSLSchema, DSL } from './validation/dsl-schema'; async function orchestrate(task: string): Promise { const rawDSL = readDSL('domain/toir.api.dsl'); // Валидировать перед началом let validatedDSL: DSL; try { validatedDSL = DSLSchema.parse(rawDSL); console.log('✅ DSL validation passed'); } catch (error) { console.error('❌ DSL validation failed:'); console.error(error.issues); process.exit(1); } // Продолжить с validated data const contract = freezeContract(validatedDSL); // ... } ``` --- > **Removed:** Sections 6 (Nx Caching Configuration) and 8 (placeholder) have > been removed. Nx caching is tracked as a future recommendation, not a worked > example. Graceful Error Handling and Performance Monitoring are now fully > implemented — see `tools/orchestrator.ts` (`generate()`, `GenerationOutput`, > `runTask()`) and `tools/performance-monitor.ts` (`PerformanceMonitor` class) > for the production code paths instead of illustrative snippets. --- ## Заключение Эти примеры показывают как: 1. **Кэшировать** результаты генерации 2. **Параллелизировать** независимые операции 3. **Расширять** систему через плагины 4. **Генерировать** OpenAPI для документации и SDK 5. **Валидировать** входные данные Реализованные оптимизации (уже в репозитории): - **Graceful error handling + parallel generation** → `tools/orchestrator.ts` (`generate()`, `Promise.allSettled`, per-stage try/catch, discriminated-union `GenerationStatus`). - **Performance monitoring** → `tools/performance-monitor.ts` (`PerformanceMonitor.mark/measure/summary/export`), отчёт в `generation-metrics.json`. - **MCP-валидаторы** → `tools/mcp/{prisma,npm,nest}-validator.mjs` + `tools/mcp/lib/mcp-server.mjs` (минимальный JSON-RPC stdio сервер без внешних зависимостей).