Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

46 linesCodeBlameRaw
1/**
2 * The Files page's About, streamed in after the files: what the default
3 * branch says, stars and releases from repos; how many watch it from the
4 * inbox; packages published from it; how many tags it has. Each is its own promise, so a slow
5 * one holds up only its own section, and a failure stays in its section
6 * rather than failing the page: what the default branch says and the
7 * packages say they could not be loaded (lib/partial.ts), and the counts
8 * of watchers and tags, which only add to a line, are left out.
9 */
10import type { PackageSummary, RepoAbout, RepoPath, Viewer } from "@g1t/contracts";
11
12import type { AboutData } from "../components/repo-about";
13import { type Failed, part } from "./partial";
14import { inbox, packages, repos } from "./services.server";
15
16/** How long a section waits before it shows without its answer. */
17const WAIT_MS = 4_000;
18
19function within<T>(promise: Promise<T>): Promise<T | null> {
20 return Promise.race([promise.catch(() => null), new Promise<null>((resolve) => setTimeout(() => resolve(null), WAIT_MS))]);
21}
22
23/** Like `within`, but one that fails or does not answer in time is `Failed`, for its section to say so. */
24function partWithin<T>(promise: Promise<T>, what: string): Promise<T | Failed | null> {
25 // Slow is not failed: a large repository's About can take longer than
26 // the page waits, and then it is quietly left out, as it always was.
27 // Only a service that did not answer or answered with an error says so.
28 const late = new Promise<null>((resolve) => setTimeout(() => resolve(null), WAIT_MS));
29 return part(Promise.race([promise, late]), what);
30}
31
32export function aboutOf(path: RepoPath, viewer: Viewer): Promise<RepoAbout | null> {
33 return repos.about(path, viewer).then((found) => (found.ok ? found.value : null));
34}
35
36export function aboutFor(path: RepoPath, viewer: Viewer, repoId: string): AboutData {
37 return {
38 about: partWithin(aboutOf(path, viewer), "Releases, contributors and languages"),
39 watchers: within(inbox.watchers(repoId)),
40 packages: partWithin(
41 packages.list(path.namespace, viewer, { repoId }).then((found): PackageSummary[] | null => (found.ok ? found.value : null)),
42 "Packages",
43 ),
44 tags: within(repos.tags(path, viewer).then((found) => (found.ok ? found.value.length : null))),
45 };
46}