diff --git a/src/utils/tags.test.ts b/src/utils/tags.test.ts new file mode 100644 index 0000000..43e1235 --- /dev/null +++ b/src/utils/tags.test.ts @@ -0,0 +1,45 @@ +import { assertEquals } from '@std/assert'; + +import { addTag, deleteTag, findQuoteTag, findReplyTag, getTagSet, hasTag } from './tags.ts'; + +Deno.test('addTag', () => { + const tags = [['p', 'alex']]; + assertEquals(addTag(tags, ['p', 'alex']), [['p', 'alex']]); + assertEquals(addTag(tags, ['p', 'fiatjaf']), [['p', 'alex'], ['p', 'fiatjaf']]); +}); + +Deno.test('deleteTag', () => { + const tags = [['p', 'alex'], ['p', 'fiatjaf']]; + assertEquals(deleteTag(tags, ['p', 'alex']), [['p', 'fiatjaf']]); + assertEquals(deleteTag(tags, ['p', 'fiatjaf']), [['p', 'alex']]); +}); + +Deno.test('findQuoteTag', () => { + assertEquals(findQuoteTag([['q', '123']]), ['q', '123']); + assertEquals(findQuoteTag([['e', '', '', 'mention', '456']]), ['e', '', '', 'mention', '456']); + assertEquals(findQuoteTag([['e', '', '', 'mention', '456'], ['q', '123']]), ['q', '123']); + assertEquals(findQuoteTag([['q', '123'], ['e', '', '', 'mention', '456']]), ['q', '123']); +}); + +Deno.test('findReplyTag', () => { + const root = ['e', '123', '', 'root']; + const reply = ['e', '456', '', 'reply']; + + assertEquals(findReplyTag([root]), root); + assertEquals(findReplyTag([reply]), reply); + assertEquals(findReplyTag([root, reply]), reply); + assertEquals(findReplyTag([reply, root]), reply); + assertEquals(findReplyTag([['e', '321'], ['e', '789']]), ['e', '789']); + assertEquals(findReplyTag([reply, ['e', '789']]), reply); +}); + +Deno.test('getTagSet', () => { + const tags = [['p', 'alex'], ['p', 'fiatjaf'], ['p', 'alex']]; + assertEquals(getTagSet(tags, 'p'), new Set(['alex', 'fiatjaf'])); +}); + +Deno.test('hasTag', () => { + const tags = [['p', 'alex']]; + assertEquals(hasTag(tags, ['p', 'alex']), true); + assertEquals(hasTag(tags, ['p', 'fiatjaf']), false); +}); diff --git a/src/utils/tags.ts b/src/utils/tags.ts index ecddaf4..6375e81 100644 --- a/src/utils/tags.ts +++ b/src/utils/tags.ts @@ -31,18 +31,40 @@ function addTag(tags: readonly string[][], tag: string[]): string[][] { } } -const isReplyTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'reply'; -const isRootTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'root'; -const isLegacyReplyTag = (tag: string[]) => tag[0] === 'e' && !tag[3]; +/** Tag is a NIP-10 root tag. */ +function isRootTag(tag: string[]): tag is ['e', string, string, 'root', ...string[]] { + return tag[0] === 'e' && tag[3] === 'root'; +} -const isQuoteTag = (tag: string[]) => tag[0] === 'q'; -const isLegacyQuoteTag = (tag: string[]) => tag[0] === 'e' && tag[3] === 'mention'; +/** Tag is a NIP-10 reply tag. */ +function isReplyTag(tag: string[]): tag is ['e', string, string, 'reply', ...string[]] { + return tag[0] === 'e' && tag[3] === 'reply'; +} -function findReplyTag(tags: string[][]): string[] | undefined { +/** Tag is a legacy "e" tag with a "mention" marker. */ +function isLegacyQuoteTag(tag: string[]): tag is ['e', string, string, 'mention', ...string[]] { + return tag[0] === 'e' && tag[3] === 'mention'; +} + +/** Tag is an "e" tag without a NIP-10 marker. */ +function isLegacyReplyTag(tag: string[]): tag is ['e', string, string] { + return tag[0] === 'e' && !tag[3]; +} + +/** Tag is a "q" tag. */ +function isQuoteTag(tag: string[]): tag is ['q', ...string[]] { + return tag[0] === 'q'; +} + +/** Get the "e" tag for the event being replied to, first according to the NIPs then falling back to the legacy way. */ +function findReplyTag(tags: string[][]): ['e', ...string[]] | undefined { return tags.find(isReplyTag) || tags.find(isRootTag) || tags.findLast(isLegacyReplyTag); } -function findQuoteTag(tags: string[][]): string[] | undefined { +/** Get the "q" tag, falling back to the legacy "e" tag with a "mention" marker. */ +function findQuoteTag( + tags: string[][], +): ['q', ...string[]] | ['e', string, string, 'mention', ...string[]] | undefined { return tags.find(isQuoteTag) || tags.find(isLegacyQuoteTag); }