Files
bbeandCursor 2ef68222bf feat: resolve live Nostr references in blog posts and add embeds
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>
2026-06-29 03:57:32 +02:00

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);
}
}