clone

a personal forge on plain git: static pages, stacked-diff reviews, signed local ci, repo sites and releases

main
forge / lib / cache.ts
cache.ts 1.9K
1// Per-repo build cache: facts keyed by immutable git object ids (commit2// metadata, range-diffs, signature checks) and the fingerprint of every page3// the last build wrote. Lives in $FORGE_STATE/cache/repos/<owner>/<repo>.json.4import fs from 'node:fs';5import path from 'node:path';6import type { CommitMeta, Config, SiteDeploy } from './types.ts';78type Sig = Pick<SiteDeploy, 'signed' | 'signer'>;910interface CacheData {11 commits?: Record<string, CommitMeta>;12 ranges?: Record<string, unknown>;13 sigs?: Record<string, Sig>;14 sigsKey?: string | null;15 pages?: { code: string; fps: Record<string, string> } | null;16}1718export class RepoCache {19 file: string;20 commits: Record<string, CommitMeta>;21 ranges: Record<string, unknown>;22 sigs: Record<string, Sig>;23 sigsKey: string | null;24 pages: { code: string; fps: Record<string, string> } | null;2526 constructor(file: string, data: CacheData) {27 this.file = file;28 this.commits = data.commits ?? {};29 this.ranges = data.ranges ?? {};30 this.sigs = data.sigs ?? {};31 this.sigsKey = data.sigsKey ?? null;32 this.pages = data.pages ?? null;33 }3435 static load(cfg: Config, slug: string): RepoCache {36 const file = path.join(cfg.cacheDir, 'repos', `${slug}.json`);37 let data: CacheData = {};38 try {39 data = JSON.parse(fs.readFileSync(file, 'utf8'));40 } catch {}41 return new RepoCache(file, data);42 }4344 // Signature checks depend on who is allowed to sign; start over when that changes.45 useSigners(key: string): void {46 if (this.sigsKey !== key) {47 this.sigs = {};48 this.sigsKey = key;49 }50 }5152 save(): void {53 fs.mkdirSync(path.dirname(this.file), { recursive: true });54 const tmp = `${this.file}.${process.pid}`;55 fs.writeFileSync(tmp, JSON.stringify({ commits: this.commits, ranges: this.ranges, sigs: this.sigs, sigsKey: this.sigsKey, pages: this.pages }));56 fs.renameSync(tmp, this.file);57 }58}