pr_01m47d15m3e54sn21z27rpy5n9/packages/contracts/src/repos.ts

194 lines6,282 bytesCodeBlame
1import type { User, Viewer } from "./identity";
2import type { Result } from "./result";
3
4export type Repo = {
5 id: string;
6 /** The slug of the workspace that owns it: the first URL segment. */
7 namespace: string;
8 name: string;
9 description: string | null;
10 isPrivate: boolean;
11 ownerId: string;
12 defaultBranch: string;
13 /** Set when this repo is a pull request's working copy of another repo. */
14 forkOf: string | null;
15 /**
16 * Whether the default branch is protected: it changes only by merging a
17 * pull request, and pushes to it are refused.
18 */
19 protected: boolean;
20 /** RFC 3339. */
21 createdAt: string;
22};
23
24export type RepoPath = { namespace: string; name: string };
25
26export type Commit = {
27 hash: string;
28 treeHash: string;
29 message: string;
30 author: { name: string; email: string };
31 parents: string[];
32 /** RFC 3339. */
33 authoredAt: string;
34};
35
36export type TreeEntry = {
37 name: string;
38 hash: string;
39 kind: "tree" | "blob" | "symlink" | "gitlink" | "exec";
40};
41
42export type TreeView = {
43 repo: Repo;
44 ref: string;
45 path: string;
46 /** Null when the repo has no commits yet. */
47 head: Commit | null;
48 entries: TreeEntry[];
49 readme: { name: string; text: string | null } | null;
50};
51
52export type BlobView = {
53 repo: Repo;
54 ref: string;
55 path: string;
56 size: number;
57 /** Null when the file is binary or too large to show. */
58 text: string | null;
59};
60
61/** A git remote and a short-lived credential for it. */
62export type GitAccess = { remote: string; token: string };
63
64export type GitService = "git-upload-pack" | "git-receive-pack";
65
66export type CreateRepoInput = {
67 /** The workspace to create it in; the creator must be a member. */
68 namespace: string;
69 name: string;
70 description?: string | null;
71 isPrivate?: boolean;
72 /**
73 * The https address of a public git repository to copy the default branch
74 * of, such as `https://github.com/owner/repo`.
75 */
76 importUrl?: string;
77};
78
79/** Repositories: metadata, contents and git access. */
80export interface ReposApi {
81 get(path: RepoPath, viewer: Viewer): Promise<Result<Repo>>;
82 getById(id: string, viewer: Viewer): Promise<Result<Repo>>;
83 /** Repos the viewer may see, newest first, optionally matching `query`. */
84 list(
85 viewer: Viewer,
86 options?: {
87 query?: string;
88 /** Only repos in this workspace. */
89 namespace?: string;
90 /** Only repos in workspaces the viewer belongs to. */
91 memberOnly?: boolean;
92 },
93 ): Promise<Repo[]>;
94 create(owner: User, input: CreateRepoInput): Promise<Result<Repo>>;
95 /**
96 * Changes whichever details are given. Members of the repository's
97 * workspace only. An empty description clears it.
98 */
99 update(
100 actor: User,
101 path: RepoPath,
102 changes: { description?: string; isPrivate?: boolean; protected?: boolean },
103 ): Promise<Result<Repo>>;
104
105 tree(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<TreeView>>;
106 blob(path: RepoPath, viewer: Viewer, ref: string, filePath: string): Promise<Result<BlobView>>;
107 log(path: RepoPath, viewer: Viewer, ref: string | null, limit: number): Promise<Result<Commit[]>>;
108 /**
109 * Who last changed each line of a file as of `ref` (the default branch if
110 * null). Not found when the file is missing or is not text.
111 */
112 blame(path: RepoPath, viewer: Viewer, ref: string | null, filePath: string): Promise<Result<Blame>>;
113
114 /**
115 * A copy-on-write copy of `source`, hidden from listings, for one pull request
116 * to work in.
117 */
118 forkForPull(sourceId: string, pullId: string, actor: User): Promise<Result<Repo>>;
119
120 /**
121 * Authorizes a git operation and returns where to send it. Pushing to a
122 * repo that does not exist creates it in the pusher's own namespace.
123 */
124 gitAccess(path: RepoPath, viewer: Viewer, service: GitService): Promise<Result<GitAccess>>;
125
126 /** The repository's branches, default branch first. */
127 branches(path: RepoPath, viewer: Viewer): Promise<Result<Branch[]>>;
128
129 /**
130 * Moves a repository's default branch to the head of a pull request's
131 * source: a fork (`sourceId` is the fork) or one of the repository's own
132 * branches (`sourceId` is the repository, and `branch` is required).
133 * Refused with "conflict" when the source is behind, since that would
134 * discard commits.
135 */
136 land(sourceId: string, actor: User, branch?: string | null): Promise<Result<{ commit: string; previous: string | null }>>;
137
138 /**
139 * What `head` changes relative to `base`. `head` is a branch or a commit
140 * and defaults to the default branch. With no `base`, a fork is compared
141 * with the last commit it shares with the repository it came from, and a
142 * branch with the point where it left the default branch.
143 */
144 compare(repoId: string, viewer: Viewer, base?: string | null, head?: string | null): Promise<Result<Comparison>>;
145}
146
147/** Lines `start` to `end` (inclusive, from 1) last changed by `commit`. */
148export type BlameRange = { start: number; end: number; commit: string };
149
150/** Who last changed each line of a file. */
151export type Blame = {
152 /** The commit the file was read at. */
153 head: string;
154 /** Every line, in order, in runs that share a commit. */
155 ranges: BlameRange[];
156 /** The commits the ranges name, each once. */
157 commits: Commit[];
158 /** True when the history was too long to read in full. */
159 partial: boolean;
160};
161
162/** A branch and the commit it points to. */
163export type Branch = { name: string; hash: string };
164
165export type DiffLine = {
166 kind: "context" | "add" | "delete";
167 /** Line number in the old file; null for added lines. */
168 old: number | null;
169 /** Line number in the new file; null for deleted lines. */
170 new: number | null;
171 text: string;
172};
173
174/** A run of changed lines with their surrounding context. */
175export type Hunk = { lines: DiffLine[] };
176
177export type FileDiff = {
178 path: string;
179 status: "added" | "modified" | "deleted";
180 additions: number;
181 deletions: number;
182 /** True when the file is binary or too large, so no lines are shown. */
183 binary: boolean;
184 hunks: Hunk[];
185};
186
187/** What changed between two commits. */
188export type Comparison = {
189 base: string | null;
190 head: string;
191 files: FileDiff[];
192 /** True when the change was too large to return in full. */
193 truncated: boolean;
194};