perf(registratura): lightweight API mode strips base64 attachments from list

ROOT CAUSE: RegistryEntry stores file attachments as base64 strings in JSON.
A single 5MB PDF becomes ~6.7MB of base64. With 6 entries, the exportAll()
endpoint was sending 30-60MB of JSON on every page load  taking 2+ minutes.

Fix: Added ?lightweight=true parameter to /api/storage GET endpoint.
When enabled, stripHeavyFields() recursively removes large 'data' and
'fileData' string fields (>1KB) from JSON values, replacing with '__stripped__'.

Changes:
- /api/storage route.ts: stripHeavyFields() + lightweight query param
- StorageService.export(): accepts { lightweight?: boolean } option
- DatabaseStorageAdapter.export(): passes lightweight flag to API
- LocalStorageAdapter.export(): accepts option (no-op, localStorage is fast)
- useStorage.exportAll(): passes options through
- registry-service.ts: getAllEntries() uses lightweight=true by default
- registry-service.ts: new getFullEntry() loads single entry with full data
- use-registry.ts: exports loadFullEntry() for on-demand full loading
- registratura-module.tsx: handleEdit/handleNavigateEntry load full entry

Result: List loading transfers ~100KB instead of 30-60MB. Editing loads
full data for a single entry on demand (~5-10MB for one entry vs all).
This commit is contained in:
AI Assistant
2026-02-27 22:37:39 +02:00
parent db9bcd7192
commit c22848b471
8 changed files with 128 additions and 39 deletions
@@ -8,18 +8,20 @@ export interface RegistryStorage {
set<T>(key: string, value: T): Promise<void>;
delete(key: string): Promise<void>;
list(): Promise<string[]>;
exportAll(): Promise<Record<string, unknown>>;
exportAll(options?: {
lightweight?: boolean;
}): Promise<Record<string, unknown>>;
}
/**
* Load all registry entries in a SINGLE request.
* Uses exportAll() which fetches namespace data in one HTTP call,
* avoiding the N+1 pattern (list keys → get each one individually).
* Load all registry entries in a SINGLE lightweight request.
* Uses exportAll({ lightweight: true }) which strips base64 attachment data
* server-side, reducing payload from potentially 30-60MB to <100KB.
*/
export async function getAllEntries(
storage: RegistryStorage,
): Promise<RegistryEntry[]> {
const all = await storage.exportAll();
const all = await storage.exportAll({ lightweight: true });
const entries: RegistryEntry[] = [];
for (const [key, value] of Object.entries(all)) {
if (key.startsWith(STORAGE_PREFIX) && value) {
@@ -30,6 +32,16 @@ export async function getAllEntries(
return entries;
}
/**
* Load a single full entry (with attachment data) for editing.
*/
export async function getFullEntry(
storage: RegistryStorage,
id: string,
): Promise<RegistryEntry | null> {
return storage.get<RegistryEntry>(`${STORAGE_PREFIX}${id}`);
}
export async function saveEntry(
storage: RegistryStorage,
entry: RegistryEntry,