@baguskto/saham
Version:
MCP Server untuk data saham Indonesia (IDX) - Implementasi Node.js/TypeScript
152 lines • 5.26 kB
JavaScript
;
/**
* 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