Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Initial g1t: services, event bus, intents and attempts | 1 | import type { Repo, RepoPath, Viewer } from "@g1t/contracts"; |
| 2 | ||
| 3 | type RepoRow = { | |
| 4 | id: string; | |
| 5 | namespace: string; | |
| 6 | name: string; | |
| 7 | description: string | null; | |
| 8 | is_private: number; | |
| 9 | owner_id: string; | |
| 10 | default_branch: string; | |
| 11 | fork_of: string | null; | |
| 12 | created_at: number; | |
| 13 | }; | |
| 14 | ||
| 15 | function toRepo(row: RepoRow): Repo { | |
| 16 | return { | |
| 17 | id: row.id, | |
| 18 | namespace: row.namespace, | |
| 19 | name: row.name, | |
| 20 | description: row.description, | |
| 21 | isPrivate: row.is_private === 1, | |
| 22 | ownerId: row.owner_id, | |
| 23 | defaultBranch: row.default_branch, | |
| 24 | forkOf: row.fork_of, | |
| 25 | createdAt: row.created_at * 1000, | |
| 26 | }; | |
| 27 | } | |
| 28 | ||
| 29 | /** The key a repo is stored under in the git store. */ | |
| 30 | export function storeKey(repo: RepoPath): string { | |
| 31 | return `${repo.namespace}--${repo.name}`; | |
| 32 | } | |
| 33 | ||
| 34 | export function canRead(repo: Repo, viewer: Viewer): boolean { | |
| 35 | return !repo.isPrivate || repo.ownerId === viewer?.id; | |
| 36 | } | |
| 37 | ||
| 38 | export function canWrite(repo: Repo, viewer: Viewer): boolean { | |
| 39 | return repo.ownerId === viewer?.id; | |
| 40 | } | |
| 41 | ||
| 42 | /** Repository metadata in D1. */ | |
| 43 | export class RepoRegistry { | |
| 44 | constructor(private readonly db: D1Database) {} | |
| 45 | ||
| 46 | async byPath(path: RepoPath): Promise<Repo | null> { | |
| 47 | const row = await this.db | |
| 48 | .prepare("SELECT * FROM repos WHERE namespace = ? AND name = ?") | |
| 49 | .bind(path.namespace.toLowerCase(), path.name.toLowerCase()) | |
| 50 | .first<RepoRow>(); | |
| 51 | return row ? toRepo(row) : null; | |
| 52 | } | |
| 53 | ||
| 54 | async byId(id: string): Promise<Repo | null> { | |
| 55 | const row = await this.db | |
| 56 | .prepare("SELECT * FROM repos WHERE id = ?") | |
| 57 | .bind(id) | |
| 58 | .first<RepoRow>(); | |
| 59 | return row ? toRepo(row) : null; | |
| 60 | } | |
| 61 | ||
| 62 | /** Excludes attempt forks. */ | |
| 63 | async list( | |
| 64 | viewer: Viewer, | |
| 65 | options: { query?: string; namespace?: string }, | |
| 66 | ): Promise<Repo[]> { | |
| 67 | const where = ["fork_of IS NULL", "(is_private = 0 OR owner_id = ?)"]; | |
| 68 | const params: unknown[] = [viewer?.id ?? ""]; | |
| 69 | if (options.namespace) { | |
| 70 | where.push("namespace = ?"); | |
| 71 | params.push(options.namespace.toLowerCase()); | |
| 72 | } | |
| 73 | if (options.query?.trim()) { | |
| 74 | where.push("(name LIKE ? ESCAPE '\\' OR description LIKE ? ESCAPE '\\')"); | |
| 75 | const pattern = `%${options.query.trim().replace(/[\\%_]/g, "\\$&")}%`; | |
| 76 | params.push(pattern, pattern); | |
| 77 | } | |
| 78 | const { results } = await this.db | |
| 79 | .prepare( | |
| 80 | `SELECT * FROM repos WHERE ${where.join(" AND ")} | |
| 81 | ORDER BY created_at DESC, id DESC LIMIT 50`, | |
| 82 | ) | |
| 83 | .bind(...params) | |
| 84 | .all<RepoRow>(); | |
| 85 | return results.map(toRepo); | |
| 86 | } | |
| 87 | ||
| 88 | async insert(repo: Repo): Promise<void> { | |
| 89 | await this.db | |
| 90 | .prepare( | |
| 91 | `INSERT INTO repos | |
| 92 | (id, namespace, name, description, is_private, owner_id, default_branch, fork_of, created_at) | |
| 93 | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, | |
| 94 | ) | |
| 95 | .bind( | |
| 96 | repo.id, | |
| 97 | repo.namespace, | |
| 98 | repo.name, | |
| 99 | repo.description, | |
| 100 | Number(repo.isPrivate), | |
| 101 | repo.ownerId, | |
| 102 | repo.defaultBranch, | |
| 103 | repo.forkOf, | |
| 104 | Math.floor(repo.createdAt / 1000), | |
| 105 | ) | |
| 106 | .run(); | |
| 107 | } | |
| 108 | } |