export type FeedKind = "issue" | "pull" | "release" | "discussion";
export interface FeedItem {
kind: FeedKind;
number: number;
title: string;
url: string;
state: "open" | "closed" | "merged" | "draft" | "published";
author: string;
authorAvatar: string;
createdAt: string;
updatedAt: string;
comments: number;
labels: { name: string; color: string }[];
body?: string;
tag?: string;
* When the item entered the state it is in — `merged_at`, `closed_at`,
* `published_at`, or `created_at`. `updatedAt` still orders the feed, but a
* surface that prints a verb ("merged", "opened") must date that verb, not
* the last comment on the thread: an issue opened in March and commented on
* today is not "opened, just now".
*/
eventAt?: string;
* GitHub's own `author_association === "FIRST_TIME_CONTRIBUTOR"` verdict,
* copied verbatim from the list payload — never inferred by us, and never
* derived from the size of the window we happened to fetch. GitHub
* recomputes the association as the author gains commits, so this marks a
* newcomer's contribution while it is still new, and stops on its own.
*/
firstTimeContributor?: boolean;
}
export interface RepoStats {
stars: number;
forks: number;
openIssues: number;
openPulls: number;
contributors: number;
latestRelease?: { tag: string; publishedAt: string; url: string };
fetchedAt: string;
}
export interface CuratedDispatch {
generatedAt: string;
headline: string;
summary: string;
highlights: { title: string; href: string; tag: string; blurb: string }[];
movers: { number: number; title: string; href: string; reason: string }[];
headlineZh?: string;
summaryZh?: string;
highlightsZh?: { title: string; href: string; tag: string; blurb: string }[];
moversZh?: { number: number; title: string; href: string; reason: string }[];
}