import { Event } from './core.ts' import { isReplaceableKind } from './kinds.ts' export type Filter = { ids?: string[] kinds?: number[] authors?: string[] since?: number until?: number limit?: number search?: string [key: `#${string}`]: string[] | undefined } export function matchFilter(filter: Filter, event: Event): boolean { if (filter.ids && filter.ids.indexOf(event.id) === -1) { if (!filter.ids.some(prefix => event.id.startsWith(prefix))) { return false } } if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) { if (!filter.authors.some(prefix => event.pubkey.startsWith(prefix))) { return false } } for (let f in filter) { if (f[0] === '#') { let tagName = f.slice(1) let values = filter[`#${tagName}`] if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values!.indexOf(v) !== -1)) return false } } if (filter.since && event.created_at < filter.since) return false if (filter.until && event.created_at > filter.until) return false return true } export function matchFilters(filters: Filter[], event: Event): boolean { for (let i = 0; i < filters.length; i++) { if (matchFilter(filters[i], event)) return true } return false } export function mergeFilters(...filters: Filter[]): Filter { let result: Filter = {} for (let i = 0; i < filters.length; i++) { let filter = filters[i] Object.entries(filter).forEach(([property, values]) => { if (property === 'kinds' || property === 'ids' || property === 'authors' || property[0] === '#') { // @ts-ignore result[property] = result[property] || [] // @ts-ignore for (let v = 0; v < values.length; v++) { // @ts-ignore let value = values[v] // @ts-ignore if (!result[property].includes(value)) result[property].push(value) } } }) if (filter.limit && (!result.limit || filter.limit > result.limit)) result.limit = filter.limit if (filter.until && (!result.until || filter.until > result.until)) result.until = filter.until if (filter.since && (!result.since || filter.since < result.since)) result.since = filter.since } return result } /** Calculate the intrinsic limit of a filter. This function may return `Infinity`. */ export function getFilterLimit(filter: Filter): number { if (filter.ids && !filter.ids.length) return 0 if (filter.kinds && !filter.kinds.length) return 0 if (filter.authors && !filter.authors.length) return 0 return Math.min( Math.max(0, filter.limit ?? Infinity), filter.ids?.length ?? Infinity, filter.authors?.length && filter.kinds?.every(kind => isReplaceableKind(kind)) ? filter.authors.length * filter.kinds.length : Infinity, ) }