158 lines
4.2 KiB
TypeScript
158 lines
4.2 KiB
TypeScript
/**
|
|
* 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<unknown>
|
|
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<string, Event[]> {
|
|
const eventsByHashId = new Map<string, Event[]>()
|
|
|
|
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<void> {
|
|
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<void> {
|
|
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<unknown>
|
|
): Promise<boolean> {
|
|
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
|
|
}
|