1/**
2 * The typed HTTP contract for git-service (DESIGN.md §5): every plane as
3 * one Effect `HttpApi`, so the Worker, the clients
4 * (`HttpApiClient.make(GitApi, ...)`), and OpenAPI share one schema-checked
5 * surface.
6 *
7 * Endpoints use `HttpApiEndpoint`; groups are implemented with
8 * `HttpApiBuilder.group`. The exported groups can also form a smaller API.
9 *
10 * | Group | Path | Routes |
11 * | --- | --- | --- |
12 * | {@link Repos} | `/api/v1/repos` | create, get, update, list, delete, fork, import, compact |
13 * | {@link Refs} | `/api/v1/repos/:owner/:repo/ref(s)` | list, get, update, remove |
14 * | {@link Objects} | `/api/v1/repos/:owner/:repo/…` | commit, log, tree, blob, diff, compare, blobRaw, file |
15 * | {@link Pulls} | `/api/v1/repos/:owner/:repo/pulls` | create, list, get, update, merge |
16 * | {@link Protocol} | `/:owner/:repo/…` | infoRefs, uploadPack, receivePack |
17 * | {@link GitHub} | `/api/v3` | the GitHub REST v3 facade |
18 *
19 * No route carries middleware: apply application HttpRouter middleware to
20 * Git.ApiLive when composing the public routes. The engine's own hash route is
21 * {@link InternalApi}, mounted separately. Shared schemas and tagged
22 * errors live in `Api/Schema.ts` and are re-exported flatly from here.
23 */
24import type * as HttpServerRequest from "effect/http/HttpServerRequest";
25import * as HttpApi from "effect/http-api/HttpApi";
26import { GitHub } from "./Api/GitHub.ts";
27import { Objects } from "./Api/Objects.ts";
28import { Protocol } from "./Api/Protocol.ts";
29import { Pulls } from "./Api/Pulls.ts";
30import { Refs } from "./Api/Refs.ts";
31import { Repos } from "./Api/Repos.ts";
32
33export * from "./Api/Schema.ts";
34export * from "./Api/GitHub.ts";
35export * from "./Api/Internal.ts";
36export * from "./Api/Objects.ts";
37export * from "./Api/Protocol.ts";
38export * from "./Api/Pulls.ts";
39export * from "./Api/Refs.ts";
40export * from "./Api/Repos.ts";
41
42/**
43 * The complete git-service API: the REST plane at `/api/v1`, the git wire
44 * protocol at the root, and the GitHub facade at `/api/v3`. Compose client
45 * schemas with `AppApi.addHttpApi(Git.Api)`, or build one from the groups.
46 */
47export class GitApi extends HttpApi.make("git-service")
48 .add(Repos)
49 .add(Refs)
50 .add(Objects)
51 .add(Pulls)
52 .add(Protocol)
53 .add(GitHub) {}
54
55/**
56 * Whether a request to one of the engine's routes only reads: the REST
57 * and raw reads, the GitHub facade's `GET`s, the ref advertisement for a
58 * fetch, and `git-upload-pack`. The advertisement for a push
59 * (`info/refs?service=git-receive-pack`) is not a read. A middleware that
60 * lets anonymous callers read public repositories asks this.
61 *
62 * ```typescript
63 * (httpEffect) =>
64 * Effect.gen(function* () {
65 * const request = yield* HttpServerRequest.HttpServerRequest;
66 * if (Git.isRead(request) && (yield* isPublic)) return yield* httpEffect;
67 * // …
68 * })
69 * ```
70 */
71export const isRead = (
72 request: HttpServerRequest.HttpServerRequest,
73): boolean => {
74 const url = new URL(request.url, "http://localhost");
75 if (request.method === "POST")
76 return url.pathname.endsWith("/git-upload-pack");
77 if (request.method !== "GET" && request.method !== "HEAD") return false;
78 return (
79 !url.pathname.endsWith("/info/refs") ||
80 url.searchParams.get("service") !== "git-receive-pack"
81 );
82};