Something went wrong. Try again.
GET /xrpc/tech.waow.typeahead.searchActors typeahead.waow.tech
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081import labelerList from "./labelers.json";
/** The moderation labelers the reputation page asks about an account. * * `labelers.json` is generated by `scripts/discover-labelers.py` from the * public appview's own `associated.labeler` flag, so every entry is a labeler * the network asserts exists. Each labeler is asked DIRECTLY on its * `com.atproto.label.queryLabels` endpoint: the unauthenticated appview * ignores `atproto-accept-labelers` (verified 2026-09-03 against a * Blacksky-labeled account), so there is no single call that answers this. * One fan-out per report, request-scoped, never on the search path. */
export type Labeler = { did: string; handle: string; name: string; endpoint: string; followers: number };
/** a label as a labeler's queryLabels returns it */export type RawLabel = { val: string; uri: string; src: string; cts: string; neg?: boolean; exp?: string };
export type AppliedLabel = { val: string; since: string; expires: string | null };
export type LabelerAnswer = { did: string; handle: string; name: string; status: "ok" | "unreachable"; /** labels currently applied to the account, negations resolved; [] when status is unreachable */ labels: AppliedLabel[];};
export const LABELERS: Labeler[] = labelerList;
const LABELER_TIMEOUT_MS = 3000;
/** Fold a labeler's label history for one subject into what is applied now. * * queryLabels returns every label record, including negations * (`neg: true` withdraws an earlier label of the same `val`). The latest * record per `val` wins; an expired label is not applied. */export function applyLabelHistory(raw: RawLabel[], now: Date = new Date()): AppliedLabel[] { const latest = new Map<string, RawLabel>(); for (const l of [...raw].sort((a, b) => a.cts.localeCompare(b.cts))) latest.set(l.val, l); const out: AppliedLabel[] = []; for (const l of latest.values()) { if (l.neg) continue; if (l.exp && new Date(l.exp).getTime() <= now.getTime()) continue; out.push({ val: l.val, since: l.cts, expires: l.exp ?? null }); } return out.sort((a, b) => a.val.localeCompare(b.val));}
async function askLabeler(labeler: Labeler, did: string): Promise<LabelerAnswer> { const base = { did: labeler.did, handle: labeler.handle, name: labeler.name }; try { const url = `${labeler.endpoint}/xrpc/com.atproto.label.queryLabels?uriPatterns=${encodeURIComponent(did)}&limit=250`; const r = await fetch(url, { signal: AbortSignal.timeout(LABELER_TIMEOUT_MS) }); if (!r.ok) return { ...base, status: "unreachable", labels: [] }; const body = await r.json<{ labels?: RawLabel[] }>(); return { ...base, status: "ok", labels: applyLabelHistory(body.labels ?? []) }; } catch { return { ...base, status: "unreachable", labels: [] }; }}
/** Ask every configured labeler what it currently says about `did`. */export function queryLabelers(did: string, labelers: Labeler[] = LABELERS): Promise<LabelerAnswer[]> { return Promise.all(labelers.map((l) => askLabeler(l, did)));}
/** Percentile of `score` among a build's actors from its p0..p100 quantiles. * * The snapshot builder writes the 101 quantiles into `index_meta`; the * search service exposes them on `/index/status`. This replaces two * `COUNT(*)` queries over the visible corpus per report, which was the one * corpus-proportional read left on a request path. */export function percentileFromQuantiles(score: number, quantiles: number[]): number | null { if (quantiles.length !== 101 || score <= 0) return null; let p = 0; while (p < 100 && quantiles[p + 1]! <= score) p++; return p;}