fix: rootReference() no longer treats a mention as the root

Follow-up to the isReply() fix: rootReference()'s positional fallback
still matched any e tag regardless of marker, so an event with only a
mention-marked e tag would incorrectly return the mentioned id as the
thread root. The fallback now only considers unmarked e tags, per the
deprecated positional NIP-10 scheme.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto
This commit is contained in:
2026-09-06 18:10:48 +02:00
parent 696f64df3f
commit ff4626f012
2 changed files with 18 additions and 3 deletions

View File

@@ -63,4 +63,16 @@ describe('rootReference', () => {
it('is undefined for a root note', () => {
expect(rootReference(note([]))).toBeUndefined();
});
it('is undefined for a note that only mentions another event', () => {
expect(rootReference(note([['e', 'mentioned-id', '', 'mention']]))).toBeUndefined();
});
it('ignores a mention when falling back to the positional scheme', () => {
const event = note([
['e', 'mentioned-id', '', 'mention'],
['e', 'root-id'],
]);
expect(rootReference(event)).toBe('root-id');
});
});

View File

@@ -93,13 +93,16 @@ export function tagValues(event: NostrEvent, name: string): string[] {
/**
* The event a reply points at, following NIP-10: prefer an explicit `root`
* marker, fall back to the first positional `e` tag (the deprecated scheme
* puts the root id first: `["e", <root-id>], ["e", <reply-id>]`).
* marker, fall back to the first *unmarked* `e` tag (the deprecated scheme
* puts the root id first: `["e", <root-id>], ["e", <reply-id>]`). A `mention`
* or `reply`-only marker is never treated as the root — a mention isn't
* part of the thread, and a lone `reply` marker without `root` is malformed
* per NIP-10 rather than an implicit root.
*/
export function rootReference(event: NostrEvent): string | undefined {
const marked = event.tags.find(([name, , , marker]) => name === 'e' && marker === 'root');
if (marked) return marked[1];
const positional = event.tags.filter(([name]) => name === 'e');
const positional = event.tags.filter(([name, , , marker]) => name === 'e' && !marker);
return positional[0]?.[1];
}