/// import { pinGlobal } from '../utils/globalState'; // Pool is process-wide so the inline ServerIsland bundle and the hydration // HydratableIsland bundle share observers at runtime. // // Cleanup: when an element intersects we `unobserve` and drop the callback. // If an element is removed from the DOM *before* it ever intersects, neither // runs — the IntersectionObserver keeps a strong ref to the (now-detached) // node for the rest of the page's lifetime. Bounded by page lifetime, so not // a real leak; revisit if islands churn faster than expected. type Entry = { observer: IntersectionObserver; callbacks: WeakMap void>; }; type Pool = Map; const pool = pinGlobal('__mochi_visibility_pool__', () => new Map()); export function observeVisible(target: Element, rootMargin: string, onVisible: () => void): void { let entry = pool.get(rootMargin); if (!entry) { const callbacks = new WeakMap void>(); const observer = new IntersectionObserver( (entries) => { for (const e of entries) { if (!e.isIntersecting) { continue; } const cb = callbacks.get(e.target); if (!cb) { continue; } observer.unobserve(e.target); callbacks.delete(e.target); cb(); } }, { rootMargin }, ); entry = { observer, callbacks }; pool.set(rootMargin, entry); } if (entry.callbacks.has(target)) { return; } entry.callbacks.set(target, onVisible); entry.observer.observe(target); }