4.5 KB195 lines
Blame
1/**
2 * BridgeService provides read access to repos via grove-bridge (Mononoke HTTP API).
3 * Replaces the old GitService that used bare git clones + shell commands.
4 */
5export class BridgeService {
6 constructor(private bridgeUrl: string) {}
7
8 async listTree(
9 _owner: string,
10 repo: string,
11 ref: string,
12 path: string = ""
13 ): Promise<TreeEntry[]> {
14 try {
15 const url = path
16 ? `${this.bridgeUrl}/repos/${repo}/tree/${ref}/${path}`
17 : `${this.bridgeUrl}/repos/${repo}/tree/${ref}/`;
18 const res = await fetch(url);
19 if (!res.ok) return [];
20 const data = await res.json();
21 return (data.entries ?? []).map((e: any) => ({
22 name: e.name,
23 type: e.type as "blob" | "tree",
24 mode: e.type === "tree" ? "040000" : "100644",
25 hash: "",
26 }));
27 } catch {
28 return [];
29 }
30 }
31
32 async getBlob(
33 _owner: string,
34 repo: string,
35 ref: string,
36 path: string
37 ): Promise<{ content: string; size: number } | null> {
38 try {
39 const res = await fetch(
40 `${this.bridgeUrl}/repos/${repo}/blob/${ref}/${path}`
41 );
42 if (!res.ok) return null;
43 const data = await res.json();
44 return { content: data.content, size: data.size };
45 } catch {
46 return null;
47 }
48 }
49
50 async getCommits(
51 _owner: string,
52 repo: string,
53 ref: string,
54 options: { path?: string; limit?: number; offset?: number } = {}
55 ): Promise<CommitInfo[]> {
56 try {
57 const limit = options.limit ?? 30;
58 const res = await fetch(
59 `${this.bridgeUrl}/repos/${repo}/commit/${ref}/history?limit=${limit}`
60 );
61 if (!res.ok) return [];
62 const data = await res.json();
63 const commits = (data.commits ?? []).map((c: any) => ({
64 hash: c.hash,
65 author: c.author,
66 email: "",
67 timestamp: c.timestamp,
68 subject: (c.message ?? "").split("\n")[0],
69 body: (c.message ?? "").split("\n").slice(1).join("\n").trim(),
70 parents: c.parents ?? [],
71 }));
72 const offset = options.offset ?? 0;
73 return offset > 0 ? commits.slice(offset) : commits;
74 } catch {
75 return [];
76 }
77 }
78
79 async getBlame(
80 _owner: string,
81 repo: string,
82 ref: string,
83 path: string
84 ): Promise<BlameLine[]> {
85 try {
86 const res = await fetch(
87 `${this.bridgeUrl}/repos/${repo}/blame/${ref}/${path}`
88 );
89 if (!res.ok) return [];
90 const data = await res.json();
91 return (data.blame ?? []).map((b: any) => ({
92 hash: b.hash,
93 originalLine: b.original_line,
94 author: b.author,
95 timestamp: b.timestamp,
96 summary: "",
97 content: b.content,
98 }));
99 } catch {
100 return [];
101 }
102 }
103
104 async getDiff(
105 _owner: string,
106 repo: string,
107 base: string,
108 head: string
109 ): Promise<{ base: string; head: string; diffs: any[] }> {
110 try {
111 const res = await fetch(
112 `${this.bridgeUrl}/repos/${repo}/diff/${base}/${head}`
113 );
114 if (!res.ok) return { base, head, diffs: [] };
115 const data = await res.json();
116 return { base: data.base ?? base, head: data.head ?? head, diffs: data.diffs ?? [] };
117 } catch {
118 return { base, head, diffs: [] };
119 }
120 }
121
122 async getBranches(_owner: string, repo: string): Promise<BranchInfo[]> {
123 try {
124 const res = await fetch(
125 `${this.bridgeUrl}/repos/${repo}/bookmarks`
126 );
127 if (!res.ok) return [];
128 const data = await res.json();
129 return (data.bookmarks ?? []).map((b: any) => ({
130 name: b.name,
131 hash: b.commit_id,
132 timestamp: 0,
133 subject: "",
134 }));
135 } catch {
136 return [];
137 }
138 }
139
140 async getReadme(
141 owner: string,
142 repo: string,
143 ref: string
144 ): Promise<string | null> {
145 const readmeNames = [
146 "README.md",
147 "README.markdown",
148 "README.txt",
149 "README",
150 "readme.md",
151 ];
152
153 for (const name of readmeNames) {
154 const blob = await this.getBlob(owner, repo, ref, name);
155 if (blob) return blob.content;
156 }
157 return null;
158 }
159}
160
161// Types
162
163export interface TreeEntry {
164 mode: string;
165 type: "blob" | "tree";
166 hash: string;
167 name: string;
168}
169
170export interface CommitInfo {
171 hash: string;
172 author: string;
173 email: string;
174 timestamp: number;
175 subject: string;
176 body: string;
177 parents: string[];
178}
179
180export interface BlameLine {
181 hash: string;
182 originalLine: number;
183 author: string;
184 timestamp: number;
185 summary: string;
186 content: string;
187}
188
189export interface BranchInfo {
190 name: string;
191 hash: string;
192 timestamp: number;
193 subject: string;
194}
195