| 1 | import type { User, Viewer } from "./identity"; |
| 2 | import type { Result } from "./result"; |
| 3 | |
| 4 | export 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 an attempt's working copy of another repo. */ |
| 14 | forkOf: string | null; |
| 15 | /** RFC 3339. */ |
| 16 | createdAt: string; |
| 17 | }; |
| 18 | |
| 19 | export type RepoPath = { namespace: string; name: string }; |
| 20 | |
| 21 | export type Commit = { |
| 22 | hash: string; |
| 23 | treeHash: string; |
| 24 | message: string; |
| 25 | author: { name: string; email: string }; |
| 26 | parents: string[]; |
| 27 | /** RFC 3339. */ |
| 28 | authoredAt: string; |
| 29 | }; |
| 30 | |
| 31 | export type TreeEntry = { |
| 32 | name: string; |
| 33 | hash: string; |
| 34 | kind: "tree" | "blob" | "symlink" | "gitlink" | "exec"; |
| 35 | }; |
| 36 | |
| 37 | export type TreeView = { |
| 38 | repo: Repo; |
| 39 | ref: string; |
| 40 | path: string; |
| 41 | /** Null when the repo has no commits yet. */ |
| 42 | head: Commit | null; |
| 43 | entries: TreeEntry[]; |
| 44 | readme: { name: string; text: string | null } | null; |
| 45 | }; |
| 46 | |
| 47 | export type BlobView = { |
| 48 | repo: Repo; |
| 49 | ref: string; |
| 50 | path: string; |
| 51 | size: number; |
| 52 | /** Null when the file is binary or too large to show. */ |
| 53 | text: string | null; |
| 54 | }; |
| 55 | |
| 56 | /** A git remote and a short-lived credential for it. */ |
| 57 | export type GitAccess = { remote: string; token: string }; |
| 58 | |
| 59 | export type GitService = "git-upload-pack" | "git-receive-pack"; |
| 60 | |
| 61 | export type CreateRepoInput = { |
| 62 | /** The workspace to create it in; the creator must be a member. */ |
| 63 | namespace: string; |
| 64 | name: string; |
| 65 | description?: string | null; |
| 66 | isPrivate?: boolean; |
| 67 | }; |
| 68 | |
| 69 | /** Repositories: metadata, contents and git access. */ |
| 70 | export interface ReposApi { |
| 71 | get(path: RepoPath, viewer: Viewer): Promise<Result<Repo>>; |
| 72 | getById(id: string, viewer: Viewer): Promise<Result<Repo>>; |
| 73 | /** Repos the viewer may see, newest first, optionally matching `query`. */ |
| 74 | list( |
| 75 | viewer: Viewer, |
| 76 | options?: { |
| 77 | query?: string; |
| 78 | /** Only repos in this workspace. */ |
| 79 | namespace?: string; |
| 80 | /** Only repos in workspaces the viewer belongs to. */ |
| 81 | memberOnly?: boolean; |
| 82 | }, |
| 83 | ): Promise<Repo[]>; |
| 84 | create(owner: User, input: CreateRepoInput): Promise<Result<Repo>>; |
| 85 | |
| 86 | tree(path: RepoPath, viewer: Viewer, ref: string | null, treePath: string): Promise<Result<TreeView>>; |
| 87 | blob(path: RepoPath, viewer: Viewer, ref: string, filePath: string): Promise<Result<BlobView>>; |
| 88 | log(path: RepoPath, viewer: Viewer, ref: string | null, limit: number): Promise<Result<Commit[]>>; |
| 89 | |
| 90 | /** |
| 91 | * A copy-on-write copy of `source`, hidden from listings, for one attempt |
| 92 | * to work in. |
| 93 | */ |
| 94 | forkForAttempt(sourceId: string, attemptId: string, actor: User): Promise<Result<Repo>>; |
| 95 | |
| 96 | /** |
| 97 | * Authorizes a git operation and returns where to send it. Pushing to a |
| 98 | * repo that does not exist creates it in the pusher's own namespace. |
| 99 | */ |
| 100 | gitAccess(path: RepoPath, viewer: Viewer, service: GitService): Promise<Result<GitAccess>>; |
| 101 | |
| 102 | /** |
| 103 | * Moves the default branch of the repo a fork came from to the fork's |
| 104 | * head. Refused with "conflict" when the fork is behind, since that would |
| 105 | * discard commits. |
| 106 | */ |
| 107 | land(forkId: string, actor: User): Promise<Result<{ commit: string; previous: string | null }>>; |
| 108 | |
| 109 | /** |
| 110 | * What a repository's head changes. An attempt's fork is compared with the |
| 111 | * last commit it shares with the repository it came from, unless `base` |
| 112 | * says otherwise. |
| 113 | */ |
| 114 | compare(repoId: string, viewer: Viewer, base?: string | null): Promise<Result<Comparison>>; |
| 115 | } |
| 116 | |
| 117 | export type DiffLine = { |
| 118 | kind: "context" | "add" | "delete"; |
| 119 | /** Line number in the old file; null for added lines. */ |
| 120 | old: number | null; |
| 121 | /** Line number in the new file; null for deleted lines. */ |
| 122 | new: number | null; |
| 123 | text: string; |
| 124 | }; |
| 125 | |
| 126 | /** A run of changed lines with their surrounding context. */ |
| 127 | export type Hunk = { lines: DiffLine[] }; |
| 128 | |
| 129 | export type FileDiff = { |
| 130 | path: string; |
| 131 | status: "added" | "modified" | "deleted"; |
| 132 | additions: number; |
| 133 | deletions: number; |
| 134 | /** True when the file is binary or too large, so no lines are shown. */ |
| 135 | binary: boolean; |
| 136 | hunks: Hunk[]; |
| 137 | }; |
| 138 | |
| 139 | /** What changed between two commits. */ |
| 140 | export type Comparison = { |
| 141 | base: string | null; |
| 142 | head: string; |
| 143 | files: FileDiff[]; |
| 144 | /** True when the change was too large to return in full. */ |
| 145 | truncated: boolean; |
| 146 | }; |