RelaySetRepositoryImpl.ts raw
1 import { RelaySetRepository, RelaySet, Pubkey, tryToRelaySet } from '@/domain'
2 import client from '@/services/client.service'
3 import indexedDb from '@/services/indexed-db.service'
4 import { kinds } from 'nostr-tools'
5 import { RepositoryDependencies } from './types'
6
7 /**
8 * IndexedDB + Relay implementation of RelaySetRepository
9 *
10 * Uses IndexedDB for local caching and the client service for relay fetching.
11 * Save operations publish to relays and update the local cache.
12 */
13 export class RelaySetRepositoryImpl implements RelaySetRepository {
14 constructor(private readonly deps: RepositoryDependencies) {}
15
16 async findById(pubkey: Pubkey, id: string): Promise<RelaySet | null> {
17 // Try cache first
18 const cachedEvent = await indexedDb.getReplaceableEvent(pubkey.hex, kinds.Relaysets, id)
19 if (cachedEvent) {
20 const relaySet = tryToRelaySet(cachedEvent)
21 if (relaySet) return relaySet
22 }
23
24 // Fetch from relays
25 const events = await client.fetchEvents([], {
26 kinds: [kinds.Relaysets],
27 authors: [pubkey.hex],
28 '#d': [id]
29 })
30
31 if (events.length === 0) return null
32
33 // Get the most recent event
34 const event = events.sort((a, b) => b.created_at - a.created_at)[0]
35
36 // Update cache
37 await indexedDb.putReplaceableEvent(event)
38
39 return tryToRelaySet(event)
40 }
41
42 async findByOwner(pubkey: Pubkey): Promise<RelaySet[]> {
43 // Fetch all relay sets from relays
44 const events = await client.fetchEvents([], {
45 kinds: [kinds.Relaysets],
46 authors: [pubkey.hex]
47 })
48
49 // Deduplicate by 'd' tag (keep latest)
50 const eventMap = new Map<string, typeof events[0]>()
51 for (const event of events) {
52 const d = event.tags.find((t) => t[0] === 'd')?.[1] || ''
53 const existing = eventMap.get(d)
54 if (!existing || existing.created_at < event.created_at) {
55 eventMap.set(d, event)
56 }
57 }
58
59 // Update cache and convert to domain objects
60 const relaySets: RelaySet[] = []
61 for (const event of eventMap.values()) {
62 await indexedDb.putReplaceableEvent(event)
63 const relaySet = tryToRelaySet(event)
64 if (relaySet) {
65 relaySets.push(relaySet)
66 }
67 }
68
69 return relaySets
70 }
71
72 async save(_pubkey: Pubkey, relaySet: RelaySet): Promise<void> {
73 const draftEvent = relaySet.toDraftEvent()
74 const publishedEvent = await this.deps.publish(draftEvent)
75
76 // Update cache
77 await indexedDb.putReplaceableEvent(publishedEvent)
78 }
79
80 async delete(_pubkey: Pubkey, _id: string): Promise<void> {
81 // Note: In Nostr, "deleting" a replaceable event is done by publishing
82 // an empty or tombstone version. For now, we just remove from local cache.
83 // The actual deletion logic depends on the application's requirements.
84 // Typically you'd publish a new version that doesn't include the relay set.
85 }
86 }
87