| 1 | /** |
| 2 | * The `objects` group: commit, log, tree, blob, diff, and compare reads, |
| 3 | * plus the two raw streaming reads (DESIGN.md §5): a blob's bytes as an |
| 4 | * octet-stream, and a file at a path under a ref. The raw routes declare |
| 5 | * no success schema and answer with the response they build. |
| 6 | */ |
| 7 | import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint"; |
| 8 | import * as Schema from "effect/Schema"; |
| 9 | import * as HttpApiGroup from "effect/http-api/HttpApiGroup"; |
| 10 | import { |
| 11 | CommitDiff, |
| 12 | CommitInfo, |
| 13 | Comparison, |
| 14 | NoMergeBase, |
| 15 | ObjectNotFound, |
| 16 | ObjectTooLarge, |
| 17 | Oid, |
| 18 | Paginated, |
| 19 | RefNotFound, |
| 20 | RepoNotFound, |
| 21 | RepoOidPath, |
| 22 | RepoPath, |
| 23 | TreeEntry, |
| 24 | WrongObjectType, |
| 25 | } from "./Schema.ts"; |
| 26 | |
| 27 | /** Reads one commit. */ |
| 28 | export const GetCommit = HttpApiEndpoint.get( |
| 29 | "commit", |
| 30 | "/repos/:owner/:repo/commits/:oid", |
| 31 | { |
| 32 | params: RepoOidPath, |
| 33 | success: CommitInfo, |
| 34 | error: [RepoNotFound, ObjectNotFound, WrongObjectType], |
| 35 | }, |
| 36 | ); |
| 37 | |
| 38 | /** Pages the commit history from a ref or oid. */ |
| 39 | export const GetLog = HttpApiEndpoint.get("log", "/repos/:owner/:repo/log", { |
| 40 | params: RepoPath, |
| 41 | query: Schema.Struct({ |
| 42 | /** Refname or oid to start from. @default HEAD */ |
| 43 | ref: Schema.optional(Schema.String), |
| 44 | cursor: Schema.optional(Schema.String), |
| 45 | limit: Schema.optional( |
| 46 | Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })), |
| 47 | ), |
| 48 | }), |
| 49 | success: Paginated(CommitInfo), |
| 50 | error: [RepoNotFound, RefNotFound], |
| 51 | }); |
| 52 | |
| 53 | /** Reads one tree's entries. */ |
| 54 | export const GetTree = HttpApiEndpoint.get( |
| 55 | "tree", |
| 56 | "/repos/:owner/:repo/trees/:oid", |
| 57 | { |
| 58 | params: RepoOidPath, |
| 59 | success: Schema.Struct({ |
| 60 | oid: Oid, |
| 61 | entries: Schema.Array(TreeEntry), |
| 62 | }), |
| 63 | error: [RepoNotFound, ObjectNotFound, WrongObjectType], |
| 64 | }, |
| 65 | ); |
| 66 | |
| 67 | /** Reads a small blob as base64 JSON (≤ 1 MiB; 422 otherwise — use /raw). */ |
| 68 | export const GetBlob = HttpApiEndpoint.get( |
| 69 | "blob", |
| 70 | "/repos/:owner/:repo/blobs/:oid", |
| 71 | { |
| 72 | params: RepoOidPath, |
| 73 | success: Schema.Struct({ |
| 74 | oid: Oid, |
| 75 | /** Uncompressed size in bytes. */ |
| 76 | size: Schema.Number, |
| 77 | encoding: Schema.Literals(["base64"]), |
| 78 | /** Base64 content — blobs ≤ 1 MiB only (422 otherwise; use /raw). */ |
| 79 | content: Schema.String, |
| 80 | }), |
| 81 | error: [RepoNotFound, ObjectNotFound, WrongObjectType, ObjectTooLarge], |
| 82 | }, |
| 83 | ); |
| 84 | |
| 85 | /** |
| 86 | * The changed-file list of a commit vs its FIRST parent (empty tree for a |
| 87 | * root commit). Merge commits are diffed against parent[0] only — the |
| 88 | * GitHub default. No rename detection in v1: a rename appears as |
| 89 | * `removed` + `added`; clients may pair entries whose old/new oids match |
| 90 | * for a cheap exact-rename display. |
| 91 | */ |
| 92 | export const GetDiff = HttpApiEndpoint.get( |
| 93 | "diff", |
| 94 | "/repos/:owner/:repo/commits/:oid/diff", |
| 95 | { |
| 96 | params: RepoOidPath, |
| 97 | success: CommitDiff, |
| 98 | error: [RepoNotFound, ObjectNotFound, WrongObjectType], |
| 99 | }, |
| 100 | ); |
| 101 | |
| 102 | /** |
| 103 | * Three-dot comparison: merge base, ahead/behind counts, head-side |
| 104 | * commits, and the file diff of mergeBase..head. `base`/`head` accept a |
| 105 | * refname (short or full) or a 40-hex oid; annotated tags are peeled. |
| 106 | */ |
| 107 | export const Compare = HttpApiEndpoint.get( |
| 108 | "compare", |
| 109 | "/repos/:owner/:repo/compare", |
| 110 | { |
| 111 | params: RepoPath, |
| 112 | query: Schema.Struct({ |
| 113 | /** Refname or oid of the base side. */ |
| 114 | base: Schema.String, |
| 115 | /** Refname or oid of the head side. */ |
| 116 | head: Schema.String, |
| 117 | }), |
| 118 | success: Comparison, |
| 119 | error: [ |
| 120 | RepoNotFound, |
| 121 | RefNotFound, |
| 122 | ObjectNotFound, |
| 123 | WrongObjectType, |
| 124 | NoMergeBase, |
| 125 | ], |
| 126 | }, |
| 127 | ); |
| 128 | |
| 129 | /** |
| 130 | * A blob's bytes as an octet-stream, no size cap (the per-object 64 MiB |
| 131 | * ingest cap is the outer bound). Streams the response it builds. |
| 132 | */ |
| 133 | export const GetBlobRaw = HttpApiEndpoint.get( |
| 134 | "blobRaw", |
| 135 | "/repos/:owner/:repo/blobs/:oid/raw", |
| 136 | {}, |
| 137 | ); |
| 138 | |
| 139 | /** |
| 140 | * A file at `?path=` under `?ref=` (refname or oid; the default branch |
| 141 | * when absent), walked tree by tree, as an octet-stream. |
| 142 | */ |
| 143 | export const GetFile = HttpApiEndpoint.get( |
| 144 | "file", |
| 145 | "/repos/:owner/:repo/file", |
| 146 | {}, |
| 147 | ); |
| 148 | |
| 149 | /** The `objects` group, mounted at `/api/v1`. */ |
| 150 | export class Objects extends HttpApiGroup.make("objects") |
| 151 | .add( |
| 152 | GetCommit, |
| 153 | GetLog, |
| 154 | GetTree, |
| 155 | GetBlob, |
| 156 | GetDiff, |
| 157 | Compare, |
| 158 | GetBlobRaw, |
| 159 | GetFile, |
| 160 | ) |
| 161 | .prefix("/api/v1") {} |