Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125import { isDid, type Did } from "@atcute/lexicons/syntax";import type { BobbinContext, XrpcRequestInit } from "$lib/api/client";import { resolveEmailToDid } from "$lib/api/emailDid";import { getBlame } from "$lib/api/gitmirror";import { resolveMiniDoc } from "$lib/api/identity";import { didFromSignature, splitMessage } from "$lib/api/repo";import type * as GetBlame from "$lib/api/lexicons/types/sh/tangled/git/temp2/getBlame";import type { Blame, BlameCommit } from "$lib/components/repo/blame/types";
type RunTable = { start: number; count: number; commit: string };
// knot and mirror payloads share the run-table contract: commits must be unique// and runs must be a gap-free, sorted cover of the file starting at line 1const toBlame = (commits: readonly BlameCommit[], runs: readonly RunTable[]): Blame => { const byShort = new Map<string, BlameCommit>(); for (const commit of commits) { if (byShort.has(commit.sha)) throw new Error(`duplicate blame commit: ${commit.sha}`); byShort.set(commit.sha, commit); } const blame: Blame = []; let nextLine = 1; for (const run of runs) { if (run.start !== nextLine || run.count < 1 || !Number.isSafeInteger(run.count)) { throw new Error(`invalid blame run at line ${run.start}`); } const commit = byShort.get(run.commit); if (!commit) throw new Error(`unknown blame commit: ${run.commit}`); blame.push({ start: run.start, count: run.count, commit }); nextLine += run.count; } return blame;};
// a did in the author slot is a tangled account signing its own commitsconst toAuthor = ({ name, email }: { name: string; email: string }) => ({ name, email, did: didFromSignature(email)});
const fromMirror = (result: GetBlame.$output): Blame => toBlame( result.commits.map((commit) => ({ sha: commit.oid, subject: splitMessage(commit.message)[0], when: commit.author.when, author: toAuthor(commit.author) })), result.runs );
export const blameFor = async ( ctx: BobbinContext, repo: Did, ref: string, path: string, init?: XrpcRequestInit): Promise<Blame> => fromMirror(await getBlame(ctx, { repo, ref, path }, init));
const MAX_AUTHOR_HYDRATIONS = 250;const AUTHOR_HYDRATION_CONCURRENCY = 8;
const mapConcurrent = async <T, U>( items: T[], concurrency: number, fn: (item: T) => Promise<U>): Promise<U[]> => { const results = new Array<U>(items.length); let next = 0; await Promise.all( Array.from({ length: Math.min(concurrency, items.length) }, async () => { while (next < items.length) { const index = next++; results[index] = await fn(items[index]); } }) ); return results;};
export const hydrateBlameAuthors = async (blame: Blame, ctx: BobbinContext): Promise<Blame> => { const emails = [ ...new Set( blame.flatMap(({ commit }) => commit.author.did || !commit.author.email ? [] : [commit.author.email] ) ) ].slice(0, MAX_AUTHOR_HYDRATIONS); const emailDids = await resolveEmailToDid(ctx.fetch, emails); const didFor = (email: string): string | undefined => isDid(email) ? email : emailDids.get(email); const dids = [ ...new Set( blame.flatMap(({ commit }) => { const did = commit.author.did ?? didFor(commit.author.email); return did ? [did] : []; }) ) ].slice(0, MAX_AUTHOR_HYDRATIONS); const docs = await mapConcurrent( dids, AUTHOR_HYDRATION_CONCURRENCY, async (did) => [did, await resolveMiniDoc(ctx, did).catch(() => null)] as const ); const handles = new Map( docs.flatMap(([did, doc]) => doc && !doc.handle.endsWith(".invalid") ? [[did, doc.handle] as const] : [] ) ); return blame.map((run) => { const did = run.commit.author.did ?? didFor(run.commit.author.email); return { ...run, commit: { ...run.commit, author: { ...run.commit.author, did, handle: did ? handles.get(did) : undefined } } }; });};