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.

71 linesCodeBlameRaw
1/**
2 * What a not-found page says. A private project, work item or workspace and one
3 * that does not exist must look the same to someone who cannot see it, so
4 * the words depend only on what kind of thing the address names and on who
5 * is looking, never on whether it exists.
6 */
7
8/** The kind of thing an address names. */
9export type MissingKind = "project" | "item" | "pull" | "workspace" | "person" | "page";
10
11const KINDS: readonly MissingKind[] = ["project", "item", "pull", "workspace", "person", "page"];
12
13/** Names that are pages of g1t itself, never a workspace. */
14const RESERVED = new Set([
15 "settings", "explore", "search", "new", "u", "pricing", "avatars", "workspaces", "login", "logout",
16 "register", "verify", "confirm-email", "forgot", "reset", "device", "oauth", "invite",
17]);
18
19/**
20 * The kind a loader said it could not find (`data({ kind }, { status: 404 })`),
21 * else the kind the address names. Only the shape of the address is used.
22 */
23export function missingKind(errorData: unknown, pathname: string): MissingKind {
24 const said = (errorData as { kind?: unknown } | null | undefined)?.kind;
25 if (typeof said === "string" && (KINDS as readonly string[]).includes(said)) return said as MissingKind;
26 const parts = pathname.split("/").filter(Boolean);
27 if (parts[0] === "u" && parts.length === 2) return "person";
28 if (parts.length === 0 || RESERVED.has(parts[0]!)) return "page";
29 if (parts.length === 1) return "workspace";
30 if (parts[1] === "-") return parts[2] === "work" && parts.length === 4 && /^\d+$/.test(parts[3]!) ? "item" : "page";
31 if (parts.length === 2) return "project";
32 // An old issue's address: what it named, a work item now.
33 if (parts[2] === "issues" && parts.length === 4 && /^\d+$/.test(parts[3]!)) return "item";
34 if (parts[2] === "pull" && parts.length === 4 && /^\d+$/.test(parts[3]!)) return "pull";
35 return "page";
36}
37
38export type NotFoundCopy = {
39 title: string;
40 body: string;
41 /** Who is signed in, for "Signed in as". Null when no one is. */
42 signedInAs: string | null;
43 /** Offer to sign in and come back. */
44 signIn: boolean;
45};
46
47/**
48 * What the page says to whoever is looking. Signed out, it says that the
49 * page may be private, and to sign in if it is yours; signed in, that it
50 * does not exist or they cannot reach it. Never which of the two.
51 */
52export function notFoundCopy(kind: MissingKind, username: string | null | undefined): NotFoundCopy {
53 // People are public: a name either belongs to someone or it does not.
54 if (kind === "person") {
55 return { title: "Nothing here", body: "No one on g1t goes by that name.", signedInAs: null, signIn: false };
56 }
57 if (!username) {
58 return {
59 title: "Nothing here",
60 body: "This page doesn't exist, or it's private. Sign in if it's yours.",
61 signedInAs: null,
62 signIn: true,
63 };
64 }
65 return {
66 title: "Nothing here",
67 body: "This page doesn't exist, or you don't have access to it.",
68 signedInAs: username,
69 signIn: false,
70 };
71}