Files
eranos/src/lib/parseBirdex.ts
T
Alex Gleason b2634d2fcb Render Birdstar Birdex events (kind 12473) as tiled life lists
A Birdex is a replaceable per-author index of every bird species the
author has ever confirmed via kind 2473, stored as positional i/n tag
pairs in chronological first-seen order. In feeds, show a compact
tile strip of the most recently-added species with a "+N" capstone
when the list overflows — mirroring how kind 3 follow lists preview
members as an avatar stack plus "+N more". On the post-detail page,
render every species as a responsive grid so visitors can browse the
author's whole life list.

Each tile resolves the species' Wikidata entity through English
Wikipedia to pull a thumbnail and common-name label, reusing the
same fetch path as kind 2473 detection cards. The Wikidata URL is
sanitized before being routed, and the paired n tag provides a
scientific-name fallback while the remote lookup is in flight.
2026-04-30 01:44:15 -05:00

64 lines
2.4 KiB
TypeScript

import type { NostrEvent } from '@nostrify/nostrify';
/**
* Canonical Wikidata entity URI shape used by Birdstar kinds 2473 and 12473.
* https → www.wikidata.org → /entity/Q<digits> — no fragment, query, or
* trailing slash. This is the trust boundary; tags that don't match are
* silently skipped.
*/
const WIKIDATA_ENTITY_URI_RE = /^https:\/\/www\.wikidata\.org\/entity\/(Q\d+)$/;
/** A single species entry on a Birdex life list. */
export interface BirdexSpeciesEntry {
/** Wikidata entity URI — the canonical species identifier. */
entityUri: string;
/** Wikidata entity ID (e.g. "Q26825") parsed from the URI. */
entityId: string;
/**
* Scientific (binomial) name carried by the positionally-paired `n`
* tag. Empty string when the source event omitted the `n` tag (older
* Birdstar events, or events authored by tools predating the
* name-pairing convention).
*/
scientificName: string;
}
/**
* Walk the tags of a kind 12473 Birdex event in order, pairing each
* valid `i` tag with the immediately-following `n` tag (if present)
* per Birdstar NIP § "Kind 12473 — Birdex".
*
* Pairing is positional: the `n` tag is accepted as this species'
* scientific name only when it is the very next entry in the tag
* array. An `i` tag not followed by an `n` yields an entry with an
* empty `scientificName` — still renderable by Q-id alone.
*
* Deduplication keeps the first occurrence of each URI so the
* chronological first-seen order is preserved even if a malformed
* publisher emits duplicates. The URL-shape regex is the trust
* boundary — no paired `k` tag is consulted (the kind contract
* already guarantees every valid `i` is a Wikidata entity URI).
*/
export function parseBirdexEvent(event: NostrEvent): BirdexSpeciesEntry[] {
const seen = new Set<string>();
const entries: BirdexSpeciesEntry[] = [];
const tags = event.tags;
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
if (tag[0] !== 'i') continue;
const uri = tag[1];
if (typeof uri !== 'string') continue;
const m = uri.match(WIKIDATA_ENTITY_URI_RE);
if (!m) continue;
if (seen.has(uri)) continue;
seen.add(uri);
const next = tags[i + 1];
const scientificName =
next && next[0] === 'n' && typeof next[1] === 'string' ? next[1] : '';
entries.push({ entityUri: uri, entityId: m[1], scientificName });
}
return entries;
}