import { unwrap } from "@snort/shared" import type { NostrEvent, ReqFilter } from "./nostr" import type { FlatReqFilter } from "./query-optimizer" /** * Request router managed splitting of requests to one or more relays, and which relay to send events to. */ export interface RequestRouter { /** * Pick relays to send an event to * @param pk The pubkey you are replying to * @param system Nostr system interface * @param pickN Number of relays to pick per recipient * @returns */ forReplyTo(pk: string, pickN?: number): Promise> /** * Pick relays to send an event to * @param ev The reply event to send * @param system Nostr system interface * @param pickN Number of relays to pick per recipient * @returns */ forReply(ev: NostrEvent, pickN?: number): Promise> /** * Split a request filter to one or more relays. * @param filter Filter to split * @param pickN Number of relays to pick * @returns */ forRequest(filter: ReqFilter, pickN?: number): Array /** * Split a request filter to one or more relays. * @param filter Filters to split * @param pickN Number of relays to pick * @returns */ forFlatRequest(filter: Array, pickN?: number): Array /** * Same as forRequest, but merges the results * @param filters */ forAllRequest(filters: Array): Array } export abstract class BaseRequestRouter implements RequestRouter { abstract forReplyTo(pk: string, pickN?: number): Promise> abstract forReply(ev: NostrEvent, pickN?: number): Promise> abstract forRequest(filter: ReqFilter, pickN?: number): Array abstract forFlatRequest(filter: FlatReqFilter[], pickN?: number): Array forAllRequest(filters: Array) { const allSplit = filters .map(a => this.forRequest(a)) .reduce((acc, v) => { for (const vn of v) { for (const r of (vn.relays?.length ?? 0) > 0 ? unwrap(vn.relays) : [""]) { const existing = acc.get(r) if (existing) { existing.push(vn) } else { acc.set(r, [vn]) } } } return acc }, new Map>()) return [...allSplit.values()].flat() } }