UNPKG

cs-element

Version:

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

1,189 lines (1,108 loc) 42.3 kB
'use strict'; var events = require('events'); var worker_threads = require('worker_threads'); const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 0x100).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } let getRandomValues; const rnds8 = new Uint8Array(16); function rng() { if (!getRandomValues) { if (typeof crypto === 'undefined' || !crypto.getRandomValues) { throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); } getRandomValues = crypto.getRandomValues.bind(crypto); } return getRandomValues(rnds8); } const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native = { randomUUID }; function v4(options, buf, offset) { if (native.randomUUID && !buf && !options) { return native.randomUUID(); } options = options || {}; const rnds = options.random ?? options.rng?.() ?? rng(); if (rnds.length < 16) { throw new Error('Random bytes length must be >= 16'); } rnds[6] = (rnds[6] & 0x0f) | 0x40; rnds[8] = (rnds[8] & 0x3f) | 0x80; if (buf) { offset = offset || 0; if (offset < 0 || offset + 16 > buf.length) { throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`); } for (let i = 0; i < 16; ++i) { buf[offset + i] = rnds[i]; } return buf; } return unsafeStringify(rnds); } /** * Интерфейсы для Web Worker поддержки CSElement */ // ===== Типы операций для Worker ===== exports.WorkerOperationType = void 0; (function (WorkerOperationType) { // Операции с элементами WorkerOperationType["CREATE_ELEMENT"] = "create_element"; WorkerOperationType["UPDATE_ELEMENT"] = "update_element"; WorkerOperationType["DELETE_ELEMENT"] = "delete_element"; WorkerOperationType["CLONE_ELEMENT"] = "clone_element"; // Операции поиска и запросов WorkerOperationType["QUERY_ELEMENTS"] = "query_elements"; WorkerOperationType["ADVANCED_QUERY"] = "advanced_query"; WorkerOperationType["FILTER_ELEMENTS"] = "filter_elements"; WorkerOperationType["SORT_ELEMENTS"] = "sort_elements"; // Операции с данными WorkerOperationType["TRANSFORM_DATA"] = "transform_data"; WorkerOperationType["VALIDATE_DATA"] = "validate_data"; WorkerOperationType["COMPUTE_STATISTICS"] = "compute_statistics"; // Операции сериализации WorkerOperationType["SERIALIZE_ELEMENT"] = "serialize_element"; WorkerOperationType["DESERIALIZE_ELEMENT"] = "deserialize_element"; WorkerOperationType["SERIALIZE_TREE"] = "serialize_tree"; WorkerOperationType["DESERIALIZE_TREE"] = "deserialize_tree"; // Операции diff и merge WorkerOperationType["COMPUTE_DIFF"] = "compute_diff"; WorkerOperationType["APPLY_PATCH"] = "apply_patch"; WorkerOperationType["THREE_WAY_MERGE"] = "three_way_merge"; // Операции анализа WorkerOperationType["ANALYZE_STRUCTURE"] = "analyze_structure"; WorkerOperationType["DETECT_CYCLES"] = "detect_cycles"; WorkerOperationType["COMPUTE_METRICS"] = "compute_metrics"; // Операции валидации WorkerOperationType["VALIDATE_STRUCTURE"] = "validate_structure"; WorkerOperationType["CHECK_INTEGRITY"] = "check_integrity"; // Пользовательские операции WorkerOperationType["CUSTOM_OPERATION"] = "custom_operation"; })(exports.WorkerOperationType || (exports.WorkerOperationType = {})); /** * Менеджер Web Workers для CSElement * Управляет пулом воркеров и выполнением тяжелых операций */ class WorkerManager extends events.EventEmitter { constructor(config = {}, poolConfig = {}) { super(); this.workers = new Map(); this.workerQueue = []; this.pendingOperations = new Map(); this.operationCounter = 0; this.isInitialized = false; this.config = { maxConcurrentOperations: 4, timeoutMs: 30000, memoryLimitMB: 512, enableLogging: true, enableProfiling: false, enableCache: true, cacheSize: 100, ...config }; this.poolConfig = { size: Math.max(1, navigator.hardwareConcurrency || 4), strategy: 'least-busy', healthCheck: true, recycleAfter: 1000, ...poolConfig }; } /** * Инициализация пула воркеров */ async initialize() { if (this.isInitialized) { return; } try { // Создаем пул воркеров const workerPromises = Array.from({ length: this.poolConfig.size }, () => this.createWorker()); await Promise.all(workerPromises); this.isInitialized = true; if (this.config.enableLogging) { console.log(`WorkerManager initialized with ${this.poolConfig.size} workers`); } this.emit('initialized', { poolSize: this.poolConfig.size }); } catch (error) { this.emit('error', { type: 'initialization', error }); throw error; } } /** * Создание нового воркера */ async createWorker(config) { const workerId = v4(); try { const worker = new CSElementWorker(workerId, config || this.config); await worker.initialize(); this.workers.set(workerId, worker); this.workerQueue.push(worker); // Настройка обработчиков событий worker.on('message', (response) => { this.handleWorkerResponse(response); }); worker.on('error', (error) => { this.handleWorkerError(workerId, error); }); worker.on('terminated', () => { this.handleWorkerTermination(workerId); }); if (this.config.enableLogging) { console.log(`Worker ${workerId} created`); } this.emit('worker:created', { workerId }); return worker; } catch (error) { this.emit('worker:error', { workerId, error }); throw error; } } /** * Завершение работы воркера */ async terminateWorker(workerId) { const worker = this.workers.get(workerId); if (!worker) { throw new Error(`Worker ${workerId} not found`); } try { await worker.terminate(); this.workers.delete(workerId); // Удаляем из очереди const index = this.workerQueue.findIndex(w => w.id === workerId); if (index !== -1) { this.workerQueue.splice(index, 1); } if (this.config.enableLogging) { console.log(`Worker ${workerId} terminated`); } this.emit('worker:terminated', { workerId }); } catch (error) { this.emit('worker:error', { workerId, error }); throw error; } } /** * Выполнение операции */ async executeOperation(operation) { if (!this.isInitialized) { await this.initialize(); } const worker = this.selectWorker(); if (!worker) { throw new Error('No available workers'); } return new Promise((resolve, reject) => { const timeout = setTimeout(() => { this.pendingOperations.delete(operation.id); reject(new Error(`Operation ${operation.id} timed out`)); }, this.config.timeoutMs); this.pendingOperations.set(operation.id, { resolve, reject, timeout }); worker.execute(operation).catch(error => { this.pendingOperations.delete(operation.id); clearTimeout(timeout); reject(error); }); }); } /** * Получение статуса воркера */ getWorkerStatus(workerId) { if (workerId) { const worker = this.workers.get(workerId); if (!worker) { throw new Error(`Worker ${workerId} not found`); } return this.createWorkerStatus(worker); } // Возвращаем статус первого доступного воркера const worker = this.workerQueue[0]; if (!worker) { throw new Error('No workers available'); } return this.createWorkerStatus(worker); } /** * Получение статуса пула */ getPoolStatus() { const totalWorkers = this.workers.size; const activeWorkers = Array.from(this.workers.values()) .filter(w => w.status === 'busy').length; const totalOperations = Array.from(this.workers.values()) .reduce((sum, w) => sum + w.operationsCount, 0); const averageExecutionTime = totalOperations > 0 ? Array.from(this.workers.values()) .reduce((sum, w) => sum + w.totalExecutionTime || 0, 0) / totalOperations : 0; const memoryUsage = Array.from(this.workers.values()) .reduce((sum, w) => sum + w.memoryUsage, 0); return { totalWorkers, activeWorkers, queuedOperations: this.pendingOperations.size, totalOperations, averageExecutionTime, memoryUsage }; } /** * Выбор воркера для выполнения операции */ selectWorker() { const availableWorkers = this.workerQueue.filter(w => w.status === 'idle' || w.status === 'busy'); if (availableWorkers.length === 0) { return null; } switch (this.poolConfig.strategy) { case 'round-robin': return this.selectRoundRobin(availableWorkers); case 'least-busy': return this.selectLeastBusy(availableWorkers); case 'random': return this.selectRandom(availableWorkers); default: return availableWorkers[0]; } } selectRoundRobin(workers) { const index = this.operationCounter % workers.length; this.operationCounter++; return workers[index]; } selectLeastBusy(workers) { return workers.reduce((least, current) => current.operationsCount < least.operationsCount ? current : least); } selectRandom(workers) { const index = Math.floor(Math.random() * workers.length); return workers[index]; } /** * Обработка ответа от воркера */ handleWorkerResponse(response) { const pending = this.pendingOperations.get(response.id); if (!pending) { return; } clearTimeout(pending.timeout); this.pendingOperations.delete(response.id); if (response.success) { pending.resolve(response.result); } else { pending.reject(new Error(response.error)); } this.emit('operation:completed', response); } /** * Обработка ошибки воркера */ handleWorkerError(workerId, error) { if (this.config.enableLogging) { console.error(`Worker ${workerId} error:`, error); } this.emit('worker:error', { workerId, error }); // Если воркер критически поврежден, пересоздаем его if (error.code === 'CRITICAL_ERROR') { this.recreateWorker(workerId); } } /** * Обработка завершения воркера */ handleWorkerTermination(workerId) { this.workers.delete(workerId); const index = this.workerQueue.findIndex(w => w.id === workerId); if (index !== -1) { this.workerQueue.splice(index, 1); } this.emit('worker:terminated', { workerId }); } /** * Пересоздание воркера */ async recreateWorker(workerId) { try { await this.terminateWorker(workerId); await this.createWorker(); } catch (error) { this.emit('error', { type: 'recreation', workerId, error }); } } /** * Создание объекта статуса воркера */ createWorkerStatus(worker) { return { id: worker.id, status: worker.status, operationsCount: worker.operationsCount, averageExecutionTime: worker.averageExecutionTime || 0, memoryUsage: worker.memoryUsage, lastError: undefined }; } /** * Завершение работы всех воркеров */ async shutdown() { const terminationPromises = Array.from(this.workers.keys()).map(workerId => this.terminateWorker(workerId)); await Promise.all(terminationPromises); this.isInitialized = false; this.emit('shutdown'); } /** * Проверка здоровья воркеров */ async healthCheck() { if (!this.poolConfig.healthCheck) { return {}; } const healthPromises = Array.from(this.workers.entries()).map(async ([workerId, worker]) => { try { const healthMessage = { id: `health-${Date.now()}`, type: exports.WorkerOperationType.CUSTOM_OPERATION, timestamp: Date.now(), payload: { operation: 'health-check' } }; await worker.execute(healthMessage); return [workerId, true]; } catch { return [workerId, false]; } }); const results = await Promise.all(healthPromises); return Object.fromEntries(results); } } /** * Реализация WorkerInstance */ class CSElementWorker extends events.EventEmitter { constructor(id, _config) { super(); this.status = 'idle'; this.operationsCount = 0; this.memoryUsage = 0; this.worker = null; this.totalExecutionTime = 0; this.id = id; this.createdAt = Date.now(); this.lastActivity = Date.now(); } async initialize() { try { // В тестовой среде используем заглушку if (typeof jest !== 'undefined' || process.env.NODE_ENV === 'test') { const listeners = new Map(); this.worker = { postMessage: (message) => { // Эмулируем обработку сообщения setTimeout(() => { const response = { id: message.id, success: true, result: { processed: true, operation: message.type }, timestamp: Date.now(), executionTime: 10 }; // Вызываем все слушатели сообщений const messageListeners = listeners.get('message') || []; messageListeners.forEach(listener => { listener({ data: response }); }); }, 10); }, terminate: () => { }, on: () => { }, off: () => { }, addListener: (event, listener) => { if (!listeners.has(event)) { listeners.set(event, []); } listeners.get(event).push(listener); }, removeListener: (event, listener) => { const eventListeners = listeners.get(event); if (eventListeners) { const index = eventListeners.indexOf(listener); if (index > -1) { eventListeners.splice(index, 1); } } } }; } else { // Создаем Worker Thread const workerScript = this.generateWorkerScript(); // В Node.js используем worker_threads if (typeof worker_threads.Worker !== 'undefined' && worker_threads.Worker.length > 1) { // Node.js Worker threads - используем временный файл try { const fs = require('fs'); const path = require('path'); const os = require('os'); const tempFile = path.join(os.tmpdir(), `cs-worker-${this.id}.js`); fs.writeFileSync(tempFile, workerScript); this.worker = new worker_threads.Worker(tempFile); this.worker.on('message', (data) => { this.handleMessage(data); }); this.worker.on('error', (error) => { this.handleError({ code: 'WORKER_ERROR', message: error.message, stack: error.stack || '' }); }); this.worker.on('exit', (code) => { // Удаляем временный файл try { fs.unlinkSync(tempFile); } catch (e) { // Игнорируем ошибки удаления } if (code !== 0) { this.handleError({ code: 'WORKER_EXIT', message: `Worker stopped with exit code ${code}`, stack: '' }); } }); } catch (nodeError) { // Fallback заглушка для Node.js this.worker = { postMessage: () => { }, terminate: () => { }, on: () => { }, addListener: () => { }, removeListener: () => { } }; } } else { // Fallback для браузеров - Web Workers const blob = new Blob([workerScript], { type: 'application/javascript' }); const workerUrl = URL.createObjectURL(blob); this.worker = new worker_threads.Worker(workerUrl); this.worker.onmessage = (event) => { this.handleMessage(event.data); }; this.worker.onerror = (error) => { this.handleError({ code: 'WORKER_ERROR', message: error.message, stack: error.filename + ':' + error.lineno }); }; // Очищаем URL после создания воркера URL.revokeObjectURL(workerUrl); } } this.status = 'idle'; } catch (error) { this.status = 'error'; throw error; } } async execute(operation) { if (!this.worker || this.status === 'terminated') { throw new Error('Worker is not available'); } this.status = 'busy'; this.lastActivity = Date.now(); return new Promise((resolve, reject) => { const startTime = Date.now(); const messageHandler = (event) => { const response = event.data; if (response.id === operation.id) { this.worker.removeListener('message', messageHandler); const executionTime = Date.now() - startTime; this.totalExecutionTime += executionTime; this.operationsCount++; this.status = 'idle'; if (response.success) { resolve(response.result); } else { reject(new Error(response.error)); } this.emit('message', response); } }; this.worker.addListener('message', messageHandler); this.worker.postMessage(operation); }); } async terminate() { if (this.worker) { this.worker.terminate(); this.worker = null; } this.status = 'terminated'; this.emit('terminated'); } handleMessage(_data) { this.lastActivity = Date.now(); // Сообщения обрабатываются в execute() } handleError(error) { this.status = 'error'; this.emit('error', error); } get averageExecutionTime() { return this.operationsCount > 0 ? this.totalExecutionTime / this.operationsCount : 0; } /** * Генерация кода для Web Worker */ generateWorkerScript() { return ` // Web Worker script for CSElement operations const operations = new Map(); // Импорт необходимых функций (будет заменен на реальную реализацию) ${this.getWorkerOperations()} self.onmessage = async function(event) { const message = event.data; const startTime = Date.now(); try { const result = await executeOperation(message); self.postMessage({ id: message.id, success: true, result: result, timestamp: Date.now(), executionTime: Date.now() - startTime }); } catch (error) { self.postMessage({ id: message.id, success: false, error: error.message, timestamp: Date.now(), executionTime: Date.now() - startTime }); } }; async function executeOperation(message) { const { type, payload } = message; switch (type) { case 'serialize_element': return serializeElement(payload); case 'deserialize_element': return deserializeElement(payload); case 'query_elements': return queryElements(payload); case 'compute_diff': return computeDiff(payload); case 'three_way_merge': return threeWayMerge(payload); case 'analyze_structure': return analyzeStructure(payload); default: throw new Error('Unknown operation type: ' + type); } } `; } /** * Получение операций для воркера */ getWorkerOperations() { return ` // Импорт необходимых модулей для воркера const msgpack = require('msgpack-lite'); // Простая реализация QueryEngine для воркера class WorkerQueryEngine { static query(rootData, selector) { const results = []; // Базовая реализация поиска по селектору if (typeof selector === 'string') { if (selector.startsWith('#')) { // Поиск по ID const id = selector.substring(1); this.findById(rootData, id, results); } else if (selector.startsWith('.')) { // Поиск по классу/имени const className = selector.substring(1); this.findByName(rootData, className, results); } else { // Поиск по имени элемента this.findByName(rootData, selector, results); } } else if (selector && typeof selector === 'object') { // Объектный селектор this.findBySelector(rootData, selector, results); } return results; } static findById(element, id, results) { if (element.id === id) { results.push(element); } if (element.children) { Object.values(element.children).forEach(child => { this.findById(child, id, results); }); } } static findByName(element, name, results) { if (element.name === name) { results.push(element); } if (element.children) { Object.values(element.children).forEach(child => { this.findByName(child, name, results); }); } } static findBySelector(element, selector, results) { let matches = true; if (selector.name && element.name !== selector.name) { matches = false; } if (selector.id && element.id !== selector.id) { matches = false; } if (selector.data && selector.data.length > 0) { for (const key of selector.data) { if (!element.data || !element.data.hasOwnProperty(key)) { matches = false; break; } } } if (matches) { results.push(element); } if (element.children) { Object.values(element.children).forEach(child => { this.findBySelector(child, selector, results); }); } } } // Простая реализация DiffEngine для воркера class WorkerDiffEngine { static computeDiff(source, target) { const changes = []; // Сравнение основных свойств if (source.id !== target.id) { changes.push({ type: 'modify', path: ['id'], oldValue: source.id, newValue: target.id }); } if (source.name !== target.name) { changes.push({ type: 'modify', path: ['name'], oldValue: source.name, newValue: target.name }); } // Сравнение данных this.compareData(source.data || {}, target.data || {}, ['data'], changes); // Сравнение дочерних элементов this.compareChildren(source.children || {}, target.children || {}, ['children'], changes); return { changes, summary: { additions: changes.filter(c => c.type === 'add').length, deletions: changes.filter(c => c.type === 'delete').length, modifications: changes.filter(c => c.type === 'modify').length } }; } static compareData(sourceData, targetData, path, changes) { const sourceKeys = Object.keys(sourceData); const targetKeys = Object.keys(targetData); // Удаленные ключи for (const key of sourceKeys) { if (!targetKeys.includes(key)) { changes.push({ type: 'delete', path: [...path, key], oldValue: sourceData[key], newValue: undefined }); } } // Добавленные ключи for (const key of targetKeys) { if (!sourceKeys.includes(key)) { changes.push({ type: 'add', path: [...path, key], oldValue: undefined, newValue: targetData[key] }); } } // Измененные ключи for (const key of sourceKeys) { if (targetKeys.includes(key) && sourceData[key] !== targetData[key]) { changes.push({ type: 'modify', path: [...path, key], oldValue: sourceData[key], newValue: targetData[key] }); } } } static compareChildren(sourceChildren, targetChildren, path, changes) { const sourceKeys = Object.keys(sourceChildren); const targetKeys = Object.keys(targetChildren); // Удаленные дочерние элементы for (const key of sourceKeys) { if (!targetKeys.includes(key)) { changes.push({ type: 'delete', path: [...path, key], oldValue: sourceChildren[key], newValue: undefined }); } } // Добавленные дочерние элементы for (const key of targetKeys) { if (!sourceKeys.includes(key)) { changes.push({ type: 'add', path: [...path, key], oldValue: undefined, newValue: targetChildren[key] }); } } // Рекурсивное сравнение существующих дочерних элементов for (const key of sourceKeys) { if (targetKeys.includes(key)) { const childDiff = this.computeDiff(sourceChildren[key], targetChildren[key]); // Добавляем изменения с обновленными путями for (const change of childDiff.changes) { changes.push({ ...change, path: [...path, key, ...change.path] }); } } } } static threeWayMerge(base, source, target) { const sourceChanges = this.computeDiff(base, source); const targetChanges = this.computeDiff(base, target); // Простая стратегия слияния const merged = JSON.parse(JSON.stringify(base)); const conflicts = []; // Применяем изменения из source for (const change of sourceChanges.changes) { this.applyChange(merged, change); } // Применяем изменения из target, проверяя конфликты for (const change of targetChanges.changes) { const conflictingChange = sourceChanges.changes.find(sc => sc.path.join('.') === change.path.join('.') && sc.type === change.type ); if (conflictingChange && conflictingChange.newValue !== change.newValue) { conflicts.push({ path: change.path, sourceValue: conflictingChange.newValue, targetValue: change.newValue, baseValue: change.oldValue, type: 'content', severity: 'medium', autoResolvable: false, suggestions: [] }); } else { this.applyChange(merged, change); } } return { merged, conflicts, autoResolved: 0, manualRequired: conflicts.length, success: conflicts.length === 0, metadata: { algorithm: 'simple', strategy: 'auto', executionTime: 0 } }; } static applyChange(obj, change) { const path = change.path.slice(); const key = path.pop(); let current = obj; for (const pathPart of path) { if (!current[pathPart]) { current[pathPart] = {}; } current = current[pathPart]; } if (change.type === 'delete') { delete current[key]; } else { current[key] = change.newValue; } } } // Утилиты для сериализации function serializeWithMessagePack(data) { try { return msgpack.encode(data); } catch (error) { // Fallback на JSON return JSON.stringify(data); } } function deserializeWithMessagePack(data) { try { if (typeof data === 'string') { return JSON.parse(data); } return msgpack.decode(data); } catch (error) { // Fallback на JSON return typeof data === 'string' ? JSON.parse(data) : data; } } function serializeElement(payload) { // Используем встроенную сериализацию CSElement if (payload && typeof payload.serialize === 'function') { const serialized = payload.serialize({ includeChildren: true, includeData: true, includeMetadata: true }); return serializeWithMessagePack(serialized); } // Fallback для простых объектов return serializeWithMessagePack(payload); } function deserializeElement(payload) { // Используем встроенную десериализацию CSElement const data = deserializeWithMessagePack(payload.data); if (data && typeof CSElement !== 'undefined' && CSElement.deserialize) { return CSElement.deserialize(data); } // Fallback для простых объектов return data; } function queryElements(payload) { const { rootData, selector, options = {} } = payload; const startTime = Date.now(); try { const results = WorkerQueryEngine.query(rootData, selector); const total = results.length; const limit = options.limit || 100; const offset = options.offset || 0; const paginatedResults = results.slice(offset, offset + limit); return { elements: paginatedResults, total, hasMore: offset + limit < total, executionTime: Date.now() - startTime, metadata: { selector: typeof selector === 'string' ? selector : JSON.stringify(selector), algorithm: 'worker-query-engine', cacheHit: false } }; } catch (error) { return { elements: [], total: 0, hasMore: false, executionTime: Date.now() - startTime, error: error.message }; } } function computeDiff(payload) { const { source, target, options = {} } = payload; const startTime = Date.now(); try { const result = WorkerDiffEngine.computeDiff(source, target); return { ...result, executionTime: Date.now() - startTime, metadata: { algorithm: options.algorithm || 'simple', includeVisualization: options.includeVisualization || false } }; } catch (error) { return { changes: [], summary: { additions: 0, deletions: 0, modifications: 0 }, executionTime: Date.now() - startTime, error: error.message }; } } function threeWayMerge(payload) { const { base, source, target, options = {} } = payload; const startTime = Date.now(); try { const result = WorkerDiffEngine.threeWayMerge(base, source, target); return { ...result, metadata: { ...result.metadata, executionTime: Date.now() - startTime, strategy: options.strategy || 'auto' } }; } catch (error) { return { merged: base, conflicts: [], autoResolved: 0, manualRequired: 1, success: false, metadata: { algorithm: 'simple', strategy: 'auto', executionTime: Date.now() - startTime }, error: error.message }; } } function analyzeStructure(payload) { const { element, options = {} } = payload; const startTime = Date.now(); try { const metrics = { totalElements: 0, maxDepth: 0, avgChildrenPerElement: 0, dataFields: new Set(), elementTypes: new Set() }; function analyzeElement(elem, depth = 0) { metrics.totalElements++; metrics.maxDepth = Math.max(metrics.maxDepth, depth); if (elem.name) { metrics.elementTypes.add(elem.name); } if (elem.data) { Object.keys(elem.data).forEach(key => metrics.dataFields.add(key)); } let childCount = 0; if (elem.children) { childCount = Object.keys(elem.children).length; Object.values(elem.children).forEach(child => { analyzeElement(child, depth + 1); }); } return childCount; } const totalChildren = analyzeElement(element); metrics.avgChildrenPerElement = metrics.totalElements > 0 ? totalChildren / metrics.totalElements : 0; const insights = []; const recommendations = []; // Генерируем инсайты if (metrics.maxDepth > 10) { insights.push({ type: 'warning', message: 'Глубокая вложенность элементов может влиять на производительность', value: metrics.maxDepth }); recommendations.push({ type: 'optimization', message: 'Рассмотрите возможность уплощения структуры', priority: 'medium' }); } if (metrics.totalElements > 1000) { insights.push({ type: 'info', message: 'Большое количество элементов', value: metrics.totalElements }); recommendations.push({ type: 'performance', message: 'Используйте пагинацию для больших структур', priority: 'high' }); } return { metrics: { ...metrics, dataFields: Array.from(metrics.dataFields), elementTypes: Array.from(metrics.elementTypes) }, insights, recommendations, executionTime: Date.now() - startTime, metadata: { algorithm: 'worker-structure-analyzer', options } }; } catch (error) { return { metrics: {}, insights: [], recommendations: [], executionTime: Date.now() - startTime, error: error.message }; } } `; } } exports.WorkerManager = WorkerManager; //# sourceMappingURL=nodejs.js.map