Skip to content
78 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.

The phone app: Chat in full with phone notifications, plus Code and Customize (#4)1/**
2 * Which g1t the app talks to. Hosted g1t is the site `https://g1t.sh` and
3 * the API `https://api.g1t.sh`; a self-hosted g1t sets its own (SITE_URL
4 * and API_URL, apps/api/src/addresses.rs), so the app keeps both.
5 *
6 * The site is where people sign in and approve the app, and where Chat's
7 * routes are; the API is the REST API and the OAuth issuer.
8 *
9 * Kept free of React Native imports so `npm test` runs it under Node.
10 */
11
12export type Server = {
13 /** An origin, no trailing slash: `https://g1t.sh`. */
14 site: string;
15 /** An origin, no trailing slash: `https://api.g1t.sh`. */
16 api: string;
The phone app with your own g1t, on g1t.sh/download until Google Play, and Node 24 (#5)17 /** False when this g1t sends phones no notifications (its PHONE_PUSH=off, lib/discover.ts). */
18 push?: boolean;
The phone app: Chat in full with phone notifications, plus Code and Customize (#4)19};
20
21export const HOSTED: Server = { site: "https://g1t.sh", api: "https://api.g1t.sh" };
22
23/**
24 * An origin as typed: http or https with a host, `https://` when no scheme
25 * is given; only the origin is kept. Null when it is not a web address.
26 * Plain http is accepted for a g1t on this network, as in development.
27 */
28export function parseOrigin(text: string): string | null {
29 const trimmed = text.trim();
30 if (!trimmed) return null;
31 const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
32 let url: URL;
33 try {
34 url = new URL(withScheme);
35 } catch {
36 return null;
37 }
38 if (url.protocol !== "https:" && url.protocol !== "http:") return null;
39 if (!url.hostname) return null;
40 return `${url.protocol}//${url.host}`;
41}
42
43/**
44 * The API's address for a site, when only the site was given: hosted
45 * g1t's own, or `api.` in front of a self-hosted site's host, which is
46 * how deploy/self-host names it.
47 */
48export function apiFor(site: string): string {
49 if (site === HOSTED.site) return HOSTED.api;
50 const url = new URL(site);
51 return `${url.protocol}//api.${url.host}`;
52}
53
54/** A server from what was typed: the site, and the API if it was given. */
55export function parseServer(siteText: string, apiText?: string): Server | null {
56 const site = parseOrigin(siteText);
57 if (!site) return null;
58 if (apiText && apiText.trim()) {
59 const api = parseOrigin(apiText);
60 if (!api) return null;
61 return { site, api };
62 }
63 return { site, api: apiFor(site) };
64}
65
66export function isHosted(server: Server): boolean {
67 return server.site === HOSTED.site && server.api === HOSTED.api;
68}
69
70/** The site's address for a path, such as a page to open in the browser. */
71export function siteUrl(server: Server, path: string): string {
72 return server.site + (path.startsWith("/") ? path : `/${path}`);
73}
74
75/** The socket address for one of the site's live paths. */
76export function socketUrl(server: Server, path: string): string {
77 return siteUrl(server, path).replace(/^http/, "ws");
78}

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