import { all, chunks } from "./db";
import { accountDb, repoDb, stmt } from "./scoped-db";
import type { ScopedStatement } from "./scoped-db";
import { backfillDone, backfillRepos } from "./backfill";
/**
* Activity rows live in each repository's RepoState; the contribution graph needs "this account's activity in a
* time range". Each actor's AccountState keeps `activity_repos` (repository, first and last counted activity) so
* the graph visits only those repositories. RepoState feeds it from every activity insert it runs.
*/
export const ACTIVITY_INDEX = "activity-repos";
/** Kinds that never count as contributions (contributions.ts COUNTED_ACTIVITY) and so are not indexed. */
const UNCOUNTED = new Set(["view", "push"]);
const ACTIVITY_INSERT = /^\s*INSERT\s+INTO\s+activity\s*\(\s*id\s*,\s*actor_id\s*,\s*repo_id\s*,\s*kind\s*,/i;
export type ActivityTouch = { actorId: string; repoId: string; at: number; counted: boolean };
/** The activity inserts among `statements` (repoActivityStatement's shape), for the actor index. */
export function activityTouches(statements: ScopedStatement[]): ActivityTouch[] {
const out: ActivityTouch[] = [];
for (const { sql, params } of statements) {
if (!ACTIVITY_INSERT.test(sql)) continue;
const [, actorId, repoId, kind, , createdAt] = params;
if (typeof actorId !== "string" || typeof repoId !== "string") continue;
out.push({ actorId, repoId, at: Number(createdAt) || Date.now(), counted: !UNCOUNTED.has(String(kind)) });
}
return out;
}
const UPSERT = `INSERT INTO activity_repos (repo_id, first_at, last_at, touched_at) VALUES (?1, ?2, ?3, ?4)
ON CONFLICT (repo_id) DO UPDATE SET
first_at = CASE WHEN excluded.first_at IS NULL THEN first_at ELSE MIN(COALESCE(first_at, excluded.first_at), excluded.first_at) END,
last_at = CASE WHEN excluded.last_at IS NULL THEN last_at ELSE MAX(COALESCE(last_at, excluded.last_at), excluded.last_at) END,
touched_at = MAX(touched_at, excluded.touched_at)`;
export async function indexActivity(touches: ActivityTouch[]): Promise<void> {
await Promise.all(
touches.map((touch) =>
accountDb(touch.actorId).run(