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.

54 linesCodeBlameRaw
1/**
2 * Where `#N` in text leads on the site: the rules of
3 * `crates/contracts/src/work/refs.rs`, which every reader of references
4 * follows. A reference is `#N`, `owner/name#N` (read in that repository)
5 * or `workspace#N` (in that workspace). Read in a repository, `#N` is its
6 * pull request N, else the work item that was its issue N, else the
7 * workspace's work item N, else a pull request N of another of its
8 * repositories: only the work service knows which, so a link goes through
9 * `/<owner>/<repo>/ref/<n>`, which asks it (`resolveRef`) and sends the
10 * browser on. Read in a workspace alone, `#N` is its work item N, else its
11 * pull request N: the work item's page sends a pull request's number on.
12 * Pure, so it is tested under Node.
13 */
14
15/** Where text is read: in a repository (and its workspace), or in a workspace alone. */
16export type RefContext = { repo: { namespace: string; name: string } } | { workspace: string };
17
18/** A reference found in text. */
19export type Ref =
20 | { scope: "here"; number: number }
21 | { scope: "repo"; namespace: string; name: string; number: number }
22 | { scope: "workspace"; workspace: string; number: number };
23
24/** A work item's page. */
25export function itemPath(workspace: string, number: number): string {
26 return `/${workspace.toLowerCase()}/-/work/${number}`;
27}
28
29/** The page that reads `#number` in a repository and sends the browser to what it names. */
30export function repoRefPath(repo: { namespace: string; name: string }, number: number): string {
31 return `/${repo.namespace.toLowerCase()}/${repo.name}/ref/${number}`;
32}
33
34/** Where a reference leads, read in `context`; null for `#N` with no context to read it in. */
35export function refHref(ref: Ref, context: RefContext | null | undefined): string | null {
36 switch (ref.scope) {
37 case "repo":
38 return repoRefPath({ namespace: ref.namespace, name: ref.name }, ref.number);
39 case "workspace":
40 return itemPath(ref.workspace, ref.number);
41 case "here":
42 if (!context) return null;
43 return "repo" in context ? repoRefPath(context.repo, ref.number) : itemPath(context.workspace, ref.number);
44 }
45}
46
47/** A reference in its written form: `owner/name#12`, `workspace#12` or `#12`. */
48export function parseRef(owner: string | undefined, name: string | undefined, number: string): Ref | null {
49 const n = Number(number);
50 if (!Number.isInteger(n) || n < 1 || number.length > 9) return null;
51 if (owner && name) return { scope: "repo", namespace: owner.toLowerCase(), name, number: n };
52 if (owner) return { scope: "workspace", workspace: owner.toLowerCase(), number: n };
53 return { scope: "here", number: n };
54}