Files
Codewalkers/apps/server/review/diff-cache.ts
Lukas May 0996073deb feat: add in-memory diff cache with TTL and commit-hash invalidation
Adds DiffCache<T> module, extends BranchManager with getHeadCommitHash,
and wires phase-level caching into getPhaseReviewDiff and getFileDiff.
Cache is invalidated in ExecutionOrchestrator after each task merges into
the phase branch, ensuring stale diffs are never served after new commits.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 19:51:04 +01:00

68 lines
1.9 KiB
TypeScript

/**
* DiffCache — in-memory cache with TTL and prefix-based invalidation.
* Used to avoid re-running expensive git diff subprocesses on repeated requests.
*/
import type { FileStatEntry } from '../git/types.js';
interface CacheEntry<T> {
value: T;
expiresAt: number;
}
export class DiffCache<T> {
private store = new Map<string, CacheEntry<T>>();
private ttlMs: number;
constructor(ttlMs: number) {
this.ttlMs = ttlMs;
}
get(key: string): T | undefined {
const entry = this.store.get(key);
if (!entry) return undefined;
if (Date.now() > entry.expiresAt) {
this.store.delete(key);
return undefined;
}
return entry.value;
}
set(key: string, value: T): void {
this.store.set(key, { value, expiresAt: Date.now() + this.ttlMs });
}
invalidateByPrefix(prefix: string): void {
for (const key of this.store.keys()) {
if (key.startsWith(prefix)) this.store.delete(key);
}
}
}
// ---------------------------------------------------------------------------
// Response shapes (mirror the return types of getPhaseReviewDiff / getFileDiff)
// ---------------------------------------------------------------------------
export interface PhaseMetaResponse {
phaseName: string;
sourceBranch: string;
targetBranch: string;
files: FileStatEntry[];
totalAdditions: number;
totalDeletions: number;
}
export interface FileDiffResponse {
binary: boolean;
rawDiff: string;
}
// ---------------------------------------------------------------------------
// Singleton instances — TTL is read once at module load time
// ---------------------------------------------------------------------------
const TTL = parseInt(process.env.REVIEW_DIFF_CACHE_TTL_MS ?? '300000', 10);
export const phaseMetaCache = new DiffCache<PhaseMetaResponse>(TTL);
export const fileDiffCache = new DiffCache<FileDiffResponse>(TTL);