203 lines · 7.3 KB
import type { CommitSummary } from "~/lib/types";

const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
const MAX_EMAIL_LENGTH = 254;
const MAX_LOOKUP_EMAILS = 40;

export type IndexedCommit = Pick<CommitSummary, "sha" | "authorName" | "authorEmail" | "authoredAt"> & {
  coAuthors: string[];
};
export type EmailDay = { email: string; day: string; commits: number };
export type ContributorRow = { email: string; name: string; commits: number; first: number; last: number };

function normalizeEmail(email: string): string {
  return email.trim().toLowerCase().slice(0, MAX_EMAIL_LENGTH);
}

export function utcDay(timestamp: number): string {
  return new Date(timestamp).toISOString().slice(0, 10);
}

/**
 * The set of commits reachable from one repo's default branch head, in Durable Object SQLite. The set is
 * kept closed under ancestry, which is what lets a fast-forward be indexed by walking only new commits.
 * `gen` tags rows written by a rebuild so a finished rebuild can drop everything no longer reachable.
 */
export class CommitIndex {
  constructor(private readonly sql: SqlStorage) {
    sql.exec(`CREATE TABLE IF NOT EXISTS commits (
      sha TEXT PRIMARY KEY,
      email TEXT NOT NULL,
      name TEXT NOT NULL,
      authored_at INTEGER NOT NULL,
      day TEXT NOT NULL,
      gen INTEGER NOT NULL
    )`);
    sql.exec("CREATE INDEX IF NOT EXISTS commits_email_day ON commits(email, day)");
    sql.exec("CREATE INDEX IF NOT EXISTS commits_authored ON commits(authored_at)");
    sql.exec("CREATE INDEX IF NOT EXISTS commits_gen ON commits(gen)");
    sql.exec(`CREATE TABLE IF NOT EXISTS coauthors (
      sha TEXT NOT NULL,