Skip to content
213 linesCodeBlameRaw
1import type { AuditSurface } from "./audit";
2import type { User, Viewer } from "./identity";
3import type { Result } from "./result";
4
5/**
6 * The packages service: the registries a workspace publishes to and
7 * installs from, beside its code (docs/PACKAGES.md). Container images
8 * first, on `g1t.sh/v2/`. Mirrors `crates/contracts/src/packages.rs`.
9 *
10 * A package linked to a repository has its visibility and, unless its
11 * admins turned inheriting off, its roles (Read pulls, Write publishes,
12 * Admin deletes and changes settings); an unlinked one is its workspace's:
13 * members by the base permission, owners administer. Roles given on the
14 * package itself, to people and teams, add to those. A workflow job's
15 * token reaches it from its linked repository, or one listed under Manage
16 * Actions access. Deleted packages and versions can be restored for
17 * `PACKAGE_RESTORE_DAYS`.
18 */
19
20/** How long a deleted package or version can be restored, in days. */
21export const PACKAGE_RESTORE_DAYS = 30;
22
23/** Which registry a package is in. */
24export const ECOSYSTEMS = ["container", "npm", "composer", "cargo", "go", "maven", "nuget", "rubygems"] as const;
25export type Ecosystem = (typeof ECOSYSTEMS)[number];
26
27export type PackageVisibility = "public" | "private";
28
29export type LinkedRepo = { id: string; namespace: string; name: string };
30
31/** A package as listings show it. */
32export type PackageSummary = {
33 id: string;
34 workspace: string;
35 ecosystem: Ecosystem;
36 /** Without the workspace: `web` for `g1t.sh/acme/web`. */
37 name: string;
38 /** What a client is given: `g1t.sh/acme/web` for a container image. */
39 address: string;
40 /** Linked packages follow their repository's visibility. */
41 visibility: PackageVisibility;
42 repo: LinkedRepo | null;
43 description: string | null;
44 versions: number;
45 /** The newest version's tag (`latest` when it has one) or version. */
46 latest: string | null;
47 /** Bytes its versions hold, each file counted once. */
48 size: number;
49 /** Pulls and installs, counted approximately. */
50 downloads: number;
51 created_at: string;
52 updated_at: string;
53 /** For a linked package: whether it takes its repository's roles. */
54 inherit_access?: boolean;
55 /** Set on a deleted package: when, by whom, and when it is purged. */
56 deleted_at?: string | null;
57 deleted_by?: string | null;
58 purge_at?: string | null;
59};
60
61/** One version: for a container image, one manifest, by digest. */
62export type PackageVersion = {
63 id: string;
64 version: string;
65 digest: string;
66 size: number;
67 media_type: string | null;
68 /** For an OCI artifact: what it is, such as a signature or an SBOM. */
69 artifact_type: string | null;
70 /** For an artifact attached to another version: that version's digest. */
71 subject: string | null;
72 /** For an image index: the platforms it holds, such as `linux/amd64`. */
73 platforms: string[];
74 tags: string[];
75 published_by: string | null;
76 published_at: string;
77 /** npm: why the version should no longer be used, when it is deprecated. */
78 deprecated?: string | null;
79 /** NuGet: whether a symbol package (`.snupkg`) was pushed for it. */
80 symbols?: boolean;
81 /** Its own pulls or downloads, counted approximately. */
82 downloads?: number | null;
83 /** Set on a deleted version: when, by whom, and when it is purged. */
84 deleted_at?: string | null;
85 deleted_by?: string | null;
86 purge_at?: string | null;
87};
88
89export type PackageTag = { tag: string; digest: string; updated_at: string };
90
91/**
92 * What the viewer may do with a package. `delete`: delete and restore it
93 * and its versions; `admin`: change its settings (access, Actions access,
94 * visibility, link).
95 */
96export type PackagePermissions = { pull: boolean; push: boolean; delete: boolean; admin: boolean };
97
98/** A role on a package: read pulls, write publishes, admin deletes and changes settings. */
99export const PACKAGE_ROLES = ["read", "write", "admin"] as const;
100export type PackageRole = (typeof PACKAGE_ROLES)[number];
101
102/** A person or a team with a role on a package itself. */
103export type PackageAccess = {
104 kind: "user" | "team";
105 id: string;
106 /** A username, or a team as `workspace/slug`. */
107 name: string;
108 role: PackageRole;
109 created_at: string;
110};
111
112/** A repository whose workflows may use a package; `linked` is its own repository, always write. */
113export type ActionsAccess = {
114 repo_id: string;
115 /** `owner/name`. */
116 repo: string;
117 role: "read" | "write";
118 linked: boolean;
119 created_at: string | null;
120};
121
122/** `package_settings`: what a package's admins see on its Settings tab. */
123export type PackageSettings = {
124 package: PackageSummary;
125 access: PackageAccess[];
126 actions_access: ActionsAccess[];
127 /** Deleted versions that can still be restored, newest first. */
128 deleted_versions: PackageVersion[];
129 permissions: PackagePermissions;
130};
131
132export type PackageDetail = {
133 package: PackageSummary;
134 /** Newest first. */
135 versions: PackageVersion[];
136 tags: PackageTag[];
137 permissions: PackagePermissions;
138 /** The package's README, as markdown: npm's, from its latest version. */
139 readme?: string | null;
140};
141
142/** What a workspace's packages hold, for billing: each file once, public when any public package uses it. */
143export type PackageStorage = { public_bytes: number; private_bytes: number };
144
145/** `storage_all`: every workspace with packages, by workspace, for billing's daily measure. */
146export type WorkspacePackageStorage = { workspace: string; public_bytes: number; private_bytes: number };
147
148export type PackageFilter = { ecosystem?: Ecosystem | null; repoId?: string | null; query?: string | null };
149
150export type PackageChange = {
151 visibility?: PackageVisibility;
152 /** A repository of the package's workspace, by name. */
153 link?: string;
154 /** Take the link away. */
155 unlink?: boolean;
156 /** For a linked package: whether it takes its repository's roles. */
157 inheritAccess?: boolean;
158};
159
160/** Who a change of access is for: a person by username, or a team by slug. */
161export type PackageGrantee = { user: string } | { team: string };
162
163export type PackagesApi = {
164 /** The workspace's packages the viewer may pull, newest first. */
165 list(workspace: string, viewer: Viewer, filter?: PackageFilter): Promise<Result<PackageSummary[]>>;
166 /** Not found when the viewer may not pull it. */
167 get(workspace: string, ecosystem: Ecosystem, name: string, viewer: Viewer): Promise<Result<PackageDetail>>;
168 /** A version by version, digest or tag; its tags go with it. */
169 deleteVersion(actor: User, workspace: string, ecosystem: Ecosystem, name: string, version: string, surface?: AuditSurface): Promise<Result<null>>;
170 deletePackage(actor: User, workspace: string, ecosystem: Ecosystem, name: string, surface?: AuditSurface): Promise<Result<null>>;
171 /** Needs Admin. A linked package's visibility is its repository's. */
172 set(actor: User, workspace: string, ecosystem: Ecosystem, name: string, change: PackageChange, surface?: AuditSurface): Promise<Result<PackageSummary>>;
173 /** Admins only: access, Actions access, deleted versions. Not found for anyone who may not pull it. */
174 settings(workspace: string, ecosystem: Ecosystem, name: string, viewer: Viewer): Promise<Result<PackageSettings>>;
175 /** A workspace's deleted packages the viewer administers that can still be restored. */
176 deleted(workspace: string, viewer: Viewer): Promise<Result<PackageSummary[]>>;
177 restorePackage(actor: User, workspace: string, ecosystem: Ecosystem, name: string, surface?: AuditSurface): Promise<Result<PackageSummary>>;
178 /** A deleted version, by its id or version. */
179 restoreVersion(actor: User, workspace: string, ecosystem: Ecosystem, name: string, version: string, surface?: AuditSurface): Promise<Result<PackageVersion>>;
180 setAccess(actor: User, workspace: string, ecosystem: Ecosystem, name: string, who: PackageGrantee, role: PackageRole, surface?: AuditSurface): Promise<Result<PackageAccess[]>>;
181 removeAccess(actor: User, workspace: string, ecosystem: Ecosystem, name: string, who: PackageGrantee, surface?: AuditSurface): Promise<Result<PackageAccess[]>>;
182 /** A repository of the workspace, by name or `owner/name`. */
183 setActionsAccess(actor: User, workspace: string, ecosystem: Ecosystem, name: string, repo: string, role: "read" | "write", surface?: AuditSurface): Promise<Result<ActionsAccess[]>>;
184 removeActionsAccess(actor: User, workspace: string, ecosystem: Ecosystem, name: string, repo: string, surface?: AuditSurface): Promise<Result<ActionsAccess[]>>;
185 /** For billing. */
186 storage(workspace: string): Promise<PackageStorage>;
187 /** For billing: every workspace with packages, from one query. */
188 storageAll(): Promise<WorkspacePackageStorage[]>;
189 /** Read a repository's Composer package again now, as a push would. Whether it is one. */
190 syncComposer(repoId: string): Promise<boolean>;
191};
192
193/** The RPC method behind each call, as the Rust service names them. */
194export const PACKAGES_METHODS = [
195 "list_packages",
196 "get_package",
197 "list_versions",
198 "get_version",
199 "delete_version",
200 "delete_package",
201 "restore_version",
202 "restore_package",
203 "deleted_packages",
204 "set_package",
205 "package_settings",
206 "set_package_access",
207 "remove_package_access",
208 "set_actions_access",
209 "remove_actions_access",
210 "storage",
211 "storage_all",
212 "sync_composer",
213] as const;