135 lines · 3.6 KB · TypeScript
RawDownload
/* oxlint-disable executor/no-try-catch-or-throw, executor/no-instanceof-error, executor/no-unknown-error-message -- boundary: git client returns stable ok/error envelopes before git-source maps to AppSourceError */// Hand-rolled shallow fetch. Orchestrates transport and packfile parsing.import { resolveWant } from "./pktline";import { parsePack, walkTree, type ParsePackLimits } from "./packfile";import { authForHost, checkRefs, uploadPack, type AuthRecipe } from "./transport"; export interface FetchResult {  ok: boolean;  sha?: string;  resolvedRef?: string;  fileCount?: number;  totalBytes?: number;  packBytes?: number;  refsMs?: number;  fetchMs?: number;  parseMs?: number;  wallMs?: number;  truncated?: boolean;  files?: { path: string; bytes: Uint8Array }[];  error?: string;} export async function handFetch(  url: string,  ref?: string,  opts: {    token?: string;    maxBytes?: number;    fetchImpl?: typeof fetch;    parseLimits?: Partial<ParsePackLimits>;    allowPrivateHosts?: boolean;  } = {},): Promise<FetchResult> {  const t0 = Date.now();  try {    const host = new URL(url).host;    const auth: AuthRecipe = authForHost(host, opts.token);    const refs = await checkRefs(url, auth, opts.fetchImpl, {      allowPrivateHosts: opts.allowPrivateHosts,    });    const { sha, resolvedRef } = resolveWant(refs.adv, ref);     const up = await uploadPack(url, sha, {      auth,      fetchImpl: opts.fetchImpl,      maxBytes: opts.maxBytes,      allowPrivateHosts: opts.allowPrivateHosts,    });    if (up.truncated) {      return {        ok: false,        sha,        resolvedRef,        packBytes: up.packBytes.length,        refsMs: refs.wallMs,        fetchMs: up.wallMs,        truncated: true,        wallMs: Date.now() - t0,        error: `byte cap hit at ${up.capUsed} bytes`,      };    }     const tp = Date.now();    const parsed = await parsePack(up.packBytes, { limits: opts.parseLimits });    const files = walkTree(parsed, sha);    const parseMs = Date.now() - tp;     let totalBytes = 0;    for (const f of files) totalBytes += f.bytes.length;     return {      ok: true,      sha,      resolvedRef,      fileCount: files.length,      totalBytes,      packBytes: up.packBytes.length,      refsMs: refs.wallMs,      fetchMs: up.wallMs,      parseMs,      wallMs: Date.now() - t0,      truncated: false,      files: files.map((f) => ({ path: f.path, bytes: f.bytes })),    };  } catch (e) {    return {      ok: false,      error: String(e instanceof Error ? e.message : e),      wallMs: Date.now() - t0,    };  }} // refs-only cheap checkexport async function handRefsCheck(  url: string,  ref?: string,  opts: { token?: string; fetchImpl?: typeof fetch; allowPrivateHosts?: boolean } = {},): Promise<{  ok: boolean;  refCount?: number;  headSha?: string;  head?: string;  sha?: string;  resolvedRef?: string;  wallMs?: number;  error?: string;}> {  const t0 = Date.now();  try {    const host = new URL(url).host;    const auth = authForHost(host, opts.token);    const refs = await checkRefs(url, auth, opts.fetchImpl, {      allowPrivateHosts: opts.allowPrivateHosts,    });    const head = refs.adv.headTarget;    const headSha = head ? refs.adv.refs.get(head) : refs.adv.refs.get("HEAD");    const wanted = resolveWant(refs.adv, ref);    return {      ok: true,      refCount: refs.adv.refs.size,      head,      headSha,      sha: wanted.sha,      resolvedRef: wanted.resolvedRef,      wallMs: Date.now() - t0,    };  } catch (e) {    return {      ok: false,      error: String(e instanceof Error ? e.message : e),      wallMs: Date.now() - t0,    };  }}