diff --git a/filter.test.ts b/filter.test.ts index a58c93c..06ca7c3 100644 --- a/filter.test.ts +++ b/filter.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from 'bun:test' -import { matchFilter, matchFilters, mergeFilters } from './filter.ts' +import { getFilterLimit, matchFilter, matchFilters, mergeFilters } from './filter.ts' import { buildEvent } from './test-helpers.ts' describe('Filter', () => { @@ -241,4 +241,27 @@ describe('Filter', () => { ).toEqual({ kinds: [1, 7, 9, 10], since: 10, until: 30 }) }) }) + + describe('getFilterLimit', () => { + test('should handle ids', () => { + expect(getFilterLimit({ ids: ['123'] })).toEqual(1) + expect(getFilterLimit({ ids: ['123'], limit: 2 })).toEqual(1) + expect(getFilterLimit({ ids: ['123'], limit: 0 })).toEqual(0) + expect(getFilterLimit({ ids: ['123'], limit: -1 })).toEqual(0) + }) + + test('should count the authors times replaceable kinds', () => { + expect(getFilterLimit({ kinds: [0], authors: ['alex'] })).toEqual(1) + expect(getFilterLimit({ kinds: [0, 3], authors: ['alex'] })).toEqual(2) + expect(getFilterLimit({ kinds: [0, 3], authors: ['alex', 'fiatjaf'] })).toEqual(4) + }) + + test('should return Infinity for authors with regular kinds', () => { + expect(getFilterLimit({ kinds: [1], authors: ['alex'] })).toEqual(Infinity) + }) + + test('should return Infinity for empty filters', () => { + expect(getFilterLimit({})).toEqual(Infinity) + }) + }) }) diff --git a/filter.ts b/filter.ts index 0c7f2fa..ed2f107 100644 --- a/filter.ts +++ b/filter.ts @@ -1,4 +1,5 @@ import { Event } from './core.ts' +import { isReplaceableKind } from './kinds.ts' export type Filter = { ids?: string[] @@ -70,3 +71,19 @@ export function mergeFilters(...filters: Filter[]): Filter { 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, + ) +}