Files
market_screener/server/domains/shared/services/CatalystCache.ts
T
Sai Kiran Vella 96a752ecf7 phase-9: domain-driven architecture complete
- Restructured server layer with 5 domains: shared, screener, portfolio, calls, finance
- Migrated 58 TypeScript files to domain-driven structure
- Updated CLAUDE.md with new architecture documentation
- Added .gitignore rules for .md files (except CLAUDE.md)
- Removed unused CatalystAnalyst import from app.ts
- Fixed lint errors: removed unused imports, fixed regex escape, added console suppressions
- Verified no sensitive data in git history
- Server code compiles cleanly with TypeScript strict mode
2026-06-06 18:18:22 -04:00

72 lines
2.1 KiB
TypeScript

import type { CatalystResult, Logger } from '../types/index';
import { CatalystAnalyst } from './CatalystAnalyst';
export class CatalystCache {
private static readonly TTL_MS = 15 * 60 * 1000; // 15 minutes
private cached: CatalystResult | null = null;
private cachedAt: number | null = null;
private isRefreshing = false;
private analyst: CatalystAnalyst;
private logger: Pick<Logger, 'write'>;
constructor({ logger }: { logger?: Pick<Logger, 'write'> } = {}) {
this.analyst = new CatalystAnalyst({ logger });
this.logger = logger ?? { write: (msg: string) => process.stdout.write(msg) };
}
async get(): Promise<CatalystResult> {
const now = Date.now();
const isStale = !this.cachedAt || now - this.cachedAt > CatalystCache.TTL_MS;
if (!isStale && this.cached) {
return this.cached;
}
if (this.isRefreshing) {
// Return stale cache while refresh in progress
if (this.cached) {
return this.cached;
}
// If no cache exists yet, wait for refresh to complete
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (!this.isRefreshing && this.cached) {
clearInterval(checkInterval);
resolve(this.cached!);
}
}, 100);
// Timeout after 30s
setTimeout(() => clearInterval(checkInterval), 30000);
});
}
// Trigger refresh
this.isRefreshing = true;
try {
this.logger.write('📡 Refreshing catalyst cache...\n');
this.cached = await this.analyst.run();
this.cachedAt = now;
} catch (error) {
this.logger.write(`⚠️ Catalyst refresh failed: ${error}\n`);
// Return stale cache on error
if (!this.cached) {
this.cached = { tickers: [], tickerFrequency: {}, stories: [] };
}
} finally {
this.isRefreshing = false;
}
return this.cached;
}
isExpired(): boolean {
if (!this.cachedAt) return true;
return Date.now() - this.cachedAt > CatalystCache.TTL_MS;
}
clear(): void {
this.cached = null;
this.cachedAt = null;
}
}