import { NostrEvent, NostrFilter, NRelay1, NStore } from '@nostrify/nostrify'; import Debug from '@soapbox/stickynotes/debug'; import { normalizeFilters } from '@/filter.ts'; import { type DittoEvent } from '@/interfaces/DittoEvent.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; import { abortError } from '@/utils/abort.ts'; interface SearchStoreOpts { relay: string | undefined; fallback: NStore; hydrator?: NStore; } class SearchStore implements NStore { #debug = Debug('ditto:storages:search'); #fallback: NStore; #hydrator: NStore; #relay: NRelay1 | undefined; constructor(opts: SearchStoreOpts) { this.#fallback = opts.fallback; this.#hydrator = opts.hydrator ?? this; if (opts.relay) { this.#relay = new NRelay1(opts.relay); } } event(_event: NostrEvent, _opts?: { signal?: AbortSignal }): Promise { return Promise.reject(new Error('EVENT not implemented.')); } async query(filters: NostrFilter[], opts?: { signal?: AbortSignal; limit?: number }): Promise { filters = normalizeFilters(filters); if (opts?.signal?.aborted) return Promise.reject(abortError()); if (!filters.length) return Promise.resolve([]); this.#debug('REQ', JSON.stringify(filters)); const query = filters[0]?.search; if (this.#relay && this.#relay.socket.readyState === WebSocket.OPEN) { this.#debug(`Searching for "${query}" at ${this.#relay.socket.url}...`); const events = await this.#relay.query(filters, opts); return hydrateEvents({ events, store: this.#hydrator, signal: opts?.signal, }); } else { this.#debug(`Searching for "${query}" locally...`); return this.#fallback.query(filters, opts); } } } export { SearchStore };