UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

80 lines (79 loc) • 2.66 kB
import { AsyncLocalStorage } from "node:async_hooks"; /** Keep only the handles each in-flight reader actually obtained. */ export function indexReaders(current, close) { const calls = new AsyncLocalStorage(); const readers = new Map(); const retired = new Set(); return { get() { const handle = current(); const call = calls.getStore(); if (call?.active && !call.handles.has(handle)) { call.handles.add(handle); readers.set(handle, (readers.get(handle) ?? 0) + 1); } return handle; }, retire(handle) { if (readers.has(handle)) retired.add(handle); else close(handle); }, run(fn) { const call = { active: true, handles: new Set() }; return calls.run(call, async () => { try { return await fn(); } finally { call.active = false; for (const handle of call.handles) { const count = readers.get(handle) - 1; if (count) readers.set(handle, count); else { readers.delete(handle); if (retired.delete(handle)) close(handle); } } } }); }, }; } /** Coalesce local freshness checks without a timer or any remote polling. */ export function localIndexRefresh(options) { const now = options.now ?? Date.now; const interval = options.intervalMs ?? 1000; let checkedAt = -Infinity; let pending; let failure; return { async ensureFresh() { if (pending) return pending; if (now() - checkedAt < interval) { if (failure) throw failure; return; } checkedAt = now(); try { if (options.isFresh()) { failure = undefined; return; } } catch (error) { failure = error; throw error; } pending = options.runExclusive(options.refresh).then(() => { failure = undefined; }) .catch((error) => { failure = error; throw error; }) .finally(() => { checkedAt = now(); pending = undefined; }); return pending; }, }; }