Support naddr/nevent/note slugs for unindexed posts, cache naddr lookups, render Nostr embeds in markdown, and add a consistency check for events mirrors. Co-authored-by: Cursor <cursoragent@cursor.com>
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
// Lightweight TTL cache backed by localStorage so resolved Nostr data (author
|
|
// profiles, long-form events) survives page reloads and navigation instead of
|
|
// being re-queried from relays every visit. No-ops on the server and degrades
|
|
// silently on quota/parse errors so it can never break rendering.
|
|
|
|
const PREFIX = "bbe:nostr:";
|
|
|
|
interface Entry<T> {
|
|
v: T;
|
|
t: number; // stored-at epoch ms
|
|
}
|
|
|
|
export function readCache<T>(key: string, ttlMs: number): T | null {
|
|
if (typeof window === "undefined") return null;
|
|
try {
|
|
const raw = window.localStorage.getItem(PREFIX + key);
|
|
if (!raw) return null;
|
|
const entry = JSON.parse(raw) as Entry<T>;
|
|
if (Date.now() - entry.t > ttlMs) {
|
|
window.localStorage.removeItem(PREFIX + key);
|
|
return null;
|
|
}
|
|
return entry.v;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeCache<T>(key: string, value: T): void {
|
|
if (typeof window === "undefined") return;
|
|
const payload = JSON.stringify({ v: value, t: Date.now() } satisfies Entry<T>);
|
|
try {
|
|
window.localStorage.setItem(PREFIX + key, payload);
|
|
} catch {
|
|
// Quota exceeded (or similar): evict our namespace and retry once.
|
|
try {
|
|
pruneNamespace();
|
|
window.localStorage.setItem(PREFIX + key, payload);
|
|
} catch {
|
|
// Give up silently — caching is best-effort.
|
|
}
|
|
}
|
|
}
|
|
|
|
// Removes every entry written by this cache to recover space when localStorage
|
|
// is full. Only touches our own namespace.
|
|
function pruneNamespace(): void {
|
|
for (let i = window.localStorage.length - 1; i >= 0; i--) {
|
|
const k = window.localStorage.key(i);
|
|
if (k && k.startsWith(PREFIX)) window.localStorage.removeItem(k);
|
|
}
|
|
}
|