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
119 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 daily1/**
2 * Building search queries and addresses on the site: the filters on the
3 * search page change the query people can see and edit, never something
4 * hidden, so a search is always one shareable address.
5 */
6import type { SearchType } from "@g1t/contracts";
7
8/** The qualifiers a search understands, as the help lists them. */
9export const QUALIFIERS: { example: string; means: string }[] = [
10 { example: "repo:acme/web", means: "Only in one repository" },
11 { example: "org:acme", means: "Only in one workspace (also workspace:acme)" },
12 { example: "language:rust", means: "Code in a language, or repositories mostly in it" },
13 { example: "path:src/", means: "Code under a path; path:*.rs for a pattern" },
14 { example: "is:issue is:pr", means: "Only issues, or only pull requests" },
15 { example: "is:open is:closed is:merged", means: "Issues and pull requests by state" },
16 { example: "author:ana", means: "Opened by someone" },
17 { example: "label:bug", means: "Issues and pull requests with a label" },
18 { example: "\"exact phrase\"", means: "These words together, in this order" },
19 { example: "-word -label:wontfix", means: "Leave out what has it" },
20];
21
22/** Languages offered by the language filter; any other can be typed. */
23export const LANGUAGES = [
24 "rust",
25 "typescript",
26 "tsx",
27 "javascript",
28 "python",
29 "go",
30 "ruby",
31 "java",
32 "kotlin",
33 "swift",
34 "c",
35 "c++",
36 "c#",
37 "php",
38 "elixir",
39 "shell",
40 "sql",
41 "html",
42 "css",
43 "markdown",
44 "yaml",
45 "toml",
46 "json",
47];
48
49/** What each tab is called. */
50export const TYPE_LABELS: Record<SearchType, string> = {
51 repositories: "Repositories",
52 code: "Code",
53 issues: "Issues",
54 pulls: "Pull requests",
55 people: "People",
56};
57
58/** Splits a query into its tokens, keeping quoted runs whole. */
59export function tokens(query: string): string[] {
60 return query.match(/(?:[^\s"]+|"[^"]*"?)+/g) ?? [];
61}
62
63/** The value of a qualifier in a query, the first if it is given more than once. */
64export function qualifier(query: string, key: string): string | null {
65 for (const token of tokens(query)) {
66 const [name, ...rest] = token.split(":");
67 if (name?.toLowerCase() === key && rest.length) return rest.join(":").replace(/^"|"$/g, "") || null;
68 }
69 return null;
70}
71
72/**
73 * The query with `key:value` in place of any `key:` it had; with `value`
74 * null, without it. `aliases` are other names for the same qualifier
75 * (`org` and `workspace`), removed as well. Kept in the order written.
76 */
77export function withQualifier(query: string, key: string, value: string | null, aliases: string[] = []): string {
78 const names = new Set([key, ...aliases]);
79 const kept = tokens(query).filter((token) => {
80 const name = token.split(":")[0]?.toLowerCase() ?? "";
81 return !(token.includes(":") && names.has(name));
82 });
83 if (value) kept.push(`${key}:${/\s/.test(value) ? `"${value}"` : value}`);
84 return kept.join(" ");
85}
86
87/** `is:` values of one family set to `value`, the family's others removed. */
88export function withIs(query: string, family: string[], value: string | null): string {
89 const kept = tokens(query).filter((token) => {
90 const [name, rest] = token.split(":");
91 return !(name?.toLowerCase() === "is" && family.includes((rest ?? "").toLowerCase()));
92 });
93 if (value) kept.push(`is:${value}`);
94 return kept.join(" ");
95}
96
97/** The `is:` value of a family the query has, if any. */
98export function isValue(query: string, family: string[]): string | null {
99 for (const token of tokens(query)) {
100 const [name, rest] = token.split(":");
101 if (name?.toLowerCase() === "is" && family.includes((rest ?? "").toLowerCase())) return rest!.toLowerCase();
102 }
103 return null;
104}
105
106/** The address of a search. */
107export function searchHref(query: string, type?: SearchType | null, page?: number): string {
108 const params = new URLSearchParams();
109 params.set("q", query.trim());
110 if (type) params.set("type", type);
111 if (page && page > 1) params.set("page", String(page));
112 return `/search?${params.toString()}`;
113}
114
115/** A count as a tab shows it: up to the cap, then "1k+". */
116export function shortCount(count: number, cap = 1000): string {
117 if (count > cap - 1) return "1k+";
118 return String(count);
119}

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