/** * In-memory EvidenceLink repository. * * Implements the storage layer for `src/binder/services/bindings.ts`. The * repo is deliberately the same shape as the engine's MVP repos so the * future durable-store ADR (see ADR-0005) replaces it with the same * interface. * * Indexed by `(targetType, targetId)` and by `evidenceItemId` for the * two query directions the binder needs: "all evidence for this field" * and "all targets this evidence is linked to". */ import type { EvidenceLink, EvidenceTarget } from "@shared/evidence-link"; import type { EvidenceItemId, EvidenceLinkId } from "@shared/ids"; export interface EvidenceLinkRepository { create(link: EvidenceLink): EvidenceLink; get(id: EvidenceLinkId): EvidenceLink | null; update(link: EvidenceLink): EvidenceLink; delete(id: EvidenceLinkId): boolean; listForTarget(target: EvidenceTarget): readonly EvidenceLink[]; listForEvidenceItem(id: EvidenceItemId): readonly EvidenceLink[]; list(): readonly EvidenceLink[]; } function targetKey(target: EvidenceTarget): string { return `${target.targetType}${target.targetId}`; } export function createInMemoryLinkRepo(): EvidenceLinkRepository { const byId = new Map(); return { create(link) { if (byId.has(link.id)) { throw new Error(`EvidenceLinkRepository.create: duplicate id ${link.id}`); } byId.set(link.id, link); return link; }, get(id) { return byId.get(id) ?? null; }, update(link) { if (!byId.has(link.id)) { throw new Error(`EvidenceLinkRepository.update: unknown id ${link.id}`); } byId.set(link.id, link); return link; }, delete(id) { return byId.delete(id); }, listForTarget(target) { const key = targetKey(target); const out: EvidenceLink[] = []; for (const link of byId.values()) { if (targetKey({ targetType: link.targetType, targetId: link.targetId }) === key) { out.push(link); } } return out; }, listForEvidenceItem(id) { const out: EvidenceLink[] = []; for (const link of byId.values()) { if (link.evidenceItemId === id) out.push(link); } return out; }, list() { return [...byId.values()]; }, }; }