import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'node:fs'; import { dirname } from 'node:path'; import type { Manifest, ManifestEntry } from './types'; export function createEmpty(): Manifest { return { $schema: './dev-indices.schema.json', version: 1, lastId: 0, entries: {}, deleted: {}, }; } export function loadManifest(path: string): Manifest { if (!existsSync(path)) return createEmpty(); try { const raw = readFileSync(path, 'utf8'); const parsed = JSON.parse(raw) as Manifest; if (parsed.version !== 1) { throw new Error(`dev-indices manifest version ${parsed.version} unsupported (expected 1)`); } parsed.entries ??= {}; parsed.deleted ??= {}; return parsed; } catch (err) { if (err instanceof SyntaxError) { throw new Error(`dev-indices manifest at ${path} is corrupt JSON: ${err.message}`); } throw err; } } export function saveManifest(path: string, manifest: Manifest): void { mkdirSync(dirname(path), { recursive: true }); const tmp = path + '.tmp'; const json = JSON.stringify(manifest, null, 2); writeFileSync(tmp, json, 'utf8'); renameSync(tmp, path); } export function findBySignature(manifest: Manifest, signature: string): number | null { for (const [id, entry] of Object.entries(manifest.entries)) { if (entry.signature === signature) return Number(id); } return null; } export function addEntry(manifest: Manifest, entry: ManifestEntry): number { const id = manifest.lastId + 1; manifest.lastId = id; manifest.entries[String(id)] = entry; return id; } export function markDeleted(manifest: Manifest, id: number): void { const key = String(id); const entry = manifest.entries[key]; // no-op if id was never added or already deleted — defensive, callers should not rely on this if (!entry) return; manifest.deleted[key] = { lastSignature: entry.signature, lastFile: entry.file, deletedAt: new Date().toISOString(), }; delete manifest.entries[key]; }