UNPKG

cs-element

Version:

Advanced reactive data management library with state machines, blueprints, persistence, compression, networking, and multithreading support

931 lines (797 loc) 27.8 kB
# Расширенная система типизированных элементов Система типизированных элементов CSElement предоставляет мощную типизацию на основе схем с поддержкой наследования, валидации во время выполнения и полной интеграции с TypeScript. ## 🎯 Основные возможности ### Схемы и типизация - **JSON Schema 7** - полная поддержка стандарта - **Runtime валидация** - проверка типов во время выполнения - **TypeScript интеграция** - автогенерация типов - **Наследование схем** - множественное наследование ### Валидация и ограничения - **Поле-уровневая валидация** - индивидуальные правила для полей - **Кросс-поле валидация** - проверка связей между полями - **Асинхронная валидация** - поддержка внешних проверок - **Пользовательские валидаторы** - расширяемая система ### Вычисляемые поля - **Computed properties** - автоматически вычисляемые значения - **Зависимости** - отслеживание изменений связанных полей - **Кэширование** - оптимизация производительности - **Асинхронные вычисления** - поддержка Promise ## 🚀 Быстрый старт ### Базовая настройка ```typescript import { TypedElementManager, Schema } from 'cs-element'; // Создание менеджера типизированных элементов const manager = new TypedElementManager({ strictMode: true, enableValidation: true, enableInheritance: true, cacheSchemas: true }); // Определение базовой схемы const userSchema: Schema = { $id: 'User', type: 'object', properties: { id: { type: 'string', format: 'uuid' }, name: { type: 'string', minLength: 1, maxLength: 100 }, email: { type: 'string', format: 'email' }, age: { type: 'integer', minimum: 0, maximum: 150 }, isActive: { type: 'boolean', default: true } }, required: ['id', 'name', 'email'], additionalProperties: false }; // Регистрация схемы manager.registerSchema(userSchema); ``` ### Создание типизированных элементов ```typescript // Создание элемента с валидацией const user = await manager.createElement('User', { id: '123e4567-e89b-12d3-a456-426614174000', name: 'John Doe', email: 'john@example.com', age: 30 }); // Типобезопасный доступ к полям const userName: string = user.getField('name'); const userAge: number = user.getField('age'); const isActive: boolean = user.getField('isActive'); // Использует default значение // Обновление с валидацией await user.setField('age', 31); // ✅ Валидно // await user.setField('age', -5); // ❌ Ошибка валидации // await user.setField('name', ''); // ❌ Ошибка: слишком короткое имя console.log('Пользователь создан:', { id: user.id, data: user.getData(), isValid: user.isValid() }); ``` ### Наследование схем ```typescript // Базовая схема const entitySchema: Schema = { $id: 'Entity', type: 'object', properties: { id: { type: 'string', format: 'uuid' }, createdAt: { type: 'string', format: 'date-time' }, updatedAt: { type: 'string', format: 'date-time' } }, required: ['id'] }; // Схема с наследованием const productSchema: Schema = { $id: 'Product', extends: ['Entity'], // Наследование от Entity type: 'object', properties: { name: { type: 'string', minLength: 1 }, price: { type: 'number', minimum: 0 }, category: { type: 'string' }, inStock: { type: 'boolean', default: true } }, required: ['name', 'price'] }; manager.registerSchema(entitySchema); manager.registerSchema(productSchema); // Создание продукта с унаследованными полями const product = await manager.createElement('Product', { id: crypto.randomUUID(), createdAt: new Date().toISOString(), name: 'Laptop', price: 999.99, category: 'Electronics' }); // Доступ к унаследованным полям console.log('Продукт:', { id: product.getField('id'), // Из Entity name: product.getField('name'), // Из Product createdAt: product.getField('createdAt') // Из Entity }); ``` ## 📚 Подробное руководство ### Продвинутые схемы #### Вложенные объекты и массивы ```typescript const orderSchema: Schema = { $id: 'Order', type: 'object', properties: { id: { type: 'string' }, customer: { type: 'object', properties: { name: { type: 'string' }, email: { type: 'string', format: 'email' } }, required: ['name', 'email'] }, items: { type: 'array', items: { type: 'object', properties: { productId: { type: 'string' }, quantity: { type: 'integer', minimum: 1 }, price: { type: 'number', minimum: 0 } }, required: ['productId', 'quantity', 'price'] }, minItems: 1 }, total: { type: 'number', minimum: 0 }, status: { type: 'string', enum: ['pending', 'confirmed', 'shipped', 'delivered', 'cancelled'] } }, required: ['id', 'customer', 'items', 'total', 'status'] }; manager.registerSchema(orderSchema); const order = await manager.createElement('Order', { id: 'ORD-001', customer: { name: 'Alice Johnson', email: 'alice@example.com' }, items: [ { productId: 'PROD-1', quantity: 2, price: 29.99 }, { productId: 'PROD-2', quantity: 1, price: 15.50 } ], total: 75.48, status: 'pending' }); // Работа с вложенными данными const customerName = order.getField('customer.name'); const firstItemQuantity = order.getField('items.0.quantity'); ``` #### Условная валидация ```typescript const conditionalSchema: Schema = { $id: 'ConditionalUser', type: 'object', properties: { type: { type: 'string', enum: ['individual', 'business'] }, name: { type: 'string' }, companyName: { type: 'string' }, taxId: { type: 'string' } }, required: ['type', 'name'], // Условная валидация if: { properties: { type: { const: 'business' } } }, then: { required: ['companyName', 'taxId'] }, else: { not: { anyOf: [{ required: ['companyName'] }, { required: ['taxId'] }] } } }; manager.registerSchema(conditionalSchema); // Бизнес пользователь - требует companyName и taxId const businessUser = await manager.createElement('ConditionalUser', { type: 'business', name: 'John Smith', companyName: 'Acme Corp', taxId: '123456789' }); // Индивидуальный пользователь - не требует бизнес поля const individualUser = await manager.createElement('ConditionalUser', { type: 'individual', name: 'Jane Doe' }); ``` ### Валидация и ограничения #### Пользовательские валидаторы ```typescript // Регистрация пользовательских валидаторов manager.registerValidator('strongPassword', { validate: (value: string) => { const hasUpper = /[A-Z]/.test(value); const hasLower = /[a-z]/.test(value); const hasNumber = /\d/.test(value); const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(value); const isLongEnough = value.length >= 8; return hasUpper && hasLower && hasNumber && hasSpecial && isLongEnough; }, message: 'Пароль должен содержать заглавные и строчные буквы, цифры, спецсимволы и быть не менее 8 символов' }); manager.registerValidator('uniqueEmail', { validate: async (value: string, context) => { // Асинхронная проверка уникальности const existingUser = await database.findUserByEmail(value); return !existingUser || existingUser.id === context.elementId; }, message: 'Email уже используется другим пользователем', async: true }); // Схема с пользовательскими валидаторами const secureUserSchema: Schema = { $id: 'SecureUser', type: 'object', properties: { email: { type: 'string', format: 'email', 'x-validator': 'uniqueEmail' // Пользовательский валидатор }, password: { type: 'string', 'x-validator': 'strongPassword' } }, required: ['email', 'password'] }; ``` #### Кросс-поле валидация ```typescript const eventSchema: Schema = { $id: 'Event', type: 'object', properties: { startDate: { type: 'string', format: 'date-time' }, endDate: { type: 'string', format: 'date-time' }, title: { type: 'string' }, maxParticipants: { type: 'integer', minimum: 1 }, currentParticipants: { type: 'integer', minimum: 0 } }, required: ['startDate', 'endDate', 'title'], // Кросс-поле валидация 'x-validators': [ { name: 'dateRange', validate: (data) => new Date(data.endDate) > new Date(data.startDate), message: 'Дата окончания должна быть позже даты начала' }, { name: 'participantLimit', validate: (data) => !data.currentParticipants || data.currentParticipants <= data.maxParticipants, message: 'Количество участников не может превышать максимум' } ] }; manager.registerSchema(eventSchema); // Создание события с валидацией const event = await manager.createElement('Event', { startDate: '2024-06-01T10:00:00Z', endDate: '2024-06-01T18:00:00Z', title: 'Конференция разработчиков', maxParticipants: 100, currentParticipants: 85 }); ``` ### Вычисляемые поля #### Простые вычисляемые поля ```typescript const invoiceSchema: Schema = { $id: 'Invoice', type: 'object', properties: { items: { type: 'array', items: { type: 'object', properties: { quantity: { type: 'number' }, price: { type: 'number' } } } }, taxRate: { type: 'number', default: 0.1 }, // Вычисляемые поля subtotal: { type: 'number', 'x-computed': { dependencies: ['items'], compute: (data) => { return data.items.reduce((sum, item) => sum + (item.quantity * item.price), 0); } } }, tax: { type: 'number', 'x-computed': { dependencies: ['subtotal', 'taxRate'], compute: (data) => data.subtotal * data.taxRate } }, total: { type: 'number', 'x-computed': { dependencies: ['subtotal', 'tax'], compute: (data) => data.subtotal + data.tax } } }, required: ['items'] }; manager.registerSchema(invoiceSchema); const invoice = await manager.createElement('Invoice', { items: [ { quantity: 2, price: 50 }, { quantity: 1, price: 30 } ], taxRate: 0.15 }); // Вычисляемые поля обновляются автоматически console.log('Счет:', { subtotal: invoice.getField('subtotal'), // 130 tax: invoice.getField('tax'), // 19.5 total: invoice.getField('total') // 149.5 }); // При изменении items вычисляемые поля пересчитываются await invoice.setField('items', [ { quantity: 3, price: 50 }, { quantity: 2, price: 30 } ]); console.log('Обновленный счет:', { subtotal: invoice.getField('subtotal'), // 210 total: invoice.getField('total') // 241.5 }); ``` #### Асинхронные вычисляемые поля ```typescript const enrichedUserSchema: Schema = { $id: 'EnrichedUser', type: 'object', properties: { userId: { type: 'string' }, email: { type: 'string', format: 'email' }, // Асинхронное вычисляемое поле profile: { type: 'object', 'x-computed': { dependencies: ['userId'], async: true, compute: async (data) => { // Загрузка профиля из внешнего API const response = await fetch(`/api/users/${data.userId}/profile`); return response.json(); }, cache: true, ttl: 300000 // Кэш на 5 минут } }, permissions: { type: 'array', 'x-computed': { dependencies: ['userId'], async: true, compute: async (data) => { const response = await fetch(`/api/users/${data.userId}/permissions`); return response.json(); } } } }, required: ['userId', 'email'] }; manager.registerSchema(enrichedUserSchema); const enrichedUser = await manager.createElement('EnrichedUser', { userId: 'user123', email: 'user@example.com' }); // Асинхронное получение вычисляемых полей const profile = await enrichedUser.getFieldAsync('profile'); const permissions = await enrichedUser.getFieldAsync('permissions'); console.log('Обогащенный пользователь:', { email: enrichedUser.getField('email'), profile, permissions }); ``` ## ⚙️ Конфигурация и оптимизация ### Настройка менеджера ```typescript const manager = new TypedElementManager({ // Режим строгой типизации strictMode: true, // Валидация enableValidation: true, validateOnCreate: true, validateOnUpdate: true, // Наследование enableInheritance: true, maxInheritanceDepth: 10, // Производительность cacheSchemas: true, cacheComputedFields: true, lazyComputeFields: true, // Вычисляемые поля computedFieldTimeout: 5000, enableAsyncComputed: true, // События enableEvents: true, // Отладка debug: false, logValidationErrors: true }); ``` ### Кэширование и производительность ```typescript // Настройка кэширования для схем manager.configureCaching({ schemaCache: { enabled: true, maxSize: 1000, ttl: 3600000 // 1 час }, computedFieldCache: { enabled: true, maxSize: 5000, ttl: 300000 // 5 минут }, validationCache: { enabled: true, maxSize: 2000, ttl: 60000 // 1 минута } }); // Предварительная компиляция схем await manager.precompileSchemas(['User', 'Product', 'Order']); // Оптимизация для массовых операций manager.enableBatchMode(true); const users = await Promise.all([ manager.createElement('User', userData1), manager.createElement('User', userData2), manager.createElement('User', userData3) ]); manager.enableBatchMode(false); ``` ## 🔍 Продвинутые возможности ### Миграции схем ```typescript // Версионирование схем const userSchemaV1: Schema = { $id: 'User', version: '1.0.0', type: 'object', properties: { name: { type: 'string' }, email: { type: 'string' } } }; const userSchemaV2: Schema = { $id: 'User', version: '2.0.0', type: 'object', properties: { firstName: { type: 'string' }, lastName: { type: 'string' }, email: { type: 'string' }, phone: { type: 'string' } }, // Миграция с предыдущей версии 'x-migration': { from: '1.0.0', migrate: (oldData) => ({ firstName: oldData.name.split(' ')[0] || '', lastName: oldData.name.split(' ')[1] || '', email: oldData.email, phone: '' }) } }; // Регистрация схем с версиями manager.registerSchema(userSchemaV1); manager.registerSchema(userSchemaV2); // Автоматическая миграция при загрузке старых данных const migratedUser = await manager.loadElement({ schemaId: 'User', version: '1.0.0', data: { name: 'John Doe', email: 'john@example.com' } }); console.log('Мигрированный пользователь:', migratedUser.getData()); // { firstName: 'John', lastName: 'Doe', email: 'john@example.com', phone: '' } ``` ### Полиморфизм ```typescript // Базовая схема для полиморфизма const shapeSchema: Schema = { $id: 'Shape', type: 'object', discriminator: { propertyName: 'type' }, oneOf: [ { $ref: '#/definitions/Circle' }, { $ref: '#/definitions/Rectangle' }, { $ref: '#/definitions/Triangle' } ], definitions: { Circle: { type: 'object', properties: { type: { const: 'circle' }, radius: { type: 'number', minimum: 0 } }, required: ['type', 'radius'] }, Rectangle: { type: 'object', properties: { type: { const: 'rectangle' }, width: { type: 'number', minimum: 0 }, height: { type: 'number', minimum: 0 } }, required: ['type', 'width', 'height'] }, Triangle: { type: 'object', properties: { type: { const: 'triangle' }, base: { type: 'number', minimum: 0 }, height: { type: 'number', minimum: 0 } }, required: ['type', 'base', 'height'] } } }; manager.registerSchema(shapeSchema); // Создание полиморфных объектов const circle = await manager.createElement('Shape', { type: 'circle', radius: 5 }); const rectangle = await manager.createElement('Shape', { type: 'rectangle', width: 10, height: 8 }); // Типобезопасная работа с полиморфными объектами function calculateArea(shape: TypedElement): number { const shapeType = shape.getField('type'); switch (shapeType) { case 'circle': const radius = shape.getField('radius'); return Math.PI * radius * radius; case 'rectangle': const width = shape.getField('width'); const height = shape.getField('height'); return width * height; case 'triangle': const base = shape.getField('base'); const triangleHeight = shape.getField('height'); return 0.5 * base * triangleHeight; default: throw new Error(`Неизвестный тип фигуры: ${shapeType}`); } } console.log('Площади:', { circle: calculateArea(circle), rectangle: calculateArea(rectangle) }); ``` ### TypeScript интеграция ```typescript // Автогенерация TypeScript типов import { TypeScriptGenerator } from 'cs-element'; const generator = new TypeScriptGenerator(); // Генерация типов из схем const typeDefinitions = generator.generateTypes([ userSchema, productSchema, orderSchema ]); console.log('Сгенерированные типы:'); console.log(typeDefinitions.code); // Сохранение в файл import fs from 'fs'; fs.writeFileSync('generated-types.d.ts', typeDefinitions.code); // Использование сгенерированных типов interface GeneratedUser { id: string; name: string; email: string; age?: number; isActive?: boolean; } // Типобезопасное создание элементов const typedUser = await manager.createElement<GeneratedUser>('User', { id: crypto.randomUUID(), name: 'Alice', email: 'alice@example.com' }); // TypeScript знает типы полей const userName: string = typedUser.getField('name'); const userAge: number | undefined = typedUser.getField('age'); ``` ## 🔧 Интеграция с другими системами ### React интеграция ```typescript import React, { useState, useEffect } from 'react'; import { useTypedElement } from 'cs-element/react'; interface UserFormProps { userId?: string; } function UserForm({ userId }: UserFormProps) { const [user, setUser] = useTypedElement('User', userId); const [errors, setErrors] = useState<Record<string, string>>({}); const handleFieldChange = async (fieldName: string, value: any) => { try { await user.setField(fieldName, value); setErrors(prev => ({ ...prev, [fieldName]: '' })); } catch (error) { setErrors(prev => ({ ...prev, [fieldName]: error.message })); } }; const handleSubmit = async () => { try { await user.validate(); await user.save(); console.log('Пользователь сохранен'); } catch (error) { console.error('Ошибка сохранения:', error); } }; if (!user) return <div>Загрузка...</div>; return ( <form onSubmit={handleSubmit}> <div> <label>Имя:</label> <input value={user.getField('name') || ''} onChange={(e) => handleFieldChange('name', e.target.value)} /> {errors.name && <span className="error">{errors.name}</span>} </div> <div> <label>Email:</label> <input type="email" value={user.getField('email') || ''} onChange={(e) => handleFieldChange('email', e.target.value)} /> {errors.email && <span className="error">{errors.email}</span>} </div> <div> <label>Возраст:</label> <input type="number" value={user.getField('age') || ''} onChange={(e) => handleFieldChange('age', parseInt(e.target.value))} /> {errors.age && <span className="error">{errors.age}</span>} </div> <button type="submit" disabled={!user.isValid()}> Сохранить </button> </form> ); } ``` ### API интеграция ```typescript // REST API для типизированных элементов class TypedElementAPI { constructor(private manager: TypedElementManager) {} async createElement(req, res) { try { const { schemaId, data } = req.body; const element = await this.manager.createElement(schemaId, data); res.json({ success: true, element: { id: element.id, schemaId: element.schemaId, data: element.getData(), isValid: element.isValid() } }); } catch (error) { res.status(400).json({ success: false, error: error.message, validationErrors: error.validationErrors || [] }); } } async updateElement(req, res) { try { const { id } = req.params; const { updates } = req.body; const element = await this.manager.loadElement(id); for (const [field, value] of Object.entries(updates)) { await element.setField(field, value); } await element.save(); res.json({ success: true, element: { id: element.id, data: element.getData(), isValid: element.isValid() } }); } catch (error) { res.status(400).json({ success: false, error: error.message }); } } async validateElement(req, res) { try { const { schemaId, data } = req.body; const validationResult = await this.manager.validateData(schemaId, data); res.json({ success: true, isValid: validationResult.isValid, errors: validationResult.errors, warnings: validationResult.warnings }); } catch (error) { res.status(500).json({ success: false, error: error.message }); } } } ``` ## 🚨 Устранение неполадок ### Общие проблемы **Проблема:** Медленная валидация сложных схем ```typescript // ❌ Неправильно - валидация на каждое изменение await user.setField('name', 'John'); await user.setField('email', 'john@example.com'); await user.setField('age', 30); // ✅ Правильно - пакетное обновление await user.updateFields({ name: 'John', email: 'john@example.com', age: 30 }); ``` **Проблема:** Циклические зависимости в вычисляемых полях ```typescript // ❌ Неправильно - циклическая зависимость const badSchema: Schema = { properties: { a: { 'x-computed': { dependencies: ['b'], compute: (data) => data.b + 1 } }, b: { 'x-computed': { dependencies: ['a'], compute: (data) => data.a + 1 } } } }; // ✅ Правильно - разрыв цикла const goodSchema: Schema = { properties: { base: { type: 'number' }, a: { 'x-computed': { dependencies: ['base'], compute: (data) => data.base + 1 } }, b: { 'x-computed': { dependencies: ['a'], compute: (data) => data.a + 1 } } } }; ``` ### Диагностика ```typescript // Диагностика производительности схем const diagnostics = await manager.diagnoseSchema('User'); console.log('Диагностика схемы:', { complexity: diagnostics.complexity, validationTime: diagnostics.averageValidationTime, computedFieldCount: diagnostics.computedFieldCount, recommendations: diagnostics.recommendations }); // Профилирование валидации const profilingResult = await manager.profileValidation('User', userData); console.log('Профилирование валидации:', { totalTime: profilingResult.totalTime, fieldTimes: profilingResult.fieldTimes, bottlenecks: profilingResult.bottlenecks }); ``` Расширенная система типизированных элементов CSElement предоставляет мощные инструменты для создания строго типизированных, валидируемых и расширяемых структур данных с полной интеграцией в TypeScript экосистему.