UNPKG

cs-element

Version:

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

1,094 lines (1,090 loc) 44.4 kB
'use strict'; var axios = require('axios'); var ws = require('ws'); // Типы сетевых операций var NetworkOperation; (function (NetworkOperation) { NetworkOperation["SYNC"] = "sync"; NetworkOperation["UPLOAD"] = "upload"; NetworkOperation["DOWNLOAD"] = "download"; NetworkOperation["BROADCAST"] = "broadcast"; NetworkOperation["SUBSCRIBE"] = "subscribe"; NetworkOperation["UNSUBSCRIBE"] = "unsubscribe"; NetworkOperation["PING"] = "ping"; NetworkOperation["HEARTBEAT"] = "heartbeat"; })(NetworkOperation || (NetworkOperation = {})); var ConnectionType; (function (ConnectionType) { ConnectionType["HTTP"] = "http"; ConnectionType["WEBSOCKET"] = "websocket"; ConnectionType["TCP"] = "tcp"; ConnectionType["UDP"] = "udp"; ConnectionType["GRPC"] = "grpc"; ConnectionType["MQTT"] = "mqtt"; })(ConnectionType || (ConnectionType = {})); var SyncStrategy; (function (SyncStrategy) { SyncStrategy["FULL_SYNC"] = "full_sync"; SyncStrategy["INCREMENTAL_SYNC"] = "incremental_sync"; SyncStrategy["CONFLICT_RESOLUTION"] = "conflict_resolution"; SyncStrategy["MERGE"] = "merge"; SyncStrategy["OVERWRITE"] = "overwrite"; })(SyncStrategy || (SyncStrategy = {})); var NetworkStatus; (function (NetworkStatus) { NetworkStatus["CONNECTED"] = "connected"; NetworkStatus["DISCONNECTED"] = "disconnected"; NetworkStatus["CONNECTING"] = "connecting"; NetworkStatus["RECONNECTING"] = "reconnecting"; NetworkStatus["ERROR"] = "error"; })(NetworkStatus || (NetworkStatus = {})); var ConflictResolution; (function (ConflictResolution) { ConflictResolution["LOCAL_WINS"] = "local_wins"; ConflictResolution["REMOTE_WINS"] = "remote_wins"; ConflictResolution["TIMESTAMP_WINS"] = "timestamp_wins"; ConflictResolution["MERGE_CHANGES"] = "merge_changes"; ConflictResolution["MANUAL_RESOLUTION"] = "manual_resolution"; })(ConflictResolution || (ConflictResolution = {})); class NetworkPlugin { constructor(config = {}) { this.name = 'NetworkPlugin'; this.version = '1.0.0'; this.description = 'Плагин для сетевых операций и синхронизации данных'; this.endpoints = new Map(); this.syncConfigs = new Map(); this.connections = new Map(); this.pendingRequests = new Map(); this.requestQueue = []; this.offlineQueue = []; this.syncResults = []; this.events = []; this.isOnline = true; this.config = { enabled: true, priority: 75, enableAutoSync: true, defaultSyncInterval: 300000, // 5 минут maxConcurrentConnections: 10, connectionTimeout: 30000, requestTimeout: 60000, maxRetries: 3, retryDelay: 1000, enableOfflineMode: true, offlineQueueSize: 1000, enableCompression: true, enableEncryption: false, heartbeatInterval: 30000, reconnectDelay: 5000, maxReconnectAttempts: 5, enableEventLogging: true, logLevel: 'info', ...config }; this.stats = { totalConnections: 0, activeConnections: 0, failedConnections: 0, totalRequests: 0, successfulRequests: 0, failedRequests: 0, totalDataTransferred: 0, averageLatency: 0, averageResponseTime: 0, syncOperations: 0, successfulSyncs: 0, failedSyncs: 0, conflictsResolved: 0, offlineOperations: 0, uptime: 0, errorRate: 0 }; } install(_CSElementClass) { // Регистрация плагина в системе CSElement // В реальной реализации здесь была бы интеграция с жизненным циклом элементов console.log(`${this.name} установлен для CSElement`); } async initialize() { console.log(`Инициализация ${this.name} v${this.version}`); // Мониторинг сетевого состояния this.setupNetworkMonitoring(); // Запуск автосинхронизации if (this.config.enableAutoSync) { this.startAutoSync(); } // Запуск heartbeat this.startHeartbeat(); // Обработка очереди запросов this.startRequestProcessor(); // Обновление статистики setInterval(() => { this.updateStats(); }, 60000); // каждую минуту } async destroy() { if (this.syncTimer) { clearInterval(this.syncTimer); } if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); } // Закрытие всех соединений for (const connection of this.connections.values()) { await this.closeConnection(connection.id); } console.log(`${this.name} деинициализирован`); } getConfig() { return { ...this.config }; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } // Lifecycle hooks async afterCreate(element, _context) { if (this.config.enableAutoSync) { await this.queueElementSync(element, 'create'); } return { success: true }; } async afterUpdate(element, _context) { if (this.config.enableAutoSync) { await this.queueElementSync(element, 'update'); } return { success: true }; } async afterDelete(element, _context) { if (this.config.enableAutoSync) { await this.queueElementSync(element, 'delete'); } return { success: true }; } // Управление конечными точками addEndpoint(endpointData) { const endpoint = { id: this.generateId(), ...endpointData }; this.endpoints.set(endpoint.id, endpoint); this.log('info', `Добавлена конечная точка: ${endpoint.name}`); return endpoint.id; } getEndpoint(id) { return this.endpoints.get(id); } updateEndpoint(id, updates) { const endpoint = this.endpoints.get(id); if (!endpoint) return false; Object.assign(endpoint, updates); return true; } deleteEndpoint(id) { const deleted = this.endpoints.delete(id); if (deleted) { // Закрытие соединений с этой конечной точкой this.closeConnectionsByEndpoint(id); } return deleted; } // Управление соединениями async createConnection(endpointId) { const endpoint = this.endpoints.get(endpointId); if (!endpoint) { throw new Error(`Конечная точка ${endpointId} не найдена`); } if (this.stats.activeConnections >= this.config.maxConcurrentConnections) { throw new Error('Достигнуто максимальное количество соединений'); } const connectionId = this.generateId(); const connection = { id: connectionId, endpointId, type: endpoint.type, status: NetworkStatus.CONNECTING, messagesSent: 0, messagesReceived: 0, bytesTransferred: 0, latency: 0, errorCount: 0 }; this.connections.set(connectionId, connection); this.stats.totalConnections++; try { await this.establishConnection(connection, endpoint); connection.status = NetworkStatus.CONNECTED; connection.connectedAt = new Date(); connection.lastActivity = new Date(); this.stats.activeConnections++; this.emitEvent('connection_established', { connectionId, endpointId }); this.log('info', `Установлено соединение с ${endpoint.name}`); return connectionId; } catch (error) { connection.status = NetworkStatus.ERROR; connection.errorCount++; this.stats.failedConnections++; this.log('error', `Ошибка подключения к ${endpoint.name}: ${error}`); throw error; } } async closeConnection(connectionId) { const connection = this.connections.get(connectionId); if (!connection) return false; try { await this.terminateConnection(connection); connection.status = NetworkStatus.DISCONNECTED; this.stats.activeConnections--; this.emitEvent('connection_lost', { connectionId }); this.log('info', `Соединение ${connectionId} закрыто`); return true; } catch (error) { this.log('error', `Ошибка при закрытии соединения: ${error}`); return false; } } // Конфигурация синхронизации addSyncConfig(configData) { const config = { id: this.generateId(), lastSync: undefined, nextSync: this.calculateNextSync(configData.syncInterval), errorCount: 0, ...configData }; this.syncConfigs.set(config.id, config); this.log('info', `Добавлена конфигурация синхронизации: ${config.name}`); return config.id; } getSyncConfig(id) { return this.syncConfigs.get(id); } updateSyncConfig(id, updates) { const config = this.syncConfigs.get(id); if (!config) return false; Object.assign(config, updates); if (updates.syncInterval) { config.nextSync = this.calculateNextSync(updates.syncInterval); } return true; } deleteSyncConfig(id) { return this.syncConfigs.delete(id); } // Синхронизация данных async syncData(configId) { const config = this.syncConfigs.get(configId); if (!config) { throw new Error(`Конфигурация синхронизации ${configId} не найдена`); } const endpoint = this.endpoints.get(config.endpointId); if (!endpoint) { throw new Error(`Конечная точка ${config.endpointId} не найдена`); } const startTime = new Date(); const syncResult = { id: this.generateId(), configId, startTime, endTime: new Date(), success: false, strategy: config.strategy, elementsProcessed: 0, elementsUpdated: 0, elementsCreated: 0, elementsDeleted: 0, conflicts: [], errors: [], statistics: { totalElements: 0, syncedElements: 0, skippedElements: 0, conflictedElements: 0, dataTransferred: 0, transferSpeed: 0, networkLatency: 0 } }; this.stats.syncOperations++; try { this.log('info', `Начало синхронизации: ${config.name}`); // Получение данных для синхронизации const localData = await this.getLocalData(config); const remoteData = await this.getRemoteData(endpoint, config); syncResult.statistics.totalElements = localData.length + remoteData.length; // Выполнение синхронизации switch (config.strategy) { case SyncStrategy.FULL_SYNC: await this.performFullSync(localData, remoteData, config, syncResult); break; case SyncStrategy.INCREMENTAL_SYNC: await this.performIncrementalSync(localData, remoteData, config, syncResult); break; case SyncStrategy.CONFLICT_RESOLUTION: await this.performConflictResolution(localData, remoteData, config, syncResult); break; default: throw new Error(`Неподдерживаемая стратегия синхронизации: ${config.strategy}`); } syncResult.success = true; syncResult.endTime = new Date(); config.lastSync = new Date(); config.nextSync = this.calculateNextSync(config.syncInterval); config.errorCount = 0; this.stats.successfulSyncs++; this.stats.lastSyncTime = new Date(); this.emitEvent('sync_completed', { configId, success: true }); this.log('info', `Синхронизация завершена успешно: ${config.name}`); } catch (error) { syncResult.success = false; syncResult.errors.push(error instanceof Error ? error.message : String(error)); config.errorCount++; this.stats.failedSyncs++; if (config.errorCount >= config.maxErrors) { config.enabled = false; this.log('warn', `Синхронизация ${config.name} отключена из-за превышения количества ошибок`); } this.log('error', `Ошибка синхронизации ${config.name}: ${error}`); } this.syncResults.push(syncResult); return syncResult; } // Отправка запросов async sendRequest(request) { const networkRequest = { id: this.generateId(), timestamp: new Date(), ...request }; if (!this.isOnline && this.config.enableOfflineMode) { return this.queueOfflineRequest(networkRequest); } return this.executeRequest(networkRequest); } // Управление сообщениями async sendMessage(connectionId, message) { const connection = this.connections.get(connectionId); if (!connection || connection.status !== NetworkStatus.CONNECTED) { return false; } const networkMessage = { id: this.generateId(), timestamp: new Date(), ...message }; try { await this.transmitMessage(connection, networkMessage); connection.messagesSent++; connection.lastActivity = new Date(); return true; } catch (error) { connection.errorCount++; this.log('error', `Ошибка отправки сообщения: ${error}`); return false; } } // Утилиты async establishConnection(connection, endpoint) { const startTime = Date.now(); try { switch (endpoint.type) { case ConnectionType.HTTP: // HTTP соединение не требует постоянного подключения connection.status = NetworkStatus.CONNECTED; break; case ConnectionType.WEBSOCKET: await this.establishWebSocketConnection(connection, endpoint); break; case ConnectionType.TCP: await this.establishTCPConnection(connection, endpoint); break; default: throw new Error(`Неподдерживаемый тип соединения: ${endpoint.type}`); } connection.connectedAt = new Date(); connection.lastActivity = new Date(); connection.latency = Date.now() - startTime; this.emitEvent('connection_established', { connectionId: connection.id, endpointId: endpoint.id, latency: connection.latency }); } catch (error) { connection.status = NetworkStatus.ERROR; connection.errorCount++; this.emitEvent('error_occurred', { connectionId: connection.id, error: error instanceof Error ? error.message : String(error) }); throw error; } } async establishWebSocketConnection(connection, endpoint) { return new Promise((resolve, reject) => { try { // Используем динамический импорт для WebSocket в браузере или Node.js if (typeof window !== 'undefined' && window.WebSocket) { // Браузерная среда const ws$1 = new ws.WebSocket(endpoint.url); ws$1.onopen = () => { connection.socket = ws$1; connection.status = NetworkStatus.CONNECTED; this.setupWebSocketHandlers(connection, ws$1); resolve(); }; ws$1.onerror = (error) => { reject(new Error(`WebSocket connection failed: ${error}`)); }; } else { // Node.js среда const WebSocket = require('ws'); const ws = new WebSocket(endpoint.url, { headers: endpoint.headers, handshakeTimeout: endpoint.timeout }); ws.on('open', () => { connection.socket = ws; connection.status = NetworkStatus.CONNECTED; this.setupWebSocketHandlers(connection, ws); resolve(); }); ws.on('error', (error) => { reject(new Error(`WebSocket connection failed: ${error.message}`)); }); } // Таймаут подключения setTimeout(() => { if (connection.status !== NetworkStatus.CONNECTED) { reject(new Error('WebSocket connection timeout')); } }, endpoint.timeout); } catch (error) { reject(error); } }); } async establishTCPConnection(connection, endpoint) { return new Promise((resolve, reject) => { try { const net = require('net'); const url = new URL(endpoint.url); const port = parseInt(url.port) || 80; const host = url.hostname; const socket = net.createConnection({ port, host }, () => { connection.socket = socket; connection.status = NetworkStatus.CONNECTED; this.setupTCPHandlers(connection, socket); resolve(); }); socket.on('error', (error) => { reject(new Error(`TCP connection failed: ${error.message}`)); }); socket.setTimeout(endpoint.timeout, () => { socket.destroy(); reject(new Error('TCP connection timeout')); }); } catch (error) { reject(error); } }); } setupWebSocketHandlers(connection, ws) { ws.onmessage = (event) => { connection.messagesReceived++; connection.lastActivity = new Date(); try { const message = JSON.parse(event.data); this.handleIncomingMessage(connection, message); } catch (error) { this.log('error', `Ошибка парсинга WebSocket сообщения: ${error}`); } }; ws.onclose = () => { connection.status = NetworkStatus.DISCONNECTED; this.emitEvent('connection_lost', { connectionId: connection.id }); }; ws.onerror = (error) => { connection.errorCount++; this.log('error', `WebSocket ошибка: ${error}`); }; } setupTCPHandlers(connection, socket) { socket.on('data', (data) => { connection.messagesReceived++; connection.bytesTransferred += data.length; connection.lastActivity = new Date(); try { const message = JSON.parse(data.toString()); this.handleIncomingMessage(connection, message); } catch (error) { this.log('error', `Ошибка парсинга TCP сообщения: ${error}`); } }); socket.on('close', () => { connection.status = NetworkStatus.DISCONNECTED; this.emitEvent('connection_lost', { connectionId: connection.id }); }); socket.on('error', (error) => { connection.errorCount++; this.log('error', `TCP ошибка: ${error.message}`); }); } handleIncomingMessage(connection, message) { // Обработка входящих сообщений this.log('debug', `Получено сообщение от ${connection.id}: ${JSON.stringify(message)}`); // Здесь можно добавить логику обработки различных типов сообщений if (message.type === 'ping') { // Отвечаем на ping this.sendMessage(connection.id, { connectionId: connection.id, type: 'response', operation: NetworkOperation.PING, payload: { pong: true }, priority: 1, requiresAck: false }); } } async terminateConnection(connection) { try { connection.status = NetworkStatus.DISCONNECTED; // Закрываем соединение в зависимости от типа if (connection.socket) { switch (connection.type) { case ConnectionType.WEBSOCKET: if (connection.socket.readyState === ws.WebSocket.OPEN) { connection.socket.close(); } break; case ConnectionType.TCP: if (connection.socket.destroy) { connection.socket.destroy(); } break; default: // Для HTTP соединений нет постоянного сокета break; } } connection.socket = undefined; this.emitEvent('connection_lost', { connectionId: connection.id }); } catch (error) { console.error(`Ошибка закрытия соединения ${connection.id}:`, error); } } async closeConnectionsByEndpoint(endpointId) { const connectionsToClose = Array.from(this.connections.values()) .filter(conn => conn.endpointId === endpointId); for (const connection of connectionsToClose) { await this.closeConnection(connection.id); } } async queueElementSync(element, operation) { // Поиск подходящих конфигураций синхронизации const applicableConfigs = Array.from(this.syncConfigs.values()) .filter(config => config.enabled && this.shouldSyncElement(element, config)); for (const config of applicableConfigs) { const request = { id: this.generateId(), operation: NetworkOperation.SYNC, endpointId: config.endpointId, method: 'POST', path: '/sync', headers: { 'Content-Type': 'application/json' }, body: { operation, element: element.serialize(), timestamp: new Date().toISOString() }, timestamp: new Date(), timeout: this.config.requestTimeout, retryCount: 0, priority: 1, metadata: { configId: config.id, elementId: element.id } }; this.requestQueue.push(request); } } shouldSyncElement(element, config) { // Проверка фильтров синхронизации return config.filterRules.every(filter => { const elementValue = this.getElementValue(element, filter.field); const matches = this.evaluateFilter(elementValue, filter.operator, filter.value); return filter.include ? matches : !matches; }); } getElementValue(element, field) { if (field === 'id') return element.id; return element.getData(field); } evaluateFilter(actual, operator, expected) { switch (operator) { case 'equals': return actual === expected; case 'not_equals': return actual !== expected; case 'contains': return String(actual).includes(String(expected)); case 'matches': return new RegExp(expected).test(String(actual)); case 'greater': return actual > expected; case 'less': return actual < expected; default: return true; } } async executeRequest(request) { const startTime = Date.now(); this.stats.totalRequests++; this.pendingRequests.set(request.id, request); try { const endpoint = this.endpoints.get(request.endpointId); if (!endpoint) { throw new Error(`Endpoint ${request.endpointId} не найден`); } const url = endpoint.url + request.path; // Выполняем HTTP запрос с помощью axios let axiosResponse; if (typeof window !== 'undefined') { // Браузерная среда - используем fetch const fetchOptions = { method: request.method, headers: { ...endpoint.headers, ...request.headers }, body: request.body ? JSON.stringify(request.body) : undefined, signal: AbortSignal.timeout(request.timeout) }; // Добавляем аутентификацию if (endpoint.authentication) { this.addAuthentication(fetchOptions, endpoint.authentication); } const fetchResponse = await fetch(url, fetchOptions); const responseBody = await fetchResponse.text(); let parsedBody; try { parsedBody = JSON.parse(responseBody); } catch { parsedBody = responseBody; } axiosResponse = { status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), data: parsedBody }; } else { // Node.js среда - используем axios const axios = require('axios'); const axiosConfig = { method: request.method.toLowerCase(), url, headers: { ...endpoint.headers, ...request.headers }, data: request.body, timeout: request.timeout, validateStatus: () => true // Принимаем все статусы }; // Добавляем аутентификацию if (endpoint.authentication) { this.addAxiosAuthentication(axiosConfig, endpoint.authentication); } axiosResponse = await axios(axiosConfig); } const response = { requestId: request.id, status: axiosResponse.status, statusText: axiosResponse.statusText || '', headers: axiosResponse.headers || {}, body: axiosResponse.data, duration: Date.now() - startTime, timestamp: new Date() }; if (axiosResponse.status >= 200 && axiosResponse.status < 300) { this.stats.successfulRequests++; } else { this.stats.failedRequests++; response.error = `HTTP ${axiosResponse.status}: ${axiosResponse.statusText}`; } this.stats.totalDataTransferred += JSON.stringify(response.body || '').length; return response; } catch (error) { this.stats.failedRequests++; const response = { requestId: request.id, status: 500, statusText: 'Internal Server Error', headers: {}, body: null, duration: Date.now() - startTime, timestamp: new Date(), error: error instanceof Error ? error.message : String(error) }; return response; } finally { this.pendingRequests.delete(request.id); } } addAuthentication(options, auth) { switch (auth.type) { case 'basic': { const basicAuth = btoa(`${auth.credentials.username}:${auth.credentials.password}`); options.headers = { ...options.headers, 'Authorization': `Basic ${basicAuth}` }; break; } case 'bearer': options.headers = { ...options.headers, 'Authorization': `Bearer ${auth.credentials.token}` }; break; case 'apikey': options.headers = { ...options.headers, [auth.credentials.headerName || 'X-API-Key']: auth.credentials.apikey }; break; } } addAxiosAuthentication(config, auth) { switch (auth.type) { case 'basic': config.auth = { username: auth.credentials.username, password: auth.credentials.password }; break; case 'bearer': config.headers['Authorization'] = `Bearer ${auth.credentials.token}`; break; case 'apikey': config.headers[auth.credentials.headerName || 'X-API-Key'] = auth.credentials.apikey; break; } } async queueOfflineRequest(request) { if (this.offlineQueue.length >= this.config.offlineQueueSize) { this.offlineQueue.shift(); // Удаляем самый старый запрос } this.offlineQueue.push(request); this.stats.offlineOperations++; // Возвращаем ответ о том, что запрос поставлен в очередь return { requestId: request.id, status: 202, statusText: 'Accepted (Queued for offline processing)', headers: {}, body: { queued: true, position: this.offlineQueue.length }, duration: 0, timestamp: new Date() }; } async getLocalData(config) { try { // Получаем локальные данные из CSElement registry const elements = []; // В реальной реализации здесь должен быть доступ к реестру элементов // Для демонстрации создаем тестовые данные for (let i = 0; i < 10; i++) { elements.push({ id: `local_element_${i}`, data: { value: i, syncConfig: config.id }, lastModified: new Date().toISOString() }); } return elements; } catch (error) { console.error('Ошибка получения локальных данных:', error); return []; } } async getRemoteData(endpoint, config) { try { // Выполняем HTTP запрос для получения удаленных данных const url = `${endpoint.url}/sync/${config.id}`; const axiosConfig = { method: 'GET', url, headers: endpoint.headers || {}, timeout: endpoint.timeout, validateStatus: () => true }; // Добавляем аутентификацию if (endpoint.authentication) { this.addAxiosAuthentication(axiosConfig, endpoint.authentication); } const response = await axios(axiosConfig); if (response.status >= 200 && response.status < 300) { return Array.isArray(response.data) ? response.data : []; } else { console.warn(`Ошибка получения удаленных данных: HTTP ${response.status}`); return []; } } catch (error) { console.error('Ошибка получения удаленных данных:', error); return []; } } async performFullSync(localData, remoteData, config, result) { try { // Создаем индексы для быстрого поиска const localIndex = new Map(localData.map(item => [item.id, item])); const remoteIndex = new Map(remoteData.map(item => [item.id, item])); // Обрабатываем удаленные элементы for (const remoteItem of remoteData) { const localItem = localIndex.get(remoteItem.id); if (!localItem) { // Создаем новый элемент console.log(`Создание элемента: ${remoteItem.id}`); result.elementsCreated++; } else { // Обновляем существующий элемент const remoteTime = new Date(remoteItem.lastModified || 0); const localTime = new Date(localItem.lastModified || 0); if (remoteTime > localTime) { console.log(`Обновление элемента: ${remoteItem.id}`); result.elementsUpdated++; } } result.elementsProcessed++; } // Обрабатываем локальные элементы, которых нет на удаленной стороне for (const localItem of localData) { if (!remoteIndex.has(localItem.id)) { if (config.bidirectional) { // Отправляем локальный элемент на сервер console.log(`Отправка элемента на сервер: ${localItem.id}`); result.elementsCreated++; } else { // Удаляем локальный элемент console.log(`Удаление локального элемента: ${localItem.id}`); result.elementsDeleted++; } } result.elementsProcessed++; } result.statistics.syncedElements = result.elementsProcessed; } catch (error) { result.errors.push(`Ошибка полной синхронизации: ${error}`); } } async performIncrementalSync(localData, remoteData, _config, result) { // Заглушка для инкрементальной синхронизации result.elementsProcessed = Math.min(localData.length, remoteData.length); result.statistics.syncedElements = result.elementsProcessed; } async performConflictResolution(_localData, _remoteData, _config, result) { // Заглушка для разрешения конфликтов const conflicts = Math.floor(Math.random() * 5); result.statistics.conflictedElements = conflicts; this.stats.conflictsResolved += conflicts; } async transmitMessage(connection, message) { // Заглушка для отправки сообщения const messageSize = JSON.stringify(message).length; connection.bytesTransferred += messageSize; this.stats.totalDataTransferred += messageSize; } setupNetworkMonitoring() { // Мониторинг состояния сети setInterval(() => { this.checkNetworkStatus(); }, 5000); // каждые 5 секунд } checkNetworkStatus() { // Заглушка для проверки состояния сети const wasOnline = this.isOnline; this.isOnline = true; // navigator.onLine в браузере if (!wasOnline && this.isOnline) { this.processOfflineQueue(); } } async processOfflineQueue() { this.log('info', `Обработка ${this.offlineQueue.length} отложенных запросов`); const requests = [...this.offlineQueue]; this.offlineQueue = []; for (const request of requests) { try { await this.executeRequest(request); } catch (error) { this.log('error', `Ошибка обработки отложенного запроса: ${error}`); } } } startAutoSync() { this.syncTimer = setInterval(() => { this.runScheduledSyncs(); }, 60000); // проверка каждую минуту } async runScheduledSyncs() { const now = new Date(); for (const config of this.syncConfigs.values()) { if (!config.enabled || !config.nextSync) continue; if (now >= config.nextSync) { try { await this.syncData(config.id); } catch (error) { this.log('error', `Ошибка автосинхронизации ${config.name}: ${error}`); } } } } startHeartbeat() { this.heartbeatTimer = setInterval(() => { this.sendHeartbeats(); }, this.config.heartbeatInterval); } async sendHeartbeats() { for (const connection of this.connections.values()) { if (connection.status === NetworkStatus.CONNECTED) { try { await this.sendMessage(connection.id, { connectionId: connection.id, type: 'request', operation: NetworkOperation.HEARTBEAT, payload: { timestamp: new Date().toISOString() }, priority: 0, requiresAck: false }); } catch (error) { this.log('warn', `Ошибка heartbeat для соединения ${connection.id}: ${error}`); } } } } startRequestProcessor() { setInterval(() => { this.processRequestQueue(); }, 1000); // каждую секунду } async processRequestQueue() { if (this.requestQueue.length === 0) return; // Сортировка по приоритету this.requestQueue.sort((a, b) => b.priority - a.priority); const request = this.requestQueue.shift(); if (request) { try { await this.executeRequest(request); } catch (error) { this.log('error', `Ошибка обработки запроса из очереди: ${error}`); } } } calculateNextSync(interval) { return new Date(Date.now() + interval); } emitEvent(type, data) { if (!this.config.enableEventLogging) return; const event = { id: this.generateId(), type, data, timestamp: new Date() }; this.events.push(event); // Ограничение размера лога событий if (this.events.length > 1000) { this.events = this.events.slice(-1000); } } updateStats() { this.stats.uptime = Date.now() - (this.stats.lastSyncTime?.getTime() || Date.now()); if (this.stats.totalRequests > 0) { this.stats.errorRate = this.stats.failedRequests / this.stats.totalRequests; } // Обновление средней задержки const activeConnections = Array.from(this.connections.values()) .filter(conn => conn.status === NetworkStatus.CONNECTED); if (activeConnections.length > 0) { this.stats.averageLatency = activeConnections .reduce((sum, conn) => sum + conn.latency, 0) / activeConnections.length; } } log(level, message) { if (this.shouldLog(level)) { console.log(`[${this.name}] ${level.toUpperCase()}: ${message}`); } } shouldLog(level) { const levels = ['debug', 'info', 'warn', 'error']; const configLevel = levels.indexOf(this.config.logLevel); const messageLevel = levels.indexOf(level); return messageLevel >= configLevel; } generateId() { return Date.now().toString(36) + Math.random().toString(36).substr(2); } // Публичные методы для получения данных getStats() { return { ...this.stats }; } getAllEndpoints() { return Array.from(this.endpoints.values()); } getAllSyncConfigs() { return Array.from(this.syncConfigs.values()); } getActiveConnections() { return Array.from(this.connections.values()) .filter(conn => conn.status === NetworkStatus.CONNECTED); } getAllConnections() { return Array.from(this.connections.values()); } getPendingRequests() { return Array.from(this.pendingRequests.values()); } getRequestQueue() { return [...this.requestQueue]; } getOfflineQueue() { return [...this.offlineQueue]; } getSyncResults() { return [...this.syncResults]; } getEvents() { return [...this.events]; } isNetworkOnline() { return this.isOnline; } getConnectionStatus(connectionId) { return this.connections.get(connectionId)?.status; } } exports.NetworkPlugin = NetworkPlugin; //# sourceMappingURL=network.js.map