import { createBobbinClient, type BobbinContext } from "./client"; import { branches as knotMirrorBranches, blob as knotMirrorBlob, blobRawUrl as knotMirrorBlobRawUrl, createKnotMirrorClient, languages as knotMirrorLanguages, log as knotMirrorLog, getTag as knotMirrorGetTag, tags as knotMirrorTags, tree as knotMirrorTree } from "./knotmirror"; import { blob as knotBlob, blobRawUrl as knotBlobRawUrl, getDefaultBranch, languages as knotLanguages, tree as knotTree } from "./knot"; import { branchesFor, logFor, tagFor, tagsFor } from "./repo"; import type { Did, ResourceUri } from "@atcute/lexicons"; export interface GitServiceConfig { bobbinUrl: string; knotMirrorUrl: string; } export interface GitTarget { ctx: BobbinContext; repo: Did | ResourceUri; via: "mirror" | "bobbin"; } export const gitTarget = ( config: GitServiceConfig, repo: { uri: string; repoDid?: string }, fetch: typeof globalThis.fetch ): GitTarget => config.knotMirrorUrl && repo.repoDid ? { ctx: createKnotMirrorClient(config.knotMirrorUrl, fetch), repo: repo.repoDid as Did, via: "mirror" } : { ctx: createBobbinClient({ serviceUrl: config.bobbinUrl, fetch }), repo: repo.uri as ResourceUri, via: "bobbin" }; // the mirror has no getDefaultBranch, the default falls out of the branch // list's is_default flag instead export const resolveDefaultBranch = ( target: GitTarget, init?: { signal?: AbortSignal } ): Promise => target.via === "mirror" ? (async () => { for (let offset = 0; ; offset += 100) { const response = await knotMirrorBranches( target.ctx, { repo: target.repo, limit: 100, cursor: offset ? `${offset}` : undefined }, init ); const list = response.branches ?? []; const found = list.find((branch) => branch.is_default)?.reference.name; if (found) return found; if (list.length < 100) return null; } })().catch(() => null) : getDefaultBranch(target.ctx, { repo: target.repo }) .then((branch) => branch.name) .catch(() => null); export const tree = (target: GitTarget, params: { ref: string; path?: string }) => target.via === "mirror" ? knotMirrorTree(target.ctx, { repo: target.repo, ...params }) : knotTree(target.ctx, { repo: target.repo, ...params }); export const log = (target: GitTarget, params: { ref: string; limit: number; cursor?: string }) => target.via === "mirror" ? knotMirrorLog(target.ctx, { repo: target.repo, ...params }) : logFor(target.ctx, target.repo, params.ref, params.limit, params.cursor); export const branches = (target: GitTarget, limit: number, cursor?: string) => target.via === "mirror" ? knotMirrorBranches(target.ctx, { repo: target.repo, limit, cursor }) : branchesFor(target.ctx, target.repo, limit, cursor); export const tags = (target: GitTarget, limit: number, cursor?: string) => target.via === "mirror" ? knotMirrorTags(target.ctx, { repo: target.repo, limit, cursor }) : tagsFor(target.ctx, target.repo, limit, cursor); export const tag = (target: GitTarget, name: string) => target.via === "mirror" ? knotMirrorGetTag(target.ctx, { repo: target.repo, tag: name }) : tagFor(target.ctx, target.repo, name); export const languages = ( target: GitTarget, ref: string ): Promise<{ languages?: { name: string; size: number }[] }> => target.via === "mirror" ? knotMirrorLanguages(target.ctx, { repo: target.repo, ref }) : knotLanguages(target.ctx, { repo: target.repo, ref }); // only the mirror needs the inline cap, the knot enforces its own and 413s // past it export const blob = (target: GitTarget, params: { ref: string; path: string }, maxBytes: number) => target.via === "mirror" ? knotMirrorBlob(target.ctx, { repo: target.repo, ...params }, maxBytes) : knotBlob(target.ctx, { repo: target.repo as ResourceUri, ...params }); export const blobRawUrl = (target: GitTarget, params: { ref: string; path: string }): string => target.via === "mirror" ? knotMirrorBlobRawUrl(target.ctx.serviceUrl, { repo: target.repo, ...params }) : knotBlobRawUrl(target.ctx.serviceUrl, { repo: target.repo, ...params });