Files
portal/app/vite-plugins/dev-indices/manifest.ts
T

68 lines
2.1 KiB
TypeScript

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];
}