git-contributor-stats
Version:
CLI to compute contributor and repository statistics from a Git repository (commits, lines added/deleted, frequency, heatmap, bus-factor), with filters and multiple output formats.
1 lines • 40.5 kB
Source Map (JSON)
{"version":3,"file":"analytics-SL4YC1kG.mjs","sources":["../../src/analytics/aggregator.ts","../../src/analytics/aliases.ts","../../src/analytics/analyzer.ts"],"sourcesContent":["import { formatNumber } from '../utils/formatting.ts';\nimport { normalizeContributorName } from '../utils/normalization.ts';\nimport { calculateSimilarityScore } from '../utils/similarity.ts';\n\nexport interface ContributorBasic {\n key: string;\n name: string;\n emails: string[];\n commits: number;\n additions: number;\n deletions: number;\n changes: number;\n firstCommitDate?: string;\n lastCommitDate?: string;\n}\n\ntype Commit = {\n authorName?: string;\n authorEmail?: string;\n additions?: number;\n deletions?: number;\n date?: string | Date;\n};\n\ntype AggregationData = {\n key: string;\n name: string;\n emails: Set<string>;\n commits: number;\n additions: number;\n deletions: number;\n firstCommitDate?: Date;\n lastCommitDate?: Date;\n};\n\nexport interface AggregationOptions {\n groupBy: 'name' | 'email';\n aliasResolver?: ((n: string, name?: string, email?: string) => string) | null;\n canonicalDetails?: Map<string, { name?: string; email?: string }>;\n similarity?: number;\n}\n\nexport function normalizeKey(\n commit: Commit,\n groupBy: 'name' | 'email',\n aliasResolver?: ((n: string, name?: string, email?: string) => string) | null\n): string {\n const name = commit.authorName || '';\n const email = commit.authorEmail || '';\n const valueToNormalize = groupBy === 'email' ? email || name : name || email;\n const baseNorm = normalizeContributorName(valueToNormalize);\n return aliasResolver ? aliasResolver(baseNorm, name, email) : baseNorm;\n}\n\nexport function getDisplayDetails(\n normalizedKey: string,\n defaultName: string,\n defaultEmail: string,\n canonicalDetails?: Map<string, { name?: string; email?: string }>\n): { name: string; email: string } {\n if (canonicalDetails?.has(normalizedKey)) {\n const info = canonicalDetails.get(normalizedKey);\n if (info) {\n return {\n name: info.name || defaultName,\n email: info.email || defaultEmail\n };\n }\n }\n return { name: defaultName, email: defaultEmail };\n}\n\nexport function findSimilarKey(\n norm: string,\n existingKeys: string[],\n threshold: number\n): string | null {\n for (const mk of existingKeys) {\n const sim = calculateSimilarityScore(norm, mk);\n if (sim >= threshold) {\n return mk;\n }\n }\n return null;\n}\n\nfunction mergeEmails(target: Set<string>, source: Set<string>): void {\n for (const email of source) {\n target.add(email);\n }\n}\n\nfunction mergeFirstCommitDate(target: AggregationData, source: AggregationData): void {\n if (\n source.firstCommitDate &&\n (!target.firstCommitDate || source.firstCommitDate < target.firstCommitDate)\n ) {\n target.firstCommitDate = source.firstCommitDate;\n }\n}\n\nfunction mergeLastCommitDate(target: AggregationData, source: AggregationData): void {\n if (\n source.lastCommitDate &&\n (!target.lastCommitDate || source.lastCommitDate > target.lastCommitDate)\n ) {\n target.lastCommitDate = source.lastCommitDate;\n }\n}\n\nfunction mergeAggregationData(target: AggregationData, source: AggregationData): void {\n target.commits += source.commits;\n target.additions += source.additions;\n target.deletions += source.deletions;\n mergeEmails(target.emails, source.emails);\n mergeFirstCommitDate(target, source);\n mergeLastCommitDate(target, source);\n}\n\nfunction applySimilarityMerging(\n aggregations: AggregationData[],\n threshold: number\n): AggregationData[] {\n const merged: AggregationData[] = [];\n\n for (const agg of aggregations) {\n const similarKey = findSimilarKey(\n agg.key,\n merged.map((m) => m.key),\n threshold\n );\n\n if (similarKey) {\n const target = merged.find((m) => m.key === similarKey);\n if (target) {\n mergeAggregationData(target, agg);\n }\n } else {\n merged.push(agg);\n }\n }\n\n return merged;\n}\n\nfunction createInitialAggregation(commit: Commit, key: string): AggregationData {\n return {\n key,\n name: commit.authorName || '',\n emails: new Set(),\n commits: 0,\n additions: 0,\n deletions: 0,\n firstCommitDate: commit.date ? new Date(commit.date) : undefined,\n lastCommitDate: commit.date ? new Date(commit.date) : undefined\n };\n}\n\nfunction updateAggregation(agg: AggregationData, commit: Commit): void {\n agg.name = agg.name || commit.authorName || '';\n\n if (commit.authorEmail) {\n agg.emails.add(commit.authorEmail.toLowerCase());\n }\n\n agg.commits += 1;\n agg.additions += commit.additions || 0;\n agg.deletions += commit.deletions || 0;\n\n updateCommitDates(agg, commit);\n}\n\nfunction updateCommitDates(agg: AggregationData, commit: Commit): void {\n if (!commit.date) return;\n\n const date = new Date(commit.date);\n\n if (!agg.firstCommitDate || date < agg.firstCommitDate) {\n agg.firstCommitDate = date;\n }\n\n if (!agg.lastCommitDate || date > agg.lastCommitDate) {\n agg.lastCommitDate = date;\n }\n}\n\nfunction convertToContributor(agg: AggregationData, groupBy: 'name' | 'email'): ContributorBasic {\n return {\n key: agg.key,\n name: agg.name || (groupBy === 'name' ? agg.key : ''),\n emails: Array.from(agg.emails),\n commits: agg.commits,\n additions: agg.additions,\n deletions: agg.deletions,\n changes: agg.additions + agg.deletions,\n firstCommitDate: agg.firstCommitDate ? agg.firstCommitDate.toISOString() : undefined,\n lastCommitDate: agg.lastCommitDate ? agg.lastCommitDate.toISOString() : undefined\n } as ContributorBasic;\n}\n\nexport function aggregateBasic(commits: Commit[], options: AggregationOptions): ContributorBasic[] {\n const { groupBy, aliasResolver, canonicalDetails, similarity } = options;\n const map = new Map<string, AggregationData>();\n\n for (const commit of commits) {\n const key = normalizeKey(commit, groupBy, aliasResolver);\n\n if (!map.has(key)) {\n const { name, email } = getDisplayDetails(\n key,\n commit.authorName || '',\n commit.authorEmail || '',\n canonicalDetails\n );\n const agg = createInitialAggregation(commit, key);\n agg.name = name || agg.name;\n if (email) {\n agg.emails.add(email.toLowerCase());\n }\n map.set(key, agg);\n }\n\n const agg = map.get(key);\n if (agg) {\n updateAggregation(agg, commit);\n }\n }\n\n let aggregations = Array.from(map.values());\n\n if (similarity !== undefined && similarity > 0) {\n aggregations = applySimilarityMerging(aggregations, similarity);\n }\n\n return aggregations.map((agg) => convertToContributor(agg, groupBy));\n}\n\nexport type SortItem = { commits: number; changes: number; additions?: number; deletions?: number };\nexport function pickSortMetric(by?: string) {\n switch ((by || '').toLowerCase()) {\n case 'commits':\n return (a: SortItem, b: SortItem) => b.commits - a.commits || b.changes - a.changes;\n case 'additions':\n case 'adds':\n case 'lines-added':\n return (a: SortItem, b: SortItem) =>\n (b.additions || 0) - (a.additions || 0) || b.commits - a.commits;\n case 'deletions':\n case 'dels':\n case 'lines-deleted':\n return (a: SortItem, b: SortItem) =>\n (b.deletions || 0) - (a.deletions || 0) || b.commits - a.commits;\n default:\n return (a: SortItem, b: SortItem) => b.changes - a.changes || b.commits - a.commits;\n }\n}\n\nexport function computeMeta(contributors: ContributorBasic[]) {\n let commits = 0,\n additions = 0,\n deletions = 0;\n let first: Date | undefined, last: Date | undefined;\n\n for (const c of contributors) {\n commits += c.commits || 0;\n additions += c.additions || 0;\n deletions += c.deletions || 0;\n if (c.firstCommitDate) {\n const d = new Date(c.firstCommitDate);\n if (!first || d < first) first = d;\n }\n if (c.lastCommitDate) {\n const d = new Date(c.lastCommitDate);\n if (!last || d > last) last = d;\n }\n }\n\n return {\n contributors: contributors.length,\n commits,\n additions,\n deletions,\n firstCommitDate: first ? first.toISOString() : undefined,\n lastCommitDate: last ? last.toISOString() : undefined\n };\n}\n\nexport interface ContributorsMeta {\n contributors: number;\n commits: number;\n additions: number;\n deletions: number;\n firstCommitDate?: string;\n lastCommitDate?: string;\n}\n\nexport function printTable(\n contributors: ContributorBasic[],\n meta: ContributorsMeta,\n labelBy: 'name' | 'email' = 'name'\n) {\n const headers = [\n '#',\n labelBy === 'name' ? 'Author' : 'Email',\n 'Commits',\n '+Additions',\n '-Deletions',\n '±Changes'\n ];\n const rows: string[][] = [];\n\n for (let idx = 0; idx < contributors.length; idx++) {\n const c = contributors[idx];\n const label = labelBy === 'name' ? c.name || '(unknown)' : c.key || '(unknown)';\n rows.push([\n String(idx + 1),\n label,\n formatNumber(c.commits),\n formatNumber(c.additions),\n formatNumber(c.deletions),\n formatNumber(c.changes)\n ]);\n }\n\n const colWidths = headers.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] ? String(r[i]).length : 0)))\n );\n\n const headerLine = headers\n .map((h, i) => (i === 1 ? String(h).padEnd(colWidths[i]) : String(h).padStart(colWidths[i])))\n .join(' ');\n\n const sepLine = colWidths.map((w) => '-'.repeat(w)).join(' ');\n\n console.log(headerLine);\n console.log(sepLine);\n\n for (const r of rows) {\n const line = r\n .map((cell, i) =>\n i === 1 ? String(cell).padEnd(colWidths[i]) : String(cell).padStart(colWidths[i])\n )\n .join(' ');\n console.log(line);\n }\n\n console.log();\n console.log(\n `Contributors: ${formatNumber(meta.contributors)} | Commits: ${formatNumber(meta.commits)} | Changes: ${formatNumber(\n meta.additions + meta.deletions\n )} (+${formatNumber(meta.additions)} / -${formatNumber(meta.deletions)})`\n );\n\n if (meta.firstCommitDate || meta.lastCommitDate) {\n console.log(\n `Range: ${meta.firstCommitDate ? new Date(meta.firstCommitDate).toISOString().slice(0, 10) : '—'} → ${\n meta.lastCommitDate ? new Date(meta.lastCommitDate).toISOString().slice(0, 10) : '—'\n }`\n );\n }\n}\n\nexport function printCSV(contributors: ContributorBasic[], labelBy: 'name' | 'email' = 'name') {\n const header = [\n 'rank',\n labelBy === 'name' ? 'author' : 'email',\n 'commits',\n 'additions',\n 'deletions',\n 'changes'\n ];\n console.log(header.join(','));\n\n for (let i = 0; i < contributors.length; i++) {\n const c = contributors[i];\n const label = labelBy === 'name' ? c.name || '' : c.key || '';\n console.log([i + 1, label, c.commits, c.additions, c.deletions, c.changes].join(','));\n }\n}\n","import { normalizeContributorName } from '../utils/normalization.ts';\n\nexport type AliasGroup = string | RegExp | string[];\n\nexport interface AliasCanonicalInfo {\n name?: string;\n email?: string;\n}\n\nexport interface AliasConfigObject {\n groups?: AliasGroup[];\n map?: Record<string, string>;\n canonical?: Record<string, AliasCanonicalInfo>;\n [key: string]:\n | string\n | AliasGroup[]\n | Record<string, string>\n | Record<string, AliasCanonicalInfo>\n | undefined;\n}\n\nexport type AliasConfig = AliasGroup[] | AliasConfigObject | undefined;\n\ninterface ParsedConfig {\n mapEntries: Array<[string, string]>;\n groups: Array<string | RegExp | Array<string>>;\n canonicalDetails: Map<string, { name?: string; email?: string }>;\n}\n\nfunction extractMapEntries(config: AliasConfigObject): Array<[string, string]> {\n if (config.map && typeof config.map === 'object') {\n return Object.entries(config.map);\n }\n\n // Check for flat map structure\n const flatMapCandidates = Object.keys(config).filter(\n (k) => k !== 'groups' && k !== 'map' && k !== 'canonical'\n );\n\n if (flatMapCandidates.length === 0) {\n return [];\n }\n\n return Object.entries(config)\n .filter(([k, v]) => k !== 'groups' && k !== 'canonical' && typeof v === 'string')\n .map(([k, v]) => [k, v as string] as [string, string]);\n}\n\nfunction extractCanonicalDetails(\n config: AliasConfigObject\n): Map<string, { name?: string; email?: string }> {\n const canonicalDetails = new Map<string, { name?: string; email?: string }>();\n\n if (!config.canonical || typeof config.canonical !== 'object') {\n return canonicalDetails;\n }\n\n for (const [canonKey, info] of Object.entries(config.canonical)) {\n const normKey = normalizeContributorName(canonKey);\n const infoObj = info;\n canonicalDetails.set(normKey, {\n name: (infoObj && typeof infoObj.name === 'string' ? infoObj.name : undefined) || undefined,\n email: (infoObj && typeof infoObj.email === 'string' ? infoObj.email : undefined) || undefined\n });\n }\n\n return canonicalDetails;\n}\n\nfunction parseAliasConfig(config: AliasConfig): ParsedConfig {\n const emptyResult: ParsedConfig = {\n mapEntries: [],\n groups: [],\n canonicalDetails: new Map<string, { name?: string; email?: string }>()\n };\n\n if (!config) {\n return emptyResult;\n }\n\n if (Array.isArray(config)) {\n return {\n ...emptyResult,\n groups: config as Array<string | RegExp | string[]>\n };\n }\n\n if (typeof config === 'object') {\n const groups = Array.isArray(config.groups) ? config.groups : [];\n const mapEntries = extractMapEntries(config);\n const canonicalDetails = extractCanonicalDetails(config);\n\n return { mapEntries, groups, canonicalDetails };\n }\n\n return emptyResult;\n}\n\nfunction parseRegexPattern(pattern: string): RegExp | null {\n if (!pattern.startsWith('/') || pattern.lastIndexOf('/') <= 0) {\n return null;\n }\n\n const lastSlash = pattern.lastIndexOf('/');\n const regexPattern = pattern.slice(1, lastSlash);\n const flags = pattern.slice(lastSlash + 1);\n\n try {\n return new RegExp(regexPattern, flags);\n } catch (_error) {\n // Skip invalid regex patterns\n return null;\n }\n}\n\nfunction processMapEntries(\n mapEntries: Array<[string, string]>,\n aliasMap: Map<string, string>,\n regexList: Array<{ regex: RegExp; canonical: string }>\n): void {\n for (const [alias, canonical] of mapEntries) {\n const regex = parseRegexPattern(alias);\n\n if (regex) {\n regexList.push({ regex, canonical: normalizeContributorName(canonical) });\n } else {\n aliasMap.set(normalizeContributorName(alias), normalizeContributorName(canonical));\n }\n }\n}\n\nfunction processGroups(\n groups: Array<string | RegExp | Array<string>>,\n aliasMap: Map<string, string>,\n regexList: Array<{ regex: RegExp; canonical: string }>\n): void {\n for (const g of groups) {\n if (!Array.isArray(g) || g.length === 0) continue;\n\n const canonicalCandidate = g.find((s) => s.includes('@')) || g[0];\n const canonicalNorm = normalizeContributorName(String(canonicalCandidate));\n\n for (const item of g) {\n const regex = parseRegexPattern(item);\n if (regex) {\n regexList.push({ regex, canonical: canonicalNorm });\n } else {\n aliasMap.set(normalizeContributorName(item), canonicalNorm);\n }\n }\n }\n}\n\nfunction createResolveFunction(\n aliasMap: Map<string, string>,\n regexList: Array<{ regex: RegExp; canonical: string }>\n) {\n return function resolve(baseNorm: string, name?: string, email?: string): string {\n const mapped = aliasMap.get(baseNorm);\n if (mapped) return mapped;\n\n const rawName = name || '';\n const rawEmail = email || '';\n\n for (const { regex, canonical } of regexList) {\n try {\n if (regex.test(rawName) || regex.test(rawEmail)) {\n return canonical;\n }\n } catch (_error) {\n // Skip regex test failures and continue to next pattern\n }\n }\n\n return baseNorm;\n };\n}\n\nexport function buildAliasResolver(config?: AliasConfig) {\n if (!config) {\n return {\n resolve: null as null | ((n: string, name?: string, email?: string) => string),\n canonicalDetails: new Map<string, { name?: string; email?: string }>()\n };\n }\n\n const { mapEntries, groups, canonicalDetails } = parseAliasConfig(config);\n const aliasMap = new Map<string, string>();\n const regexList: Array<{ regex: RegExp; canonical: string }> = [];\n\n processMapEntries(mapEntries, aliasMap, regexList);\n processGroups(groups, aliasMap, regexList);\n\n const resolve = createResolveFunction(aliasMap, regexList);\n\n return { resolve, canonicalDetails };\n}\n","import { isoWeekKey } from '../utils/dates.ts';\nimport { findSimilarKey, getDisplayDetails, normalizeKey } from './aggregator.ts';\n\nexport interface FileStats {\n changes: number;\n added: number;\n deleted: number;\n}\n\nexport interface TopFileEntry {\n filename: string;\n changes: number;\n added: number;\n deleted: number;\n}\n\nexport interface TopContributor {\n name?: string;\n email?: string;\n commits: number;\n added: number;\n deleted: number;\n net: number;\n changes: number;\n files: Record<string, FileStats>;\n topFiles: TopFileEntry[];\n}\n\nexport interface ContributorsMapEntry {\n name?: string;\n email?: string;\n commits: number;\n added: number;\n deleted: number;\n files: Record<string, FileStats>;\n}\n\nexport interface TopStatsSummary {\n byCommits: TopContributor | null;\n byAdditions: TopContributor | null;\n byDeletions: TopContributor | null;\n byNet: TopContributor | null;\n byChanges: TopContributor | null;\n}\n\ntype Commit = {\n authorName?: string;\n authorEmail?: string;\n date?: string | Date;\n files?: Array<{ filename: string; added: number; deleted: number }>;\n};\n\nfunction mergeFilesIntoTarget(target: ContributorsMapEntry, src: ContributorsMapEntry): void {\n for (const [fName, info] of Object.entries(src.files || {})) {\n const inf = info as { changes: number; added: number; deleted: number };\n if (!target.files[fName]) {\n target.files[fName] = { changes: 0, added: 0, deleted: 0 };\n }\n target.files[fName].changes += inf.changes;\n target.files[fName].added += inf.added;\n target.files[fName].deleted += inf.deleted;\n }\n}\n\nfunction mergeContributorIntoTarget(target: ContributorsMapEntry, src: ContributorsMapEntry): void {\n target.commits += src.commits;\n target.added += src.added;\n target.deleted += src.deleted;\n mergeFilesIntoTarget(target, src);\n}\n\nexport function mergeSimilarContributors(\n contribMap: Record<string, ContributorsMapEntry>,\n threshold: number\n) {\n const keys = Object.keys(contribMap);\n const merged: Record<string, ContributorsMapEntry & { normalized?: string }> = {};\n\n for (const key of keys) {\n const found = findSimilarKey(key, Object.keys(merged), threshold);\n\n if (found) {\n mergeContributorIntoTarget(merged[found], contribMap[key]);\n } else {\n const src = contribMap[key];\n merged[key] = {\n normalized: key,\n name: src.name,\n email: src.email,\n commits: src.commits,\n added: src.added,\n deleted: src.deleted,\n files: { ...src.files }\n } as ContributorsMapEntry & { normalized?: string };\n }\n }\n\n return merged;\n}\n\nfunction getOrCreateContributor(\n contribMap: Record<string, ContributorsMapEntry>,\n normalized: string,\n name: string,\n email: string,\n canonicalDetails?: Map<string, { name?: string; email?: string }>\n): ContributorsMapEntry {\n if (!contribMap[normalized]) {\n const { name: displayName, email: displayEmail } = getDisplayDetails(\n normalized,\n name,\n email,\n canonicalDetails\n );\n contribMap[normalized] = {\n name: displayName,\n email: displayEmail,\n commits: 0,\n added: 0,\n deleted: 0,\n files: {}\n } as ContributorsMapEntry;\n }\n return contribMap[normalized];\n}\n\nfunction updateCommitFrequency(\n date: Date | null,\n commitFrequencyMonthly: Record<string, number>,\n commitFrequencyWeekly: Record<string, number>,\n heatmap: number[][],\n heatmapContributors: Record<string, Record<string, number>>,\n contributorName: string\n): void {\n if (!date) return;\n\n const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n commitFrequencyMonthly[monthKey] = (commitFrequencyMonthly[monthKey] || 0) + 1;\n\n const weekKey = isoWeekKey(date);\n commitFrequencyWeekly[weekKey] = (commitFrequencyWeekly[weekKey] || 0) + 1;\n\n const day = date.getDay();\n const hour = date.getHours();\n heatmap[day][hour] += 1;\n\n // Track contributors for this time slot\n const key = `${day}-${hour}`;\n if (!heatmapContributors[key]) {\n heatmapContributors[key] = {};\n }\n heatmapContributors[key][contributorName] = (heatmapContributors[key][contributorName] || 0) + 1;\n}\n\nfunction processCommitFiles(\n commit: Commit,\n contrib: ContributorsMapEntry,\n fileToContributors: Record<string, Set<string>>,\n normalized: string\n): void {\n for (const f of commit.files || []) {\n const fName = f.filename;\n contrib.added += f.added;\n contrib.deleted += f.deleted;\n\n if (!contrib.files[fName]) {\n contrib.files[fName] = { changes: 0, added: 0, deleted: 0 };\n }\n contrib.files[fName].changes += f.added + f.deleted;\n contrib.files[fName].added += f.added;\n contrib.files[fName].deleted += f.deleted;\n\n if (!fileToContributors[fName]) {\n fileToContributors[fName] = new Set();\n }\n fileToContributors[fName].add(normalized);\n }\n}\n\nfunction buildTopContributors(\n merged: Record<string, ContributorsMapEntry & { normalized?: string }>\n): TopContributor[] {\n return Object.values(merged)\n .map((c: ContributorsMapEntry & { normalized?: string }) => {\n const filesArr: TopFileEntry[] = Object.entries(c.files || {}).map(([filename, info]) => {\n const inf = info as { changes: number; added: number; deleted: number };\n return {\n filename,\n changes: inf.changes,\n added: inf.added,\n deleted: inf.deleted\n };\n });\n filesArr.sort((a, b) => b.changes - a.changes);\n\n return {\n name: c.name,\n email: c.email,\n commits: c.commits,\n added: c.added,\n deleted: c.deleted,\n net: c.added - c.deleted,\n changes: c.added + c.deleted,\n files: c.files,\n topFiles: filesArr\n } as TopContributor;\n })\n .sort((a, b) => b.commits - a.commits);\n}\n\nfunction buildBusFactor(\n fileToContributors: Record<string, Set<string>>,\n merged: Record<string, ContributorsMapEntry & { normalized?: string }>,\n contribMap: Record<string, ContributorsMapEntry>\n): Array<{ file: string; owner: string; changes: number }> {\n const filesSingleOwner: Array<{ file: string; owner: string; changes: number }> = [];\n\n for (const [file, ownersSet] of Object.entries(fileToContributors)) {\n const owners = Array.from(ownersSet);\n if (owners.length === 1) {\n const owner = owners[0];\n const m = (merged as Record<string, ContributorsMapEntry>)[owner] ||\n contribMap[owner] || { name: owner };\n const ownerEntry =\n (merged as Record<string, ContributorsMapEntry>)[owner] || contribMap[owner];\n const changes = ownerEntry?.files?.[file]?.changes ?? 0;\n filesSingleOwner.push({ file, owner: m.name || owner, changes });\n }\n }\n\n filesSingleOwner.sort((a, b) => b.changes - a.changes);\n return filesSingleOwner;\n}\n\nfunction buildTopStats(topContributors: TopContributor[]): TopStatsSummary {\n function topBy(metric: keyof TopContributor) {\n const arr = [...topContributors];\n arr.sort((a, b) => ((b[metric] as number) || 0) - ((a[metric] as number) || 0));\n return arr[0] || null;\n }\n\n return {\n byCommits: topBy('commits'),\n byAdditions: topBy('added'),\n byDeletions: topBy('deleted'),\n byNet: topBy('net'),\n byChanges: topBy('changes')\n };\n}\n\nexport function analyze(\n commits: Commit[],\n similarityThreshold: number,\n aliasResolver: ((n: string, name?: string, email?: string) => string) | null,\n canonicalDetails?: Map<string, { name?: string; email?: string }>,\n groupBy: 'email' | 'name' = 'email'\n) {\n const contribMap: Record<string, ContributorsMapEntry> = {};\n const fileToContributors: Record<string, Set<string>> = {};\n let totalCommits = 0;\n\n const commitFrequencyMonthly: Record<string, number> = {};\n const commitFrequencyWeekly: Record<string, number> = {};\n const heatmap: number[][] = Array.from({ length: 7 }, () => Array.from({ length: 24 }, () => 0));\n const heatmapContributors: Record<string, Record<string, number>> = {};\n\n for (const commit of commits) {\n totalCommits++;\n const name = commit.authorName || '';\n const email = commit.authorEmail || '';\n\n const normalized = normalizeKey(\n commit as { authorName?: string; authorEmail?: string },\n groupBy,\n aliasResolver\n );\n\n const contrib = getOrCreateContributor(contribMap, normalized, name, email, canonicalDetails);\n contrib.commits += 1;\n\n const date = commit.date ? new Date(commit.date) : null;\n updateCommitFrequency(\n date,\n commitFrequencyMonthly,\n commitFrequencyWeekly,\n heatmap,\n heatmapContributors,\n name\n );\n processCommitFiles(commit, contrib, fileToContributors, normalized);\n }\n\n const merged = mergeSimilarContributors(contribMap, similarityThreshold);\n const topContributors = buildTopContributors(merged);\n const filesSingleOwner = buildBusFactor(fileToContributors, merged, contribMap);\n const topStats = buildTopStats(topContributors);\n\n // Provide all required BusFactorInfo properties\n const busFactorInfo = {\n busFactor: 0, // TODO: implement actual bus factor calculation if needed\n candidates: [], // TODO: implement candidate calculation if needed\n details: undefined, // or provide details if available\n filesSingleOwner\n };\n\n return {\n contributors: merged,\n topContributors,\n totalCommits,\n commitFrequency: { monthly: commitFrequencyMonthly, weekly: commitFrequencyWeekly },\n heatmap,\n heatmapContributors,\n busFactor: busFactorInfo,\n topStats\n } as const;\n}\n"],"names":["agg"],"mappings":"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"}