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
Markdown
Система типизированных элементов 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 = /[!@
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 типов
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');
```
```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>
);
}
```
```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 экосистему.