diff --git a/src/services/relay-selection.ts b/src/services/relay-selection.ts index 2cc21c7..0437b7c 100644 --- a/src/services/relay-selection.ts +++ b/src/services/relay-selection.ts @@ -25,6 +25,7 @@ import type { RelaySelectionResult, RelaySelectionReasoning, RelaySelectionOptions, + RelayFilterMap, } from "@/types/relay-selection"; /** @@ -486,6 +487,37 @@ function buildReasoning( ); } +/** + * Builds per-relay filter maps for optimized queries + * + * Groups authors by which relay they should be queried from, + * allowing consumers to send only relevant authors to each relay. + * + * @param selectedPointers - ProfilePointers after optimization (with filtered relays) + * @returns Array of per-relay filter mappings + */ +function buildPerRelayFilters( + selectedPointers: ProfilePointer[], +): RelayFilterMap[] { + // Group authors by relay + const relayToAuthors = new Map>(); + + for (const pointer of selectedPointers) { + for (const relay of pointer.relays || []) { + if (!relayToAuthors.has(relay)) { + relayToAuthors.set(relay, new Set()); + } + relayToAuthors.get(relay)!.add(pointer.pubkey); + } + } + + // Convert to array + return Array.from(relayToAuthors.entries()).map(([relay, authors]) => ({ + relay, + authors: Array.from(authors), + })); +} + /** * Creates a fallback result when no pubkeys or all fetches failed * @@ -707,9 +739,13 @@ export async function selectRelaysForFilter( pTagPointers, ); + // Build per-relay filter maps for optimized queries + const perRelayFilters = buildPerRelayFilters(selectedPointers); + return { relays, reasoning, isOptimized: true, + perRelayFilters, }; } diff --git a/src/types/relay-selection.ts b/src/types/relay-selection.ts index 35ad329..f71df3e 100644 --- a/src/types/relay-selection.ts +++ b/src/types/relay-selection.ts @@ -17,6 +17,14 @@ export interface RelaySelectionResult { /** True if using NIP-65 optimization, false if using fallback */ isOptimized: boolean; + + /** + * Per-relay author mappings for optimized queries (optional) + * + * When present, consumers can use this to send only relevant + * authors to each relay instead of the full authors list. + */ + perRelayFilters?: RelayFilterMap[]; } /** @@ -52,3 +60,17 @@ export interface RelaySelectionOptions { /** Timeout in ms for fetching kind:10002 events (default: 1000) */ timeout?: number; } + +/** + * Per-relay filter mapping for optimized queries + * + * Instead of sending the same filter to all relays, we can send + * only the relevant authors to each relay based on their relay lists. + */ +export interface RelayFilterMap { + /** Relay URL (normalized) */ + relay: string; + + /** Authors that should be queried from this relay */ + authors: string[]; +}