| 1 | import type { User, Viewer } from "./identity"; |
| 2 | import type { RepoPath } from "./repos"; |
| 3 | import type { Result } from "./result"; |
| 4 | |
| 5 | /** |
| 6 | * Projects: the thing a workspace builds and runs. A project has exactly |
| 7 | * one source, where its code lives; everything about running it |
| 8 | * (deployments, environments, domains, secrets and variables) belongs to |
| 9 | * the project, while branches, pull requests and review stay with the |
| 10 | * repository. One repository may carry several projects, each from its own |
| 11 | * root directory. Every repository on g1t gets a project of its own name. |
| 12 | */ |
| 13 | |
| 14 | /** Where a project's code lives. */ |
| 15 | export type ProjectSource = |
| 16 | /** A repository hosted on g1t. */ |
| 17 | | { kind: "hosted"; repoId: string; repo: RepoPath; rootDir: string; defaultBranch: string } |
| 18 | /** Mirrored from another host; coming next. */ |
| 19 | | { kind: "mirror"; provider: "github" | "gitlab" | "bitbucket"; url: string; rootDir: string }; |
| 20 | |
| 21 | export type Project = { |
| 22 | id: string; |
| 23 | /** The workspace's slug. */ |
| 24 | workspace: string; |
| 25 | /** Unique in its workspace; the project's address is `g1t.sh/<workspace>/<slug>`. */ |
| 26 | slug: string; |
| 27 | name: string; |
| 28 | /** The project's own description, or its repository's while it has none. */ |
| 29 | description: string | null; |
| 30 | /** Whether `description` is its repository's, following it as it changes. */ |
| 31 | descriptionInherited: boolean; |
| 32 | source: ProjectSource; |
| 33 | /** Whether its repository is private: only people with a role on it can see it. */ |
| 34 | private: boolean; |
| 35 | /** Whether its repository is archived: read-only, kept for reference. */ |
| 36 | archived: boolean; |
| 37 | /** Whether it is the project its repository's workflows read secrets from. */ |
| 38 | primary: boolean; |
| 39 | /** What a person set it to be; each part null while it is left to detection. */ |
| 40 | setting: ProjectSetting; |
| 41 | /** What it is: from `setting`, or for what is left out, from its deployments, packages and files. */ |
| 42 | kind: ProjectKind; |
| 43 | /** Why it is that kind. */ |
| 44 | kindReason: KindReason; |
| 45 | /** |
| 46 | * Where it runs: `g1t` when g1t deploys it (Deployments, on g1t.page), |
| 47 | * `elsewhere` when it is deployed by other means, at `productionUrl`. |
| 48 | * Null for what is not deployed (a library, a tool) and for an app nobody |
| 49 | * has said yet while Deployments are off. |
| 50 | */ |
| 51 | runs: ProjectRuns | null; |
| 52 | /** Where production is when it runs elsewhere; null when nobody has given one. */ |
| 53 | productionUrl: string | null; |
| 54 | /** What detection decides, whatever the setting is, to show beside it. */ |
| 55 | detected: { kind: ProjectKind; reason: KindReason }; |
| 56 | /** The ecosystem its files say it publishes to, for how to publish; null when none says. */ |
| 57 | ecosystem: ProjectEcosystem | null; |
| 58 | /** Its homepage, docs and other links, shown wherever the project is. */ |
| 59 | links: ProjectLinks; |
| 60 | createdBy: string; |
| 61 | /** RFC 3339. */ |
| 62 | createdAt: string; |
| 63 | updatedAt: string; |
| 64 | /** RFC 3339: when its repository was last pushed to; null until it is, after projects began keeping it. */ |
| 65 | pushedAt: string | null; |
| 66 | /** |
| 67 | * How active it is lately: each push, issue or pull request opened or |
| 68 | * closed, review, comment and deployment counts one, halving every week. |
| 69 | * 0 for none. |
| 70 | */ |
| 71 | activity: number; |
| 72 | }; |
| 73 | |
| 74 | /** |
| 75 | * What a project is. An app (or a site) runs somewhere and gets a |
| 76 | * production card; a library is published and installed; a tool, such as |
| 77 | * a CLI, ships as releases people install; docs are documentation or site |
| 78 | * content; other is anything else, such as configuration or research. |
| 79 | */ |
| 80 | export type ProjectKind = "app" | "library" | "tool" | "docs" | "other"; |
| 81 | |
| 82 | /** Every kind, in the order pages offer them. */ |
| 83 | export const PROJECT_KINDS: readonly ProjectKind[] = ["app", "library", "tool", "docs", "other"]; |
| 84 | |
| 85 | /** Where an app or a site runs: deployed by g1t on g1t.page, or deployed by other means. */ |
| 86 | export type ProjectRuns = "g1t" | "elsewhere"; |
| 87 | |
| 88 | /** What a person set; null for each part left to detection. */ |
| 89 | export type ProjectSetting = { kind: ProjectKind | null; runs: ProjectRuns | null }; |
| 90 | |
| 91 | /** One of a project's own links: a label and an http(s) address. */ |
| 92 | export type ProjectLink = { label: string; url: string }; |
| 93 | |
| 94 | export type ProjectLinks = { |
| 95 | /** Its homepage: its own, or its repository's website while it has none. */ |
| 96 | homepage: string | null; |
| 97 | /** Whether `homepage` is its repository's website, following it as it changes. */ |
| 98 | homepageInherited: boolean; |
| 99 | /** Where its documentation is read. */ |
| 100 | docs: string | null; |
| 101 | /** Any others, in the order given, at most `MAX_PROJECT_LINKS`. */ |
| 102 | custom: ProjectLink[]; |
| 103 | }; |
| 104 | |
| 105 | /** How many links of its own a project keeps besides its homepage and docs. */ |
| 106 | export const MAX_PROJECT_LINKS = 10; |
| 107 | /** The longest link label. */ |
| 108 | export const MAX_LINK_LABEL = 40; |
| 109 | /** The longest link address. */ |
| 110 | export const MAX_LINK_URL = 255; |
| 111 | |
| 112 | /** A change to a project; only what is given changes. */ |
| 113 | export type ProjectChanges = { |
| 114 | name?: string; |
| 115 | /** Null or blank goes back to the repository's. */ |
| 116 | description?: string | null; |
| 117 | rootDir?: string; |
| 118 | /** What it is; `auto` leaves it to detection. Anything but an app or docs stops it running anywhere. */ |
| 119 | kind?: ProjectKind | "auto"; |
| 120 | /** Where it runs; `auto` leaves it to Deployments. Setting it makes it an app unless it is docs. */ |
| 121 | runs?: ProjectRuns | "auto"; |
| 122 | /** Production's address when it runs elsewhere; null or blank clears it. */ |
| 123 | productionUrl?: string | null; |
| 124 | /** Null or blank goes back to the repository's website. */ |
| 125 | homepage?: string | null; |
| 126 | /** Null or blank clears it. */ |
| 127 | docsUrl?: string | null; |
| 128 | /** Replaces its other links. */ |
| 129 | links?: ProjectLink[]; |
| 130 | }; |
| 131 | |
| 132 | /** |
| 133 | * What decided the kind: the setting, Deployments being on, a package its |
| 134 | * repository publishes, its files, or nothing (an app, by default). |
| 135 | * `detail` says it in a sentence. |
| 136 | */ |
| 137 | export type KindReason = { by: "set" | "deployments" | "packages" | "files" | "default"; detail: string }; |
| 138 | |
| 139 | /** Where a library's files say it is published. */ |
| 140 | export type ProjectEcosystem = "composer" | "npm" | "cargo" | "go" | "python"; |
| 141 | |
| 142 | export type NewProject = { |
| 143 | name: string; |
| 144 | description?: string | null; |
| 145 | /** The hosted repository it builds from. */ |
| 146 | repo: RepoPath; |
| 147 | /** Where in the repository it lives; empty for the whole repository. */ |
| 148 | rootDir?: string; |
| 149 | }; |
| 150 | |
| 151 | /** One end of a dependency, as a page shows it. */ |
| 152 | export type DependencyLink = { |
| 153 | slug: string; |
| 154 | name: string; |
| 155 | /** The variable carrying the other project's address, such as `API_URL`. */ |
| 156 | as: string | null; |
| 157 | /** Declared on the site, or in the project's `.g1t/project.yml`. */ |
| 158 | source: "ui" | "file"; |
| 159 | }; |
| 160 | |
| 161 | /** What a project uses, and what uses it. */ |
| 162 | export type Dependencies = { dependsOn: DependencyLink[]; usedBy: DependencyLink[] }; |
| 163 | |
| 164 | /** A project's dependencies by id, for services. */ |
| 165 | export type ProjectGraph = { |
| 166 | dependsOn: { id: string; slug: string; workspace: string; as: string | null }[]; |
| 167 | usedBy: { id: string; slug: string; workspace: string; as: string | null }[]; |
| 168 | }; |
| 169 | |
| 170 | /** |
| 171 | * What a person keeps at hand in a workspace: the projects they pinned, in |
| 172 | * their order, then the ones they opened last that they have not pinned. |
| 173 | * Only projects they can still see. |
| 174 | */ |
| 175 | export type ProjectShortcuts = { pinned: Project[]; recent: Project[] }; |
| 176 | |
| 177 | export interface ProjectsApi { |
| 178 | /** A workspace's projects, by name. Members, or anyone for public repositories. */ |
| 179 | list(workspace: string, viewer: Viewer): Promise<Result<Project[]>>; |
| 180 | get(workspace: string, slug: string, viewer: Viewer): Promise<Result<Project>>; |
| 181 | /** The projects built from a repository, its primary one first. For services. */ |
| 182 | byRepo(repoId: string): Promise<Project[]>; |
| 183 | /** Members only. */ |
| 184 | create(actor: User, workspace: string, input: NewProject): Promise<Result<Project>>; |
| 185 | /** |
| 186 | * Members with a role that may change its settings. Making it something |
| 187 | * that does not run (a library, a tool, other) while Deployments are on |
| 188 | * is refused: they are turned off first. |
| 189 | */ |
| 190 | update(actor: User, workspace: string, slug: string, changes: ProjectChanges): Promise<Result<Project>>; |
| 191 | /** For deployments: Deployments were turned on or off for the project. */ |
| 192 | deploymentsChanged(projectId: string, enabled: boolean): Promise<void>; |
| 193 | /** What a project uses and what uses it. Whoever may see the project. */ |
| 194 | dependencies(workspace: string, slug: string, viewer: Viewer): Promise<Result<Dependencies>>; |
| 195 | /** |
| 196 | * `slug` uses `on`, with `as` the variable that carries `on`'s address. |
| 197 | * Members only; refused if it would make a cycle. |
| 198 | */ |
| 199 | addDependency(actor: User, workspace: string, slug: string, on: string, as: string | null): Promise<Result<Dependencies>>; |
| 200 | /** Members only. A dependency from `.g1t/project.yml` is changed there. */ |
| 201 | removeDependency(actor: User, workspace: string, slug: string, on: string): Promise<Result<Dependencies>>; |
| 202 | /** For services: a project's dependencies by id. */ |
| 203 | graph(projectId: string): Promise<ProjectGraph>; |
| 204 | /** A person's pinned and recent projects in a workspace. Empty for anyone else. */ |
| 205 | shortcuts(workspace: string, viewer: Viewer): Promise<ProjectShortcuts>; |
| 206 | /** |
| 207 | * Pins a project the person can see, at `position` (0 first) or at the |
| 208 | * end; pinning one already pinned moves it. A person's own, at most 8 a |
| 209 | * workspace. Returns their pins, in order. |
| 210 | */ |
| 211 | pin(actor: User, workspace: string, slug: string, position?: number | null): Promise<Result<Project[]>>; |
| 212 | /** Unpins it. Returns their pins, in order. */ |
| 213 | unpin(actor: User, workspace: string, slug: string): Promise<Result<Project[]>>; |
| 214 | /** Puts their pins in this order: every pinned project's slug, once. */ |
| 215 | reorderPins(actor: User, workspace: string, slugs: string[]): Promise<Result<Project[]>>; |
| 216 | /** The person opened the project: it leads their recent ones. */ |
| 217 | visited(actor: User, projectId: string): Promise<void>; |
| 218 | /** |
| 219 | * For lists of public repositories, such as Explore: each one's own |
| 220 | * project's address to show, by `namespace/name` in lower case: |
| 221 | * production when it is deployed elsewhere, else its homepage, else its |
| 222 | * docs. Repositories without one are left out. At most 100 asked at once. |
| 223 | */ |
| 224 | publicLinks(repos: RepoPath[]): Promise<Record<string, string>>; |
| 225 | } |