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.

g1t.sh
56 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1import type { Invite, InvitesOverview, User } from "@g1t/contracts";
2
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)3import { type BringInto, bringIntoChoices, inviteDraft, peoplePages } from "./invites";
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)4import { readCookie } from "./mission";
5import { billing, identity } from "./services.server";
6import { WORKSPACE_COOKIE, chosenWorkspace } from "./workspace-choice";
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily7
8/** What the Invites section's forms answer. */
9export type InviteActionData = { inviteCreated?: Invite; inviteError?: string; inviteRevoked?: boolean } | null;
10
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)11/**
12 * The workspaces an invite to g1t can also invite its person to
13 * (`bringIntoChoices`), and the People pages where a workspace invitation
14 * is made instead (`peoplePages`).
15 */
16export type BringIntoChoices = {
17 options: BringInto[];
18 note: string | null;
19 people: { slug: string; name: string; to: string }[];
20};
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)21
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily22/** A person's invites, or null when they cannot make any yet (email unconfirmed). */
23export async function loadInvites(user: User): Promise<InvitesOverview | null> {
24 if (!user.verified) return null;
25 return identity.listInvites(user).catch(() => null);
26}
27
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)28/**
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)29 * The workspaces an invite can also invite its person to, and the People
30 * pages to invite someone to a workspace from, the one the person is in
31 * (the shell's current workspace, from its cookie) first. A workspace
32 * whose plan cannot be checked is not offered: identity would refuse it.
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)33 */
34export async function loadBringInto(user: User, request: Request): Promise<BringIntoChoices> {
35 const memberships = user.workspaces ?? [];
36 const owned = memberships.filter((m) => m.role === "owner").map((m) => m.slug);
37 const free = owned.length > 0 ? await billing.freeWorkspaces(owned).catch(() => owned) : [];
38 const current = chosenWorkspace(memberships, readCookie(request.headers.get("cookie"), WORKSPACE_COOKIE))?.slug ?? null;
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)39 return { ...bringIntoChoices(memberships, free, current), people: peoplePages(memberships, current) };
Merge workspace invitations: nobody joins a workspace without saying yes, people are found by username, your own invites can bring someone in, and nobody is left without a workspace (identity 0040)40}
41
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily42/** Handles the Invites section's intents; null for any other intent. */
43export async function inviteAction(user: User, form: FormData): Promise<InviteActionData | undefined> {
44 switch (form.get("intent")) {
45 case "create-invite": {
Merge two kinds of invite, kept apart: an invite to g1t (Settings, invite-only only, no workspace unless asked) and an invitation to a workspace (its People page)46 // A workspace goes with it only when its box is ticked (`inviteDraft`).
47 const result = await identity.createInvite(user, inviteDraft(form));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily48 return result.ok ? { inviteCreated: result.value } : { inviteError: result.error.message };
49 }
50 case "revoke-invite": {
51 const result = await identity.revokeInvite(user, String(form.get("id") ?? ""));
52 return result.ok ? { inviteRevoked: true } : { inviteError: result.error.message };
53 }
54 }
55 return undefined;
56}

This file's history is long; its oldest lines are credited to the oldest commit read.