import { BaseStore } from './base-store.js'; import { StoreInMemory } from './store-inmemory.js'; /** * Storage Adapter that takes another Storage Adapter (e.g. IndexedDB) and "syncs" its contents to system memory for faster read access. * * All read operations will use system memory - all write operations will use the provided adapter. */ export class StoreInMemorySynced extends BaseStore { constructor( private inMemoryCache: StoreInMemory, private store: BaseStore, ) { super(); // Hook into all write operations so that we can sync the In-Memory cache. this.store.on('set', async (payload) => { await this.inMemoryCache.set(payload.key, payload.value); this.emit('set', { key: payload.key, value: payload.value }); }); this.store.on('delete', async (payload) => { await this.inMemoryCache.delete(payload.key); this.emit('delete', { key: payload.key }); }); this.store.on('clear', async () => { await this.inMemoryCache.clear(); this.emit('clear', undefined); }); } static async create(store: BaseStore) { // Instantiate in-memory cache and IndexedDB. const inMemoryCache = new StoreInMemory(); // Create instance of this store. const memorySyncedStore = new this(inMemoryCache, store); // Sync the data from IndexedDB into the In-Memory cache. const indexedDBEntries = await store.all(); for (let [key, value] of Object.entries(indexedDBEntries)) { inMemoryCache.set(key, value); } // Return our instance of this store. return memorySyncedStore; } async all() { return await this.inMemoryCache.all(); } async keys() { return await this.inMemoryCache.keys(); } async get(key: string) { // TODO: Consider whether you want to structuredClone this to prevent mutation. return await this.inMemoryCache.get(key); } async set(key: string, value: T) { await this.store.set(key, value); } async delete(key: string) { await this.store.delete(key); } async clear() { await this.store.clear(); } async has(key: string) { return await this.inMemoryCache.has(key); } async close() { await this.store.close(); } }