/// // The IndexedDB database where pi-connect keeps credentials, model catalogs and auth documents. export type StoreName = "credentials" | "models" | "documents"; const STORES: readonly StoreName[] = ["credentials", "models", "documents"]; const DATABASE_VERSION = 1; /** One open pi-connect database: key-value stores of structured-cloned values. */ export class ConnectDatabase { private readonly db: IDBDatabase; private constructor(db: IDBDatabase) { this.db = db; } /** Open (and on first use create) the database `name`. */ static open(name: string, factory: IDBFactory = indexedDB): Promise { const request = factory.open(name, DATABASE_VERSION); request.onupgradeneeded = () => { for (const store of STORES) { if (!request.result.objectStoreNames.contains(store)) request.result.createObjectStore(store); } }; return settle(request).then((db) => new ConnectDatabase(db)); } /** Read one value. */ get(store: StoreName, key: string): Promise { return settle(this.db.transaction(store, "readonly").objectStore(store).get(key)); } /** List the keys of a store. */ async keys(store: StoreName): Promise { const keys = await settle(this.db.transaction(store, "readonly").objectStore(store).getAllKeys()); return keys.map(String); } /** Write one value; resolves once the write is committed. */ put(store: StoreName, key: string, value: unknown): Promise { return this.write(store, (objects) => objects.put(value, key)); } /** Delete one value; resolves once the deletion is committed. */ delete(store: StoreName, key: string): Promise { return this.write(store, (objects) => objects.delete(key)); } /** Close the connection. */ close(): void { this.db.close(); } private write(store: StoreName, change: (objects: IDBObjectStore) => void): Promise { const transaction = this.db.transaction(store, "readwrite"); change(transaction.objectStore(store)); return new Promise((resolve, reject) => { transaction.oncomplete = () => resolve(); transaction.onerror = () => reject(transaction.error); transaction.onabort = () => reject(transaction.error ?? new Error("IndexedDB transaction aborted")); }); } } function settle(request: IDBRequest): Promise { return new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); }