| 1 | import type { Contributors, Languages, License, NewRelease, Release, ReleaseChange, RepoAbout, Stargazer, StarredRepo, Stars } from "./about"; |
| 2 | import type { User, Viewer } from "./identity"; |
| 3 | import type { RepoMirror } from "./mirrors"; |
| 4 | import type { Result } from "./result"; |
| 5 | |
| 6 | export type Repo = { |
| 7 | id: string; |
| 8 | /** The slug of the workspace that owns it: the first URL segment. */ |
| 9 | namespace: string; |
| 10 | name: string; |
| 11 | description: string | null; |
| 12 | isPrivate: boolean; |
| 13 | ownerId: string; |
| 14 | defaultBranch: string; |
| 15 | /** Set when this repo is a pull request's working copy of another repo. */ |
| 16 | forkOf: string | null; |
| 17 | /** |
| 18 | * Whether the default branch is protected: it changes only by merging a |
| 19 | * pull request, and pushes to it are refused. |
| 20 | */ |
| 21 | protected: boolean; |
| 22 | /** RFC 3339. */ |
| 23 | createdAt: string; |
| 24 | /** |
| 25 | * Words that say what it is about, for search and Explore: lowercase |
| 26 | * letters, digits and hyphens, at most 20. |
| 27 | */ |
| 28 | topics: string[]; |
| 29 | /** Its home page, an http(s) address. */ |
| 30 | website?: string | null; |
| 31 | /** |
| 32 | * RFC 3339: when it was archived. While archived it is read-only: |
| 33 | * pushes, merges, agents and workflows are refused, and issues and pull |
| 34 | * requests are locked. Null when it is not archived. |
| 35 | */ |
| 36 | archivedAt?: string | null; |
| 37 | /** |
| 38 | * Set when it mirrors a remote that leads. Unless g1t has taken over, |
| 39 | * it is read-only: pushes, merges, issues, pull requests and agents are |
| 40 | * refused, and nothing runs. See `./mirrors`. |
| 41 | */ |
| 42 | mirror?: RepoMirror | null; |
| 43 | }; |
| 44 | |
| 45 | /** How long a deleted repository can be restored before it is purged. */ |
| 46 | export const RESTORE_DAYS = 30; |
| 47 | |
| 48 | /** A deleted repository, as its workspace's Recently deleted list shows it. */ |
| 49 | export type DeletedRepo = { |
| 50 | id: string; |
| 51 | namespace: string; |
| 52 | name: string; |
| 53 | description: string | null; |
| 54 | isPrivate: boolean; |
| 55 | /** RFC 3339. */ |
| 56 | deletedAt: string; |
| 57 | /** The username of who deleted it. */ |
| 58 | deletedBy: string; |
| 59 | /** RFC 3339: when it is purged, unless restored first. */ |
| 60 | purgeAfter: string; |
| 61 | }; |
| 62 | |
| 63 | /** Whether a repository is archived or deleted, for services deciding whether to act on it. */ |
| 64 | export type RepoStatus = { archived: boolean; deleted: boolean }; |
| 65 | |
| 66 | /** The most topics a repository has, and the longest topic. */ |
| 67 | export const MAX_TOPICS = 20; |
| 68 | export const MAX_TOPIC_CHARS = 35; |
| 69 | |
| 70 | export type RepoPath = { namespace: string; name: string }; |
| 71 | |
| 72 | export type Commit = { |
| 73 | hash: string; |
| 74 | treeHash: string; |
| 75 | message: string; |
| 76 | author: { name: string; email: string }; |
| 77 | parents: string[]; |
| 78 | /** RFC 3339. */ |
| 79 | authoredAt: string; |
| 80 | }; |
| 81 | |
| 82 | export type TreeEntry = { |
| 83 | name: string; |
| 84 | hash: string; |
| 85 | kind: "tree" | "blob" | "symlink" | "gitlink" | "exec"; |
| 86 | }; |
| 87 | |
| 88 | export type TreeView = { |
| 89 | repo: Repo; |
| 90 | ref: string; |
| 91 | path: string; |
| 92 | /** Null when the repo has no commits yet. */ |
| 93 | head: Commit | null; |
| 94 | entries: TreeEntry[]; |
| 95 | readme: { name: string; text: string | null } | null; |
| 96 | }; |
| 97 | |
| 98 | export type BlobView = { |
| 99 | repo: Repo; |
| 100 | ref: string; |
| 101 | path: string; |
| 102 | size: number; |
| 103 | /** Null when the file is binary or too large to show. */ |
| 104 | text: string | null; |
| 105 | }; |
| 106 | |
| 107 | /** A git remote and a short-lived credential for it. */ |
| 108 | export type GitAccess = { remote: string; token: string }; |
| 109 | |
| 110 | export type GitService = "git-upload-pack" | "git-receive-pack"; |
| 111 | |
| 112 | export type CreateRepoInput = { |
| 113 | /** The workspace to create it in; the creator must be a member. */ |
| 114 | namespace: string; |
| 115 | name: string; |
| 116 | description?: string | null; |
| 117 | isPrivate?: boolean; |
| 118 | /** |
| 119 | * The https address of a public git repository to copy the default branch |
| 120 | * of, such as `https://github.com/owner/repo`. |
| 121 | */ |
| 122 | importUrl?: string; |
| 123 | }; |
| 124 | |
| 125 | /** |
| 126 | * Where repositories may be kept. `euAvailable`: an EU namespace takes new |
| 127 | * repositories, so a workspace may keep its data in the EU. |
| 128 | */ |
| 129 | export type StorageOptions = { euAvailable: boolean }; |
| 130 | |
| 131 | /** Repositories: metadata, contents and git access. */ |
| 132 | export interface ReposApi { |
| 133 | get(path: RepoPath, viewer: Viewer): Promise<Result<Repo>>; |
| 134 | getById(id: string, viewer: Viewer): Promise<Result<Repo>>; |
| 135 | /** |
| 136 | * Of these ids (at most 500), the repositories the viewer can read, in |
| 137 | * one call. Forks and deleted repositories are left out. |
| 138 | */ |
| 139 | readable(ids: string[], viewer: Viewer): Promise<Repo[]>; |
| 140 | /** |
| 141 | * The workspaces in which this account made a public repository, and so |
| 142 | * a public project anyone can see. By account id. |
| 143 | */ |
| 144 | publicNamespaces(ownerId: string): Promise<string[]>; |
| 145 | /** Repos the viewer may see, newest first, optionally matching `query`. */ |
| 146 | list( |
| 147 | viewer: Viewer, |
| 148 | options?: { |
| 149 | query?: string; |
| 150 | /** Only repos in this workspace. */ |
| 151 | namespace?: string; |
| 152 | /** Only repos in workspaces the viewer belongs to. */ |
| 153 | memberOnly?: boolean; |
| 154 | }, |
| 155 | ): Promise<Repo[]>; |
| 156 | create(owner: User, input: CreateRepoInput): Promise<Result<Repo>>; |
| 157 | /** |
| 158 | * Changes whichever details are given. Members of the repository's |
| 159 | * workspace only. An empty description clears it. |
| 160 | */ |
| 161 | update( |
| 162 | actor: User, |
| 163 | path: RepoPath, |
| 164 | changes: { |
| 165 | description?: string; |
| 166 | isPrivate?: boolean; |
| 167 | protected?: boolean; |
| 168 | topics?: string[]; |
| 169 | /** An empty string clears it. */ |
| 170 | website?: string; |
| 171 | }, |
| 172 | ): Promise<Result<Repo>>; |
| 173 | /** |
| 174 | * Deletes the repository. Owners only, who type its full name as |
| 175 | * `confirm`. It is hidden at once and can be restored for |
| 176 | * `RESTORE_DAYS` days, then purged. Publishes `repo.deleted`. |
| 177 | */ |
| 178 | delete(actor: User, path: RepoPath, confirm: string): Promise<Result<DeletedRepo>>; |
| 179 | /** A workspace's recently deleted repositories, newest first. Owners only; empty otherwise. */ |
| 180 | deleted(viewer: Viewer, namespace: string): Promise<DeletedRepo[]>; |
| 181 | /** Brings a deleted repository back as it was. Owners only. Publishes `repo.restored`. */ |
| 182 | restore(actor: User, path: RepoPath): Promise<Result<Repo>>; |
| 183 | /** |
| 184 | * Removes a deleted repository for good now, its git data with it, and |
| 185 | * frees its name. Owners only, who type its full name. Publishes `repo.purged`. |
| 186 | */ |
| 187 | purge(actor: User, path: RepoPath, confirm: string): Promise<Result<boolean>>; |
| 188 | /** |
| 189 | * Renames the repository in its workspace. Owners only. The old path |
| 190 | * redirects until a repository is made there. Publishes `repo.renamed`. |
| 191 | */ |
| 192 | rename(actor: User, path: RepoPath, name: string): Promise<Result<Repo>>; |
| 193 | /** |
| 194 | * Archives the repository (read-only) or unarchives it. Owners only. |
| 195 | * Publishes `repo.archived` or `repo.unarchived`. |
| 196 | */ |
| 197 | archive(actor: User, path: RepoPath, archived: boolean): Promise<Result<Repo>>; |
| 198 | /** |
| 199 | * Makes the repository public or private. Owners only, who type its full |
| 200 | * name as `confirm`. Publishes `repo.updated` and `repo.visibility_changed`. |
| 201 | */ |
| 202 | setVisibility(actor: User, path: RepoPath, isPrivate: boolean, confirm: string): Promise<Result<Repo>>; |
| 203 | /** Makes another existing branch the default. Members. */ |
| 204 | setDefaultBranch(actor: User, path: RepoPath, branch: string): Promise<Result<Repo>>; |
| 205 | /** |
| 206 | * Renames a branch; pull requests from it follow, and addresses naming the |
| 207 | * old one redirect. Members; only owners rename the default branch. |
| 208 | */ |
| 209 | renameBranch(actor: User, path: RepoPath, from: string, to: string): Promise<Result<Repo>>; |
| 210 | /** What a branch renamed away from `branch` is called now, or null. */ |
| 211 | resolveBranch(repoId: string, branch: string): Promise<string | null>; |
| 212 | /** Whether a repository is archived or deleted; an unknown id answers as deleted. */ |
| 213 | statusById(id: string): Promise<RepoStatus>; |
| 214 | /** What a workspace may choose about where its repositories are kept. */ |
| 215 | storageOptions(): Promise<StorageOptions>; |
| 216 | /** |
| 217 | * Moves the repository to the workspace `to`, keeping its name, id and |
| 218 | * everything under it. The actor must own both workspaces. The old path |
| 219 | * redirects until a repository is made there. Publishes `repo.transferred`. |
| 220 | */ |
| 221 | transfer(actor: User, path: RepoPath, to: string): Promise<Result<Repo>>; |
| 222 | /** |
| 223 | * Where a repository transferred away from `path` is now; null when |
| 224 | * `path` is a repository or never was one that moved. Callers check the |
| 225 | * viewer may see it there. |
| 226 | */ |
| 227 | resolvePath(path: RepoPath): Promise<RepoPath | null>; |
| 228 | |
| 229 | tree(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<TreeView>>; |
| 230 | blob(path: RepoPath, viewer: Viewer, ref: string, filePath: string): Promise<Result<BlobView>>; |
| 231 | log(path: RepoPath, viewer: Viewer, ref: string | null, limit: number): Promise<Result<Commit[]>>; |
| 232 | /** |
| 233 | * Who last changed each line of a file as of `ref` (the default branch if |
| 234 | * null). Not found when the file is missing or is not text. |
| 235 | */ |
| 236 | blame(path: RepoPath, viewer: Viewer, ref: string | null, filePath: string): Promise<Result<Blame>>; |
| 237 | |
| 238 | /** |
| 239 | * A copy-on-write copy of `source`, hidden from listings, for one pull request |
| 240 | * to work in. |
| 241 | */ |
| 242 | forkForPull(sourceId: string, pullId: string, actor: User): Promise<Result<Repo>>; |
| 243 | |
| 244 | /** |
| 245 | * Authorizes a git operation and returns where to send it. Pushing to a |
| 246 | * repo that does not exist creates it in the pusher's own namespace. |
| 247 | */ |
| 248 | gitAccess(path: RepoPath, viewer: Viewer, service: GitService): Promise<Result<GitAccess>>; |
| 249 | |
| 250 | /** The repository's branches, default branch first. */ |
| 251 | branches(path: RepoPath, viewer: Viewer): Promise<Result<Branch[]>>; |
| 252 | |
| 253 | /** Which commit last changed each entry of a directory at `ref` (the default branch when null). */ |
| 254 | lastCommits(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<LastCommits>>; |
| 255 | |
| 256 | /** |
| 257 | * How far each branch head has moved from `base` (the default branch's |
| 258 | * head commit), with each head's commit and `base`'s own, in one call. |
| 259 | * Kept by the pair of hashes in the repos service. |
| 260 | */ |
| 261 | branchDrift(path: RepoPath, viewer: Viewer, base: string, heads: string[]): Promise<Result<BranchDrifts>>; |
| 262 | |
| 263 | /** The repository's tags, newest commit first, at most 100. */ |
| 264 | tags(path: RepoPath, viewer: Viewer): Promise<Result<Tag[]>>; |
| 265 | |
| 266 | /** |
| 267 | * The Files page's About in one answer: license, security policy, |
| 268 | * languages, contributors, stars and releases. What comes from files and |
| 269 | * history is kept by commit and worked out in the background when it is |
| 270 | * behind the head (see `about.ts`). |
| 271 | */ |
| 272 | about(path: RepoPath, viewer: Viewer): Promise<Result<RepoAbout>>; |
| 273 | languages(path: RepoPath, viewer: Viewer): Promise<Result<Languages>>; |
| 274 | /** Everyone whose commits are on the default branch, with their weeks. */ |
| 275 | contributors(path: RepoPath, viewer: Viewer): Promise<Result<Contributors>>; |
| 276 | license(path: RepoPath, viewer: Viewer): Promise<Result<License | null>>; |
| 277 | /** How many starred it, and whether the viewer did. */ |
| 278 | stars(path: RepoPath, viewer: Viewer): Promise<Result<Stars>>; |
| 279 | /** Stars it for the actor, or takes the star back. */ |
| 280 | star(actor: User, path: RepoPath, starred: boolean): Promise<Result<Stars>>; |
| 281 | /** Who starred it, newest first, 100 a page from 1. */ |
| 282 | stargazers(path: RepoPath, viewer: Viewer, page?: number): Promise<Result<Stargazer[]>>; |
| 283 | /** What a person starred that the viewer can see, newest first, at most 100. */ |
| 284 | starred(username: string, viewer: Viewer): Promise<StarredRepo[]>; |
| 285 | /** Releases, newest first, at most 100: drafts only for those who can push. */ |
| 286 | releases(path: RepoPath, viewer: Viewer): Promise<Result<Release[]>>; |
| 287 | /** One release by id or tag, or the latest. */ |
| 288 | release(path: RepoPath, viewer: Viewer, which: { id?: string; tag?: string; latest?: boolean }): Promise<Result<Release>>; |
| 289 | /** Needs Write. A tag that does not exist yet is made at `target`. */ |
| 290 | createRelease(actor: User, path: RepoPath, release: NewRelease): Promise<Result<Release>>; |
| 291 | updateRelease(actor: User, path: RepoPath, id: string, change: ReleaseChange): Promise<Result<Release>>; |
| 292 | /** The tag stays. */ |
| 293 | deleteRelease(actor: User, path: RepoPath, id: string): Promise<Result<boolean>>; |
| 294 | |
| 295 | /** |
| 296 | * Every file at `ref` (the default branch when null), at most `limit` |
| 297 | * (10,000 at most). No viewer: check access first. |
| 298 | */ |
| 299 | listFiles(repoId: string, ref: string | null, limit: number): Promise<FileList>; |
| 300 | |
| 301 | /** Blobs' bytes as standard base64, at most 100; `data` is null for one missing or over `maxBytes`. No viewer. */ |
| 302 | rawBlobs(repoId: string, hashes: string[], maxBytes: number): Promise<RawBlob[]>; |
| 303 | |
| 304 | /** One file's bytes at a branch, tag or commit; null when missing or over `maxBytes`. No viewer: check access first. */ |
| 305 | rawFile(repoId: string, ref: string, path: string, maxBytes: number): Promise<RawFile | null>; |
| 306 | |
| 307 | /** |
| 308 | * Writes one file on a new branch made from the default branch's head, as |
| 309 | * one commit by `actor`, for a change g1t proposes on their behalf (a |
| 310 | * starter workflow). Refused unless they may push, when the branch exists, |
| 311 | * or when the file is already there. |
| 312 | */ |
| 313 | commitFile( |
| 314 | repo: RepoPath, |
| 315 | actor: User, |
| 316 | file: { branch: string; path: string; content: string; message: string }, |
| 317 | ): Promise<Result<{ branch: string; commit: string }>>; |
| 318 | |
| 319 | /** |
| 320 | * Moves a repository's default branch to the head of a pull request's |
| 321 | * source: a fork (`sourceId` is the fork) or one of the repository's own |
| 322 | * branches (`sourceId` is the repository, and `branch` is required). |
| 323 | * Refused with "conflict" when the source is behind, since that would |
| 324 | * discard commits. |
| 325 | */ |
| 326 | land(sourceId: string, actor: User, branch?: string | null): Promise<Result<{ commit: string; previous: string | null }>>; |
| 327 | |
| 328 | /** |
| 329 | * What `head` changes relative to `base`. `head` is a branch or a commit |
| 330 | * and defaults to the default branch. With no `base`, a fork is compared |
| 331 | * with the last commit it shares with the repository it came from, and a |
| 332 | * branch with the point where it left the default branch. |
| 333 | */ |
| 334 | /** |
| 335 | * With no `base`, a branch is compared from where it left `baseBranch` |
| 336 | * (the default branch when absent). |
| 337 | */ |
| 338 | compare( |
| 339 | repoId: string, |
| 340 | viewer: Viewer, |
| 341 | base?: string | null, |
| 342 | head?: string | null, |
| 343 | baseBranch?: string | null, |
| 344 | ): Promise<Result<Comparison>>; |
| 345 | |
| 346 | /** |
| 347 | * Services only, for the runner's sweep: up to `limit` queued nightly |
| 348 | * backups, each now running with a token of its own, so long as no more |
| 349 | * than `maxRunning` are then running. Empty when backups are off. |
| 350 | */ |
| 351 | claimBackups(limit: number, maxRunning: number): Promise<BackupClaim[]>; |
| 352 | |
| 353 | /** |
| 354 | * Services only: a backup's sandbox stopped before it reported, so the |
| 355 | * job is tried again later. Refused harmlessly once it has reported. |
| 356 | */ |
| 357 | failBackup(jobId: string, token: string, error: string): Promise<Result<boolean>>; |
| 358 | } |
| 359 | |
| 360 | /** |
| 361 | * A nightly backup to start (`g1t_contracts::backups`): the sandbox is |
| 362 | * given the job's id and token, and nothing else. |
| 363 | */ |
| 364 | export type BackupClaim = { |
| 365 | jobId: string; |
| 366 | token: string; |
| 367 | repoId: string; |
| 368 | path: RepoPath; |
| 369 | }; |
| 370 | |
| 371 | /** Lines `start` to `end` (inclusive, from 1) last changed by `commit`. */ |
| 372 | export type BlameRange = { start: number; end: number; commit: string }; |
| 373 | |
| 374 | /** Who last changed each line of a file. */ |
| 375 | export type Blame = { |
| 376 | /** The commit the file was read at. */ |
| 377 | head: string; |
| 378 | /** Every line, in order, in runs that share a commit. */ |
| 379 | ranges: BlameRange[]; |
| 380 | /** The commits the ranges name, each once. */ |
| 381 | commits: Commit[]; |
| 382 | /** True when the history was too long to read in full. */ |
| 383 | partial: boolean; |
| 384 | }; |
| 385 | |
| 386 | /** A branch and the commit it points to. */ |
| 387 | export type Branch = { name: string; hash: string }; |
| 388 | |
| 389 | /** Each entry's last commit; `complete` is false when some were not reached. */ |
| 390 | export type LastCommits = { entries: { name: string; commit: Commit }[]; complete: boolean }; |
| 391 | |
| 392 | /** Commits a branch has that the default branch does not, and the other way round. */ |
| 393 | export type BranchDriftCount = { ahead: number; behind: number }; |
| 394 | |
| 395 | /** |
| 396 | * `base`'s commit, and each head's commit and drift in the order asked; |
| 397 | * `drift` is null when the two histories do not meet within what is read. |
| 398 | */ |
| 399 | export type BranchDrifts = { |
| 400 | base: Commit | null; |
| 401 | branches: { head: string; commit: Commit | null; drift: BranchDriftCount | null }[]; |
| 402 | }; |
| 403 | |
| 404 | /** A tag, and its commit when it could be read. */ |
| 405 | export type Tag = { name: string; commit: Commit | null }; |
| 406 | |
| 407 | /** Files at a commit, and whether there were more than were listed. */ |
| 408 | export type FileList = { commit: string | null; files: { path: string; hash: string | null }[]; truncated: boolean }; |
| 409 | |
| 410 | /** One blob's bytes, standard base64; null when missing or too large. */ |
| 411 | export type RawBlob = { hash: string; size: number; data: string | null }; |
| 412 | |
| 413 | /** One file's bytes, standard base64. */ |
| 414 | export type RawFile = { size: number; data: string }; |
| 415 | |
| 416 | /** |
| 417 | * What came of bringing a pull request up to date with the default branch |
| 418 | * without a sandbox. `needs_agent` pushed nothing: the runner's `update` |
| 419 | * merges it in a sandbox, with an agent if it conflicts. |
| 420 | */ |
| 421 | export type PullBranchUpdate = |
| 422 | | { outcome: "updated"; commit: string; previous: string } |
| 423 | | { outcome: "up_to_date"; commit: string } |
| 424 | | { |
| 425 | outcome: "needs_agent"; |
| 426 | /** |
| 427 | * `overlap`: both sides changed some of the same files. `conflicting`: |
| 428 | * merging is known to conflict. `unsupported`: it could not be worked |
| 429 | * out without git, such as for a very large change. |
| 430 | */ |
| 431 | reason: "overlap" | "conflicting" | "unsupported"; |
| 432 | detail: string; |
| 433 | /** The files both changed, or that conflict, when known. */ |
| 434 | paths: string[]; |
| 435 | }; |
| 436 | |
| 437 | export type DiffLine = { |
| 438 | kind: "context" | "add" | "delete"; |
| 439 | /** Line number in the old file; null for added lines. */ |
| 440 | old: number | null; |
| 441 | /** Line number in the new file; null for deleted lines. */ |
| 442 | new: number | null; |
| 443 | text: string; |
| 444 | }; |
| 445 | |
| 446 | /** A run of changed lines with their surrounding context. */ |
| 447 | export type Hunk = { lines: DiffLine[] }; |
| 448 | |
| 449 | export type FileDiff = { |
| 450 | path: string; |
| 451 | status: "added" | "modified" | "deleted"; |
| 452 | additions: number; |
| 453 | deletions: number; |
| 454 | /** True when the file is binary or too large, so no lines are shown. */ |
| 455 | binary: boolean; |
| 456 | hunks: Hunk[]; |
| 457 | }; |
| 458 | |
| 459 | /** What changed between two commits. */ |
| 460 | export type Comparison = { |
| 461 | base: string | null; |
| 462 | head: string; |
| 463 | files: FileDiff[]; |
| 464 | /** True when the change was too large to return in full. */ |
| 465 | truncated: boolean; |
| 466 | }; |