const timeZone = 'America/New_York'; interface HeatmapEntry { timestamp: number; contributions: number; } export interface GiteaProfile { baseUrl: string; username: string; profileUrl: string; } export function parseGiteaProfile(profileUrl: string | undefined): GiteaProfile | null { if (!profileUrl) return null; const url = new URL(profileUrl); const username = url.pathname.split('/').filter(Boolean)[0]; return username ? { baseUrl: url.origin, username, profileUrl } : null; } function easternDate(timestampSeconds: number): string { return new Intl.DateTimeFormat('en-CA', { timeZone, year: 'numeric', month: '2-digit', day: '2-digit' }).format( new Date(timestampSeconds * 1000) ); } export async function fetchGiteaDailyCounts({ baseUrl, username }: GiteaProfile): Promise> { const response = await fetch(`${baseUrl}/api/v1/users/${username}/heatmap`, { headers: { Accept: 'application/json' } }); if (!response.ok) throw new Error(`Gitea heatmap returned ${response.status}`); const entries: HeatmapEntry[] = await response.json(); const dailyCounts = new Map(); for (const { timestamp, contributions } of entries) { const date = easternDate(timestamp); dailyCounts.set(date, (dailyCounts.get(date) ?? 0) + contributions); } return dailyCounts; }