import { type ChannelPriority, type FocusSummary, type PendingByLeaf, focusSummary, } from "../lib/channelPriority"; // THE STATUS PAYLOAD'S HALF OF CHANNEL PRIORITY — the shape of "which tree does // this lane dispatch from", and the focus banner's numbers off it. // // The RESOLUTION of that shape is a read (a channel listing, and for a site // focus a sites read), so it stays in the editor as `readPriorityView`. What // lives here is everything downstream of it: the view type the four lanes are // compiled against, and the two pure folds the payload applies to it. export type PriorityView = { model: ChannelPriority; // The model says something, so the four trees are compiled rather than // stored. The lane consoles read this to go read-only on the tree. compiled: boolean; // Every channel slug, the population `compileLaneRoot` filters per lane. // Empty when nothing is compiled — it is never read in that case. slugs: string[]; focusSlugs: string[]; // A display name for the focus: the site's title for a site focus, the slugs // for a channel focus. Resolved by the reader because the model stores a // siteId and the banner does no I/O. name: string | null; }; // `focusSummary` reads nothing but each leaf's COUNT, and `computeLeafPending` // throws the arrays away before this layer sees them (it returns counts plus a // truncated head). Rather than widen that return type — which would put the // whole pending set of every leaf on a three-second poll's heap — the counts are // re-presented as arrays of the right length. `new Array(n)` allocates no // elements; only `.length` is ever read. export function pendingByLeafFromCounts( counts: Record, ): PendingByLeaf { const out: Record = {}; for (const [id, n] of Object.entries(counts)) out[id] = new Array(n); return out; } export function laneFocusSummary( view: PriorityView, counts: Record, ): FocusSummary { return focusSummary( view.model, view.focusSlugs, pendingByLeafFromCounts(counts), ); }