import { cache } from "react"; import type { Paths } from "yt-dlp-transcript-common/lib/paths"; import { listChannelBriefs } from "yt-dlp-transcript-common/controller/channels"; import { readDuplicateReport } from "yt-dlp-transcript-common/controller/duplicateShorts"; import { getSettings } from "yt-dlp-transcript-common/lib/settings"; // Per-REQUEST memoization for loaders that more than one component on the same // page reaches for. The dashboard derives the channel list three separate ways // in a single render (the layout's reclaimable-disk badge, the actionable // census and buildWidgetSyncPayload); without this it reads the same 65 files // each time. // // `React.cache` is scoped to one request, so there is no staleness risk here by // construction — the request boundary is already the consistency boundary, and // two components rendering the same page should see the same numbers anyway. // // This lives in editor/app rather than common/ deliberately, on both counts: // common/ is shared with the export, MCP and CLI packages and must not import // react. And no cache with a LONGER life than a request belongs on this data — // no `unstable_cache`, no `use cache`, no module-level map. The snapshot // scheduler rewrites these files on a ~1 s debounce from job runners inside // common/, which cannot import next/cache to invalidate anything. A cache // nothing can invalidate is just a stale number with extra steps. // ONE EXCEPTION, bounded: the operations status poll's 3-second memo of the // lanes' pending counts (operations/status.ts, `autoQueueStatusMemo`), keyed by // the settings it is folded from and otherwise expired by time. // Keyed on the `paths` argument by identity, which works because getPaths() // memoizes its result at module scope and hands back the same object every // call. Pass it straight through; don't spread or rebuild it. // // (loadActionableSummary gets the same treatment, but its cached wrapper lives // beside it in ./actionable/loadActionable — this module deliberately imports // nothing from app/ so those loaders can import IT.) export const getChannelBriefs = cache((paths: Paths) => listChannelBriefs(paths), ); // The duplicates report is a 6.7 MB JSON parse. It measures at ~65 ms. Only // /review reads it now — the dashboard and the widget poll used to pay for it // through loadActionableSummary and never showed a cluster. export const getDuplicateReport = cache((paths: Paths) => readDuplicateReport(paths), ); export const getSettingsCached = cache(() => getSettings());