event-cache.actor.ts raw

   1  import { Func, Inbox, Lifecycle, spawn, loop, STOP } from 'anneal'
   2  import { Event as NEvent } from 'nostr-tools'
   3  import { compareEvents, getReplaceableCoordinateFromEvent, isReplaceableEvent } from '@/lib/event'
   4  
   5  export type EventCacheActor = {
   6    cache: Inbox<NEvent>
   7    lookup: Func<string, NEvent | undefined>
   8    lookupMany: Func<string[], (NEvent | undefined)[]>
   9    lookupReplaceable: Func<string, NEvent | undefined>
  10    lc: Lifecycle
  11  }
  12  
  13  export function createEventCacheActor(): EventCacheActor {
  14    const events = new Map<string, NEvent>()
  15    const replaceables = new Map<string, NEvent>()
  16  
  17    const cache = new Inbox<NEvent>(0)
  18    const lookup = new Func<string, NEvent | undefined>()
  19    const lookupMany = new Func<string[], (NEvent | undefined)[]>()
  20    const lookupReplaceable = new Func<string, NEvent | undefined>()
  21    const lc = new Lifecycle()
  22  
  23    spawn(lc, async () => {
  24      await loop(
  25        lc.on(() => STOP),
  26        cache.on((event) => {
  27          events.set(event.id, event)
  28          if (isReplaceableEvent(event.kind)) {
  29            const coord = getReplaceableCoordinateFromEvent(event)
  30            const existing = replaceables.get(coord)
  31            if (!existing || compareEvents(event, existing) > 0) {
  32              replaceables.set(coord, event)
  33            }
  34          }
  35        }),
  36        lookup.on(({ req: id, reply }) => {
  37          reply(events.get(id))
  38        }),
  39        lookupMany.on(({ req: ids, reply }) => {
  40          reply(ids.map((id) => events.get(id)))
  41        }),
  42        lookupReplaceable.on(({ req: coord, reply }) => {
  43          reply(replaceables.get(coord))
  44        })
  45      )
  46    })
  47  
  48    return { cache, lookup, lookupMany, lookupReplaceable, lc }
  49  }
  50