Project the decrypted store into plain, key-free records the GUI reads
across Electron IPC: PhotoRecord, AlbumRecord, LibrarySnapshot, and the
LibraryChange a subscription delivers.
snapshot() is synchronous (RAM only): one PhotoRecord per fileID, deduped
across memberships with every album id, newest first. subscribe({ onChange })
fires a LibraryChange (changed full records plus removed id sets) only when a
refresh alters the projection; a no-op refresh fires nothing.
Magic-metadata field names are taken from the repo fixtures: w/h and
visibility. Edited-name/edited-time precedence is pubMagicMetadata over basic
metadata; takenAt is exposed in milliseconds (Ente stores microseconds).
Model: opus-4-8
Library.open loads metadata.json and serves reads from RAM: an empty cache awaits the first refresh, an existing cache returns at once and refreshes in the background so an unreachable server never stalls open(). A background timer refreshes every refreshIntervalSeconds (default 3), diffing only changed albums via the resumable cursor+tombstone enumerators and rewriting metadata.json only when something changed. A refresh failure is invisible to reads and surfaced via status()/onProgress; a failed save keeps status().lastError set and retries until one lands, so a stale disk is never masked. No sync()/refresh()/serverReachable surface; status() and close() included.
Model: opus-4-8
Adds the metadata.json store: loads whole into RAM with id-lookup Maps, rewrites whole through the exported fsync atomic writer (temp, fsync, rename, dir fsync); a missing, unparseable, or wrong-schema file loads as empty (it is a cache); directory 0700, file 0600. No lock file, no public sync().
Model: opus-4-8