/** * Helper for grouping and caching events by hash * Centralizes the pattern of grouping events by hash ID and caching the latest version */ import type { Event } from 'nostr-tools' import { extractTagsFromEvent } from '../nostrTagSystem' import { parseObjectId } from '../urlGenerator' import { getLatestVersion } from '../versionManager' import type { ObjectType } from '../objectCache' import { writeObjectToCache } from './writeObjectHelper' export interface EventCacheConfig { objectType: ObjectType extractor: (event: Event) => Promise getHash?: (extracted: unknown) => string | null getIndex?: (extracted: unknown) => number getVersion?: (event: Event) => number getHidden?: (event: Event) => boolean } interface ExtractedObjectWithId { id?: string index?: number } function groupEventsByHashId(events: Event[]): Map { const eventsByHashId = new Map() for (const event of events) { const tags = extractTagsFromEvent(event) const { id } = tags if (id) { const parsed = parseObjectId(id) const hash = parsed.hash ?? id const current = eventsByHashId.get(hash) if (current) { current.push(event) } else { eventsByHashId.set(hash, [event]) } } } return eventsByHashId } function resolveExtractedId(extracted: unknown, getHash: EventCacheConfig['getHash']): string | undefined { const extractedHash = getHash ? getHash(extracted) : null const extractedObj = extracted as ExtractedObjectWithId return extractedHash ?? extractedObj.id } function resolveHashAndIndex( extractedId: string, extracted: unknown, getIndex: EventCacheConfig['getIndex'] ): { hash: string; index: number } { const parsed = parseObjectId(extractedId) const extractedObj = extracted as ExtractedObjectWithId const hash = parsed.hash ?? extractedId const index = getIndex ? getIndex(extracted) : (parsed.index ?? extractedObj.index ?? 0) return { hash, index } } function resolveVersionAndHidden( latestEvent: Event, getVersion: EventCacheConfig['getVersion'], getHidden: EventCacheConfig['getHidden'] ): { version: number; hidden: boolean } { const tags = extractTagsFromEvent(latestEvent) const version = getVersion ? getVersion(latestEvent) : (tags.version ?? 0) const hidden = getHidden ? getHidden(latestEvent) : (tags.hidden ?? false) return { version, hidden } } async function cacheLatestEventForHashGroup(hashEvents: Event[], config: EventCacheConfig): Promise { const { objectType, extractor, getHash, getIndex, getVersion, getHidden } = config const latestEvent = getLatestVersion(hashEvents) if (!latestEvent) { return } const extracted = await extractor(latestEvent) if (!extracted) { return } const extractedId = resolveExtractedId(extracted, getHash) if (!extractedId) { return } const { hash, index } = resolveHashAndIndex(extractedId, extracted, getIndex) const { version, hidden } = resolveVersionAndHidden(latestEvent, getVersion, getHidden) await writeObjectToCache({ objectType, hash, event: latestEvent, parsed: extracted, version, hidden, index, }) } /** * Group events by hash ID and cache the latest version of each */ export async function groupAndCacheEventsByHash( events: Event[], config: EventCacheConfig ): Promise { const eventsByHashId = groupEventsByHashId(events) for (const hashEvents of eventsByHashId.values()) { await cacheLatestEventForHashGroup(hashEvents, config) } } /** * Cache a single event as an object * Simplified version for direct event caching */ export async function cacheEventAsObject( event: Event, objectType: ObjectType, extractor: (event: Event) => Promise ): Promise { const extracted = await extractor(event) if (!extracted) { return false } const tags = extractTagsFromEvent(event) const hash = (extracted as { hash?: string })?.hash ?? (extracted as { id?: string })?.id ?? '' if (!hash) { return false } const index = (extracted as { index?: number })?.index ?? 0 await writeObjectToCache({ objectType, hash, event, parsed: extracted, version: tags.version, hidden: tags.hidden, index, }) return true }