1/**
2 * The `refs` group: ref reads and transactional (CAS) writes (DESIGN.md §5).
3 *
4 * Ref names contain `/`, so the single-ref endpoints address the ref via
5 * the `name` query parameter, not a path segment.
6 */
7import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
8import * as Schema from "effect/Schema";
9import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
10import * as HttpApiSchema from "effect/http-api/HttpApiSchema";
11import {
12 ObjectNotFound,
13 Oid,
14 ReadOnlyRepo,
15 Ref,
16 RefConflict,
17 RefName,
18 RefNotFound,
19 RepoNotFound,
20 RepoPath,
21 PushDenied,
22} from "./Schema.ts";
23
24/** Lists refs, optionally filtered by prefix (e.g. `refs/heads/`). */
25export const ListRefs = HttpApiEndpoint.get(
26 "list",
27 "/repos/:owner/:repo/refs",
28 {
29 params: RepoPath,
30 query: Schema.Struct({
31 /** Filter by prefix, e.g. `refs/heads/`. */
32 prefix: Schema.optional(Schema.String),
33 }),
34 success: Schema.Struct({
35 /** The default branch's full ref name, `null` on an unborn repo. */
36 head: Schema.NullOr(Schema.String),
37 refs: Schema.Array(Ref),
38 }),
39 error: [RepoNotFound],
40 },
41);
42
43/** Reads one ref by full name (`?name=refs/heads/main`). */
44export const GetRef = HttpApiEndpoint.get("get", "/repos/:owner/:repo/ref", {
45 params: RepoPath,
46 query: Schema.Struct({ name: RefName }),
47 success: Ref,
48 error: [RepoNotFound, RefNotFound],
49});
50
51/** Writes one ref with CAS semantics. */
52export const UpdateRef = HttpApiEndpoint.put(
53 "update",
54 "/repos/:owner/:repo/ref",
55 {
56 params: RepoPath,
57 query: Schema.Struct({ name: RefName }),
58 payload: Schema.Struct({
59 newOid: Oid,
60 /**
61 * CAS token: current oid the ref must hold. `null` = the ref must
62 * not exist (create). Absent = unconditional write.
63 */
64 expectedOid: Schema.optional(Schema.NullOr(Oid)),
65 }),
66 success: Ref,
67 error: [
68 PushDenied,
69 RepoNotFound,
70 RefConflict,
71 ObjectNotFound,
72 ReadOnlyRepo,
73 ],
74 },
75);
76
77/** Deletes one ref with CAS semantics. */
78export const RemoveRef = HttpApiEndpoint.delete(
79 "remove",
80 "/repos/:owner/:repo/ref",
81 {
82 params: RepoPath,
83 query: Schema.Struct({ name: RefName }),
84 payload: Schema.Struct({
85 /** CAS token: current oid the ref must hold. Absent = unconditional. */
86 expectedOid: Schema.optional(Oid),
87 }),
88 success: HttpApiSchema.NoContent,
89 error: [PushDenied, RepoNotFound, RefNotFound, RefConflict, ReadOnlyRepo],
90 },
91);
92
93/** The `refs` group, mounted at `/api/v1`. */
94export class Refs extends HttpApiGroup.make("refs")
95 .add(ListRefs, GetRef, UpdateRef, RemoveRef)
96 .prefix("/api/v1") {}