g1t/packages/contracts/src/packages.ts

121 lines5,318 bytesCodeBlame
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 roles (Read
11 * pulls, Write publishes, Admin deletes and changes settings); an unlinked
12 * one is its workspace's: members by the base permission, owners delete.
13 */
14
15/** Which registry a package is in. */
16export const ECOSYSTEMS = ["container", "npm", "composer", "cargo", "go", "maven", "nuget", "rubygems"] as const;
17export type Ecosystem = (typeof ECOSYSTEMS)[number];
18
19export type PackageVisibility = "public" | "private";
20
21export type LinkedRepo = { id: string; namespace: string; name: string };
22
23/** A package as listings show it. */
24export type PackageSummary = {
25 id: string;
26 workspace: string;
27 ecosystem: Ecosystem;
28 /** Without the workspace: `web` for `g1t.sh/acme/web`. */
29 name: string;
30 /** What a client is given: `g1t.sh/acme/web` for a container image. */
31 address: string;
32 /** Linked packages follow their repository's visibility. */
33 visibility: PackageVisibility;
34 repo: LinkedRepo | null;
35 description: string | null;
36 versions: number;
37 /** The newest version's tag (`latest` when it has one) or version. */
38 latest: string | null;
39 /** Bytes its versions hold, each file counted once. */
40 size: number;
41 /** Pulls and installs, counted approximately. */
42 downloads: number;
43 created_at: string;
44 updated_at: string;
45};
46
47/** One version: for a container image, one manifest, by digest. */
48export type PackageVersion = {
49 id: string;
50 version: string;
51 digest: string;
52 size: number;
53 media_type: string | null;
54 /** For an OCI artifact: what it is, such as a signature or an SBOM. */
55 artifact_type: string | null;
56 /** For an artifact attached to another version: that version's digest. */
57 subject: string | null;
58 /** For an image index: the platforms it holds, such as `linux/amd64`. */
59 platforms: string[];
60 tags: string[];
61 published_by: string | null;
62 published_at: string;
63 /** npm: why the version should no longer be used, when it is deprecated. */
64 deprecated?: string | null;
65 /** NuGet: whether a symbol package (`.snupkg`) was pushed for it. */
66 symbols?: boolean;
67 /** NuGet: its own downloads, where they are counted by version. */
68 downloads?: number | null;
69};
70
71export type PackageTag = { tag: string; digest: string; updated_at: string };
72
73/** What the viewer may do with a package. `admin`: change its visibility and link. */
74export type PackagePermissions = { pull: boolean; push: boolean; delete: boolean; admin: boolean };
75
76export type PackageDetail = {
77 package: PackageSummary;
78 /** Newest first. */
79 versions: PackageVersion[];
80 tags: PackageTag[];
81 permissions: PackagePermissions;
82 /** The package's README, as markdown: npm's, from its latest version. */
83 readme?: string | null;
84};
85
86/** What a workspace's packages hold, for billing: each file once, public when any public package uses it. */
87export type PackageStorage = { public_bytes: number; private_bytes: number };
88
89/** `storage_all`: every workspace with packages, by workspace, for billing's daily measure. */
90export type WorkspacePackageStorage = { workspace: string; public_bytes: number; private_bytes: number };
91
92export type PackageFilter = { ecosystem?: Ecosystem | null; repoId?: string | null; query?: string | null };
93
94export type PackageChange = {
95 visibility?: PackageVisibility;
96 /** A repository of the package's workspace, by name. */
97 link?: string;
98 /** Take the link away. */
99 unlink?: boolean;
100};
101
102export type PackagesApi = {
103 /** The workspace's packages the viewer may pull, newest first. */
104 list(workspace: string, viewer: Viewer, filter?: PackageFilter): Promise<Result<PackageSummary[]>>;
105 /** Not found when the viewer may not pull it. */
106 get(workspace: string, ecosystem: Ecosystem, name: string, viewer: Viewer): Promise<Result<PackageDetail>>;
107 /** A version by version, digest or tag; its tags go with it. */
108 deleteVersion(actor: User, workspace: string, ecosystem: Ecosystem, name: string, version: string, surface?: AuditSurface): Promise<Result<null>>;
109 deletePackage(actor: User, workspace: string, ecosystem: Ecosystem, name: string, surface?: AuditSurface): Promise<Result<null>>;
110 /** Needs Admin. A linked package's visibility is its repository's. */
111 set(actor: User, workspace: string, ecosystem: Ecosystem, name: string, change: PackageChange, surface?: AuditSurface): Promise<Result<PackageSummary>>;
112 /** For billing. */
113 storage(workspace: string): Promise<PackageStorage>;
114 /** For billing: every workspace with packages, from one query. */
115 storageAll(): Promise<WorkspacePackageStorage[]>;
116 /** Read a repository's Composer package again now, as a push would. Whether it is one. */
117 syncComposer(repoId: string): Promise<boolean>;
118};
119
120/** The RPC method behind each call, as the Rust service names them. */
121export const PACKAGES_METHODS = ["list_packages", "get_package", "delete_version", "delete_package", "set_package", "storage", "storage_all", "sync_composer"] as const;