From 31064d2324dafd37cf2a834de471150652170acb Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Wed, 2 Apr 2025 17:14:04 -0700 Subject: [PATCH] Simplify storage adapters --- packages/app/src/collection.ts | 20 +++- packages/app/src/handles.ts | 1 + packages/app/src/relays.ts | 1 + packages/app/src/storage.ts | 196 ++------------------------------- packages/app/src/zappers.ts | 1 + 5 files changed, 28 insertions(+), 191 deletions(-) diff --git a/packages/app/src/collection.ts b/packages/app/src/collection.ts index 5462a25..f658ee7 100644 --- a/packages/app/src/collection.ts +++ b/packages/app/src/collection.ts @@ -1,5 +1,5 @@ -import {readable, derived, type Readable} from "svelte/store" -import {indexBy, type Maybe, now} from "@welshman/lib" +import {readable, derived, type Readable, type Subscriber} from "svelte/store" +import {indexBy, remove, type Maybe, now} from "@welshman/lib" import {withGetter} from "@welshman/store" import {getFreshness, setFreshnessThrottled} from "./freshness.js" @@ -18,6 +18,8 @@ export const collection = ({ const pending = new Map>>() const loadAttempts = new Map() + let subscribers: Subscriber[] = [] + const loadItem = async (key: string, ...args: LoadArgs) => { const stale = indexStore.get().get(key) @@ -65,6 +67,10 @@ export const collection = ({ if (fresh) { loadAttempts.delete(key) + + for (const subscriber of subscribers) { + subscriber(fresh) + } } return fresh @@ -82,5 +88,13 @@ export const collection = ({ return derived(indexStore, $index => $index.get(key)) } - return {indexStore, deriveItem, loadItem} + const onItem = (cb: Subscriber) => { + subscribers.push(cb) + + return () => { + subscribers = remove(cb, subscribers) + } + } + + return {indexStore, deriveItem, loadItem, onItem} } diff --git a/packages/app/src/handles.ts b/packages/app/src/handles.ts index 4ac3f74..ea6507c 100644 --- a/packages/app/src/handles.ts +++ b/packages/app/src/handles.ts @@ -82,6 +82,7 @@ export const { indexStore: handlesByNip05, deriveItem: deriveHandle, loadItem: loadHandle, + onItem: onHandle, } = collection({ name: "handles", store: handles, diff --git a/packages/app/src/relays.ts b/packages/app/src/relays.ts index b00212f..2a1a017 100644 --- a/packages/app/src/relays.ts +++ b/packages/app/src/relays.ts @@ -85,6 +85,7 @@ export const { indexStore: relaysByUrl, deriveItem: deriveRelay, loadItem: loadRelay, + onItem: onRelay, } = collection({ name: "relays", store: relays, diff --git a/packages/app/src/storage.ts b/packages/app/src/storage.ts index 6690ac7..82737cc 100644 --- a/packages/app/src/storage.ts +++ b/packages/app/src/storage.ts @@ -2,7 +2,7 @@ import {openDB, deleteDB} from "idb" import {IDBPDatabase} from "idb" import {writable} from "svelte/store" import {Unsubscriber, Writable} from "svelte/store" -import {indexBy, equals, throttle, fromPairs} from "@welshman/lib" +import {indexBy, call, equals, throttle, fromPairs} from "@welshman/lib" import {TrustedEvent} from "@welshman/util" import {Repository} from "@welshman/relay" import {Tracker} from "@welshman/net" @@ -15,8 +15,8 @@ export type StorageAdapterOptions = { export type StorageAdapter = { keyPath: string - store: Writable - options: StorageAdapterOptions + init: () => Promise + sync: () => Unsubscriber } export let db: IDBPDatabase | undefined @@ -60,41 +60,6 @@ export const bulkDelete = async (name: string, ids: string[]) => { await tx.done } -export const initIndexedDbAdapter = async (name: string, adapter: StorageAdapter) => { - let prevRecords = await getAll(name) - - adapter.store.set(prevRecords) - - setTimeout(() => { - adapter.store.subscribe(async (currentRecords: any[]) => { - if (dead.get()) { - return - } - - const currentIds = new Set(currentRecords.map(item => item[adapter.keyPath])) - const removedRecords = prevRecords.filter(r => !currentIds.has(r[adapter.keyPath])) - - const prevRecordsById = indexBy(item => item[adapter.keyPath], prevRecords) - const updatedRecords = currentRecords.filter( - r => !equals(r, prevRecordsById.get(r[adapter.keyPath])), - ) - - prevRecords = currentRecords - - if (updatedRecords.length > 0) { - await bulkPut(name, updatedRecords) - } - - if (removedRecords.length > 0) { - await bulkDelete( - name, - removedRecords.map(item => item[adapter.keyPath]), - ) - } - }) - }, adapter.options.throttle || 0) -} - export const initStorage = async ( name: string, version: number, @@ -128,9 +93,11 @@ export const initStorage = async ( }, }) - await Promise.all( - Object.entries(adapters).map(([name, config]) => initIndexedDbAdapter(name, config)), - ) + await Promise.all(Object.values(adapters).map(adapter => adapter.init())) + + const unsubscribers = Object.values(adapters).map(adapter => adapter.sync()) + + return () => unsubscribers.forEach(call) } export const closeStorage = async () => { @@ -146,150 +113,3 @@ export const clearStorage = async () => { db = undefined // force initStorage to run again in tests } } - -const migrate = (data: any[], options: StorageAdapterOptions) => - options.migrate ? options.migrate(data) : data - -export const storageAdapters = { - fromCollectionStore: ( - keyPath: string, - store: Writable, - options: StorageAdapterOptions = {}, - ) => ({ - options, - keyPath, - store: throttled(options.throttle || 0, store), - }), - fromObjectStore: ( - store: Writable>, - options: StorageAdapterOptions = {}, - ) => ({ - options, - keyPath: "key", - store: adapter({ - store: throttled(options.throttle || 0, store), - forward: (data: Record) => - migrate( - Object.entries(data).map(([key, value]) => ({key, value})), - options, - ), - backward: (data: {key: string; value: T}[]) => - fromPairs(data.map(({key, value}) => [key, value])), - }), - }), - fromMapStore: (store: Writable>, options: StorageAdapterOptions = {}) => ({ - options, - keyPath: "key", - store: adapter({ - store: throttled(options.throttle || 0, store), - forward: (data: Map) => - migrate( - Array.from(data.entries()).map(([key, value]) => ({key, value})), - options, - ), - backward: (data: {key: string; value: T}[]) => - new Map(data.map(({key, value}) => [key, value])), - }), - }), - fromTracker: (tracker: Tracker, options: StorageAdapterOptions = {}) => ({ - options, - keyPath: "key", - store: custom( - setter => { - let onUpdate = () => - setter( - migrate( - Array.from(tracker.relaysById.entries()).map(([key, urls]) => ({ - key, - value: Array.from(urls), - })), - options, - ), - ) - - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } - - onUpdate() - tracker.on("update", onUpdate) - - return () => tracker.off("update", onUpdate) - }, - { - set: (data: {key: string; value: string[]}[]) => - tracker.load(new Map(data.map(({key, value}) => [key, new Set(value)]))), - }, - ), - }), - fromRepository: (repository: Repository, options: StorageAdapterOptions = {}) => ({ - options, - keyPath: "id", - store: custom( - setter => { - let onUpdate = () => setter(migrate(repository.dump(), options)) - - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } - - onUpdate() - repository.on("update", onUpdate) - - return () => repository.off("update", onUpdate) - }, - { - set: (events: TrustedEvent[]) => repository.load(events), - }, - ), - }), - fromRepositoryAndTracker: ( - repository: Repository, - tracker: Tracker, - options: StorageAdapterOptions = {}, - ) => ({ - options, - keyPath: "id", - store: custom( - setter => { - let onUpdate = () => { - const events = migrate(repository.dump(), options) - - setter( - events.map(event => { - const relays = Array.from(tracker.getRelays(event.id)) - - return {id: event.id, event, relays} - }), - ) - } - - if (options.throttle) { - onUpdate = throttle(options.throttle, onUpdate) - } - - onUpdate() - tracker.on("update", onUpdate) - repository.on("update", onUpdate) - - return () => { - tracker.off("update", onUpdate) - } - }, - { - set: (items: {event: TrustedEvent; relays: string[]}[]) => { - const events: TrustedEvent[] = [] - const relaysById = new Map>() - - for (const {event, relays} of items) { - events.push(event) - relaysById.set(event.id, new Set(relays)) - } - - repository.load(events) - tracker.load(relaysById) - }, - }, - ), - }), -} diff --git a/packages/app/src/zappers.ts b/packages/app/src/zappers.ts index 23f7af8..ef1684c 100644 --- a/packages/app/src/zappers.ts +++ b/packages/app/src/zappers.ts @@ -58,6 +58,7 @@ export const { indexStore: zappersByLnurl, deriveItem: deriveZapper, loadItem: loadZapper, + onItem: onZapper, } = collection({ name: "zappers", store: zappers,