UNPKG

@baguskto/saham

Version:

MCP Server untuk data saham Indonesia (IDX) - Implementasi Node.js/TypeScript

152 lines 5.26 kB
"use strict"; /** * Base data source implementation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DataSourceManager = exports.DataSource = void 0; const logger_1 = require("../utils/logger"); class DataSource { name; priority; timeout; stats; constructor(name, priority, timeout = 10000) { this.name = name; this.priority = priority; this.timeout = timeout; this.stats = { name, priority, successCount: 0, errorCount: 0, averageResponseTime: 0, isHealthy: true }; } async makeRequest(requestName, requestFn) { const startTime = Date.now(); try { logger_1.logger.debug(`${this.name}: Starting ${requestName}`); const result = await Promise.race([ requestFn(), this.createTimeoutPromise() ]); const responseTime = Date.now() - startTime; this.updateStats(true, responseTime); logger_1.logger.debug(`${this.name}: ${requestName} completed in ${responseTime}ms`); this.stats.lastRequest = new Date(); return result; } catch (error) { const responseTime = Date.now() - startTime; this.updateStats(false, responseTime); logger_1.logger.error(`${this.name}: ${requestName} failed in ${responseTime}ms:`, error); this.stats.lastRequest = new Date(); return null; } } createTimeoutPromise() { return new Promise((_, reject) => { setTimeout(() => { reject(new Error(`${this.name}: Request timeout after ${this.timeout}ms`)); }, this.timeout); }); } updateStats(success, responseTime) { if (success) { this.stats.successCount++; } else { this.stats.errorCount++; } // Update average response time const totalRequests = this.stats.successCount + this.stats.errorCount; this.stats.averageResponseTime = (this.stats.averageResponseTime * (totalRequests - 1) + responseTime) / totalRequests; // Update health status (consider unhealthy if error rate > 50% in last 10 requests) const recentErrorRate = this.stats.errorCount / Math.max(totalRequests, 10); this.stats.isHealthy = recentErrorRate <= 0.5; } getStats() { return { ...this.stats }; } getName() { return this.name; } getPriority() { return this.priority; } isHealthy() { return this.stats.isHealthy; } } exports.DataSource = DataSource; class DataSourceManager { sources = []; addSource(source) { this.sources.push(source); this.sources.sort((a, b) => b.getPriority() - a.getPriority()); logger_1.logger.info(`Added data source: ${source.getName()} (priority: ${source.getPriority()})`); } async getStockInfo(ticker) { return this.tryDataSources(source => source.getStockInfo(ticker)); } async getMarketOverview() { return this.tryDataSources(source => source.getMarketOverview()); } async getHistoricalData(ticker, period) { return this.tryDataSources(source => source.getHistoricalData(ticker, period)); } async getSectorPerformance() { return this.tryDataSources(source => source.getSectorPerformance()); } async searchStocks(query) { const results = await this.tryDataSources(source => source.searchStocks(query)); return results || []; } async tryDataSources(operation) { // Filter to only healthy sources const healthySources = this.sources.filter(source => source.isHealthy()); if (healthySources.length === 0) { logger_1.logger.warn('No healthy data sources available, trying all sources'); // If no healthy sources, try all sources as fallback for (const source of this.sources) { try { const result = await operation(source); if (result) { return result; } } catch (error) { logger_1.logger.debug(`Data source ${source.getName()} failed:`, error); } } return null; } // Try healthy sources in priority order for (const source of healthySources) { try { const result = await operation(source); if (result) { return result; } } catch (error) { logger_1.logger.debug(`Data source ${source.getName()} failed:`, error); } } return null; } getStats() { const stats = {}; this.sources.forEach(source => { stats[source.getName()] = source.getStats(); }); return stats; } getHealthySources() { return this.sources.filter(source => source.isHealthy()); } } exports.DataSourceManager = DataSourceManager; //# sourceMappingURL=base.js.map