Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206import type { BobbinContext, XrpcRequestInit } from "$lib/api/client";import { buildUrl, jsonGet, rawGet } from "$lib/api/_request";import type * as RepoBlob from "$lib/api/lexicons/types/sh/tangled/repo/blob";import type * as GitDefs from "$lib/api/lexicons/types/sh/tangled/git/temp/defs";import type * as MirrorGetRef from "$lib/api/lexicons/types/org/tangled/temp/git/getRef";import type * as MirrorEntry from "$lib/api/lexicons/types/org/tangled/temp/git/getEntry";import type * as MirrorListBranches from "$lib/api/lexicons/types/org/tangled/temp/git/listBranches";import type * as MirrorListTags from "$lib/api/lexicons/types/org/tangled/temp/git/listTags";import type * as MirrorTree from "$lib/api/lexicons/types/org/tangled/temp/git/getTree";import type * as Tree from "$lib/api/lexicons/types/sh/tangled/repo/tree";import { treeEntryKind } from "$lib/api/repo";
const TREE_NSID = "org.tangled.temp.git.getTree";const ENTRY_NSID = "org.tangled.temp.git.getEntry";const MERGE_BASE_NSID = "org.tangled.temp.git.getMergeBase";const BRANCHES_NSID = "org.tangled.temp.git.listBranches";const TAGS_NSID = "org.tangled.temp.git.listTags";const GET_REF_NSID = "org.tangled.temp.git.getRef";
const BLOB_NSID = "org.tangled.temp.git.getBlob";const FORMAT_PATCH_NSID = "sh.tangled.git.temp.formatPatch";const LANGUAGES_NSID = "sh.tangled.git.temp.listLanguages";
const MAX_README_BYTES = 1 << 20;
export interface KnotMirrorLanguagesResponse { languages?: { name: string; size: number }[]; total?: number; ref?: string;}
export const entry = ( bobbin: BobbinContext, params: { repo: string; ref: string; path: string }, init?: XrpcRequestInit) => jsonGet<MirrorEntry.$output>(bobbin, ENTRY_NSID, params, init);
export const blobRawUrl = ( serviceUrl: string, params: { repo: string; ref: string; path: string }): string => buildUrl(serviceUrl, BLOB_NSID, params).toString();
// the mirror streams `git format-patch --stdout` for a range, which is the only// way to get a large diff in one response: the knot builds the whole thing as// json first and refuses anything past its 5MB response ceilingexport const formatPatchUrl = ( serviceUrl: string, params: { repo: string; from: string; to: string }): string => buildUrl(serviceUrl, FORMAT_PATCH_NSID, params).toString();
export const formatPatch = ( ctx: BobbinContext, params: { repo: string; from: string; to: string }, init?: XrpcRequestInit) => rawGet(ctx, FORMAT_PATCH_NSID, params, init);
type LastCommit = { hash: string; message: string; when: string; author?: { name: string; email: string; when: string };};
const normalizeCommit = (commit: GitDefs.Commit | undefined): LastCommit | undefined => commit ? { hash: commit.hash, message: commit.message, when: commit.committer?.when ?? commit.author?.when ?? "", author: commit.author ? { name: commit.author.name, email: commit.author.email, when: commit.author.when } : undefined } : undefined;
const normalizeTree = (tree: MirrorTree.$output, ref: string): Tree.$output => ({ ref, lastCommit: normalizeCommit(tree.lastCommit), files: tree.entries.map((entry) => ({ name: entry.name, mode: entry.mode, size: entry.size, last_commit: normalizeCommit(entry.lastCommit) }))});
// same heuristic as git: a null byte in the first chunk means binaryconst looksBinary = (bytes: Uint8Array): boolean => bytes.subarray(0, 8000).includes(0);
// temp.getBlob answers raw bytes only, metadata comes from getEntry and// the binary flag from a content sniffexport const blob = async ( ctx: BobbinContext, params: { repo: string; ref: string; path: string }, maxBytes: number, init?: XrpcRequestInit): Promise<RepoBlob.$output> => { const meta = await entry(ctx, params, init); const lastCommit = normalizeCommit(meta.lastCommit);
// a gitlink is a submodule pointer, not a file if (treeEntryKind(meta.mode) === "submodule") { return { path: params.path, ref: params.ref, size: meta.size, submodule: { name: meta.name, url: "" } }; }
if (meta.size !== undefined && meta.size > maxBytes) { return { path: params.path, ref: params.ref, size: meta.size, fileTooLarge: true, lastCommit }; }
const bytes = new Uint8Array(await (await rawGet(ctx, BLOB_NSID, params, init)).arrayBuffer()); if (looksBinary(bytes)) { return { path: params.path, ref: params.ref, size: meta.size ?? bytes.length, isBinary: true, lastCommit }; }
return { path: params.path, ref: params.ref, size: meta.size ?? bytes.length, isBinary: false, encoding: "utf-8", content: new TextDecoder().decode(bytes), lastCommit };};
const isReadmeFile = (name: string, mode: string): boolean => /^readme(?:\.[^.]+)?$/i.test(name) && treeEntryKind(mode) === "file";
const pathInTree = (path: string | undefined, name: string): string => path ? `${path}/${name}` : name;
export const tree = async ( ctx: BobbinContext, params: { repo: string; ref: string; path?: string }, init?: XrpcRequestInit): Promise<Tree.$output> => { const response = await jsonGet<MirrorTree.$output>(ctx, TREE_NSID, params, init); const result = normalizeTree(response, params.ref); const readme = response.entries.find((entry) => isReadmeFile(entry.name, entry.mode)); if (!readme) return result; if (readme.size !== undefined && readme.size > MAX_README_BYTES) return result;
try { const blob = await rawGet( ctx, BLOB_NSID, { repo: params.repo, ref: params.ref, path: pathInTree(params.path, readme.name) }, init ); return { ...result, readme: { filename: readme.name, contents: await blob.text() } }; } catch { return result; }};
export const branches = ( ctx: BobbinContext, params: { repo: string; limit: number; cursor?: string }, init?: XrpcRequestInit) => jsonGet<MirrorListBranches.$output>(ctx, BRANCHES_NSID, params, init);
export const tags = ( ctx: BobbinContext, params: { repo: string; limit: number; cursor?: string }, init?: XrpcRequestInit) => jsonGet<MirrorListTags.$output>(ctx, TAGS_NSID, params, init);
export const getRef = ( ctx: BobbinContext, params: { repo: string; ref: string }, init?: XrpcRequestInit) => jsonGet<MirrorGetRef.$output>(ctx, GET_REF_NSID, params, init);
export const languages = ( ctx: BobbinContext, params: { repo: string; ref: string }, init?: XrpcRequestInit) => jsonGet<KnotMirrorLanguagesResponse>(ctx, LANGUAGES_NSID, params, init);
export const getMergeBase = ( bobbin: BobbinContext, params: { repo: string; base: string; head: string }, init?: XrpcRequestInit) => jsonGet<{ commit: string }>(bobbin, MERGE_BASE_NSID, params, init);