← back to Govarbitrage
src/lib/digest-top.ts
33 lines
import { queryListings, type ListingRow } from "@/lib/listings";
// Shared "top ranked opportunities" selection — the single definition of how
// the Top-10 digest (scripts/send-digest.ts) and the skeptic agent pick the
// ranked head of the corpus, so the adversarial gate audits exactly what the
// digest is about to email.
export interface TopRankedOptions {
limit: number;
/**
* When true (digest behavior) only still-open auctions (closingAt in the
* future) are returned. The skeptic passes false so it can also catch
* stale rows — closed auctions still ranked as if live.
*/
openOnly?: boolean;
}
/** ACTIVE listings ranked by Overall Opportunity, best first. */
export async function topRankedOpportunities(opts: TopRankedOptions): Promise<ListingRow[]> {
const { limit, openOnly = true } = opts;
const { rows } = await queryListings({
profile: "OVERALL_OPPORTUNITY",
sort: "opportunityScore",
dir: "desc",
pageSize: Math.max(200, limit),
});
const now = Date.now();
const pool = openOnly
? rows.filter((r) => r.closingAt && new Date(r.closingAt).getTime() > now)
: rows;
return pool.slice(0, limit);
}