Skip to content
66 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Commits show their accounts, and every name opens a card (repos 0016)1/**
2 * The one place commits get their people (lib/commit-people.ts): every
3 * page that shows a commit's author asks here, once for all its commits.
4 * One identity call per page for the addresses not already known, and
5 * each answer kept a minute in the isolate, so a page of fifty commits by
6 * three people asks about three addresses, and the next page asks about
7 * none. A failure matches nobody: the names on the commits are shown.
8 */
9import type { Commit, EmailOwner } from "@g1t/contracts";
10
11import { type ShownCommit, addressesToMatch, showCommit } from "./commit-people";
12import { accounts } from "./services.server";
13
14/** How long an answer is kept: an address can be confirmed or removed. */
15const KEEP_MS = 60_000;
16/** Addresses kept at most; the oldest go first. */
17const MAX_KEPT = 5_000;
18/** `email_owners` takes at most this many at once. */
19const BATCH = 200;
20
21/** Each address's owner, or null for nobody, until `until`. */
22const kept = new Map<string, { until: number; owner: EmailOwner | null }>();
23
24function keep(email: string, owner: EmailOwner | null) {
25 kept.delete(email);
26 kept.set(email, { until: Date.now() + KEEP_MS, owner });
27 if (kept.size > MAX_KEPT) kept.delete(kept.keys().next().value as string);
28}
29
30/** The accounts behind these (lowercased) addresses; unknown ones are left out. */
31export async function emailOwners(emails: string[]): Promise<Record<string, EmailOwner>> {
32 const owners: Record<string, EmailOwner> = {};
33 const ask: string[] = [];
34 const now = Date.now();
35 for (const email of new Set(emails)) {
36 const hit = kept.get(email);
37 if (hit && hit.until > now) {
38 if (hit.owner) owners[email] = hit.owner;
39 } else {
40 ask.push(email);
41 }
42 }
43 for (let start = 0; start < ask.length; start += BATCH) {
44 const chunk = ask.slice(start, start + BATCH);
45 const found = await accounts.emailOwners(chunk).catch(() => null);
46 if (!found) continue;
47 for (const email of chunk) {
48 const owner = found[email] ?? null;
49 keep(email, owner);
50 if (owner) owners[email] = owner;
51 }
52 }
53 return owners;
54}
55
56/** Commits with their people, in one identity call at most. */
57export async function showCommits(commits: Commit[]): Promise<ShownCommit[]> {
58 if (commits.length === 0) return [];
59 const owners = await emailOwners(addressesToMatch(commits));
60 return commits.map((commit) => showCommit(commit, owners));
61}
62
63/** One commit, or null, with its people. */
64export async function showOneCommit(commit: Commit | null | undefined): Promise<ShownCommit | null> {
65 return commit ? (await showCommits([commit]))[0]! : null;
66}