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
This commit is contained in:
committed by
saikiranvella
parent
83116baa3c
commit
96a752ecf7
@@ -0,0 +1,71 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user