Creates diff-cache.ts module with generic DiffCache<T> class (TTL, prefix invalidation, env-var configuration) and exports phaseMetaCache / fileDiffCache singletons. Wires cache into getPhaseReviewDiff via getHeadCommitHash on BranchManager. Adds 6 unit tests for DiffCache and 5 integration tests verifying cache hit/miss behaviour, prefix invalidation, and NOT_FOUND guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
/**
|
|
* DiffCache — in-memory TTL cache for git diff results.
|
|
*
|
|
* Keyed by `phaseId:headHash` (or `phaseId:headHash:filePath` for per-file diffs).
|
|
* TTL defaults to 5 minutes, configurable via REVIEW_DIFF_CACHE_TTL_MS env var.
|
|
* Prefix-based invalidation clears all entries for a phase when a new commit lands.
|
|
*/
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
const TTL = parseInt(process.env.REVIEW_DIFF_CACHE_TTL_MS ?? '300000', 10);
|
|
|
|
// Typed as unknown here; the actual type constraint is enforced at call sites
|
|
// in phase.ts where PhaseMetaResponse / FileDiffResponse are inferred.
|
|
export const phaseMetaCache = new DiffCache<unknown>(TTL);
|
|
export const fileDiffCache = new DiffCache<unknown>(TTL);
|