Files
eranos/src/lib/db.ts
T
Alex Gleason 2ecd557430 Fix IndexedDB crash on iOS Lockdown Mode
openDatabase() now catches errors from idb's openDB() (which throws
synchronously when indexedDB is undefined) and returns null. All
consumers — profileCache, nip05Cache, dmMessageStore — check for null
and silently degrade to in-memory only.

The DM message store also stops re-throwing errors, which previously
could produce unhandled rejections in DMProvider.
2026-04-06 13:41:32 -05:00

59 lines
1.9 KiB
TypeScript

import { openDB, type IDBPDatabase } from 'idb';
// ============================================================================
// Unified IndexedDB database for Ditto.
//
// All persistent client-side data lives in a single "ditto" database with
// one object store per data domain. Callers should import `openDatabase()`
// rather than managing their own `openDB` calls.
//
// When IndexedDB is unavailable (e.g. iOS Lockdown Mode, certain private-
// browsing modes) every function in this module still works — callers get
// `null` instead of a database handle and should skip persistence silently.
// ============================================================================
const DB_NAME = 'ditto';
const DB_VERSION = 1;
/** Store names — keep in sync with the `upgrade` callback below. */
export const STORE = {
NIP05: 'nip05',
PROFILES: 'profiles',
MESSAGES: 'messages',
} as const;
let dbPromise: Promise<IDBPDatabase | null> | null = null;
/**
* Open (or reuse) the shared Ditto database.
*
* Returns `null` when IndexedDB is not available (e.g. iOS Lockdown Mode,
* some private-browsing contexts). The result is cached for the page
* lifetime so the availability check runs only once.
*/
export function openDatabase(): Promise<IDBPDatabase | null> {
if (!dbPromise) {
dbPromise = (async () => {
try {
return await openDB(DB_NAME, DB_VERSION, {
upgrade(db) {
if (!db.objectStoreNames.contains(STORE.NIP05)) {
db.createObjectStore(STORE.NIP05);
}
if (!db.objectStoreNames.contains(STORE.PROFILES)) {
db.createObjectStore(STORE.PROFILES);
}
if (!db.objectStoreNames.contains(STORE.MESSAGES)) {
db.createObjectStore(STORE.MESSAGES);
}
},
});
} catch {
// IndexedDB is unavailable — degrade to in-memory only.
return null;
}
})();
}
return dbPromise;
}