159 lines · 6.4 KB
import { recordArtifactsCall } from "./artifacts-health";
import { env } from "cloudflare:workers";
import { first } from "../db";
import { basicAuthHeader, repoTokenSecret } from "./artifacts";
import { concatBytes, packetText, parsePacket, pktLine } from "./pktline";
import { parsePack } from "./pack";
import { limitStream, PackTooLargeError } from "./ref-sync-protocol";

const DELIM_PKT = new TextEncoder().encode("0001");
const FLUSH = new TextEncoder().encode("0000");
const SIDEBAND_DATA = 1;
const SIDEBAND_ERROR = 3;
const MAX_SNAPSHOT_BYTES = 32 * 1024 * 1024;
const MAX_SNAPSHOT_OBJECTS = 200_000;
const MAX_TREE_OBJECT_BYTES = 8 * 1024 * 1024;
const SNAPSHOT_TIMEOUT_MS = 20_000;
const REMOTE_CACHE_SECONDS = 60 * 60;
const SHA_BYTES = 20;
const decoder = new TextDecoder();

export type TreeListing = Map<string, ArtifactsTreeEntry[]>;
export type Snapshot = { trees: TreeListing; blobs: Map<string, Uint8Array> };
/** Inflated bytes kept in memory for a snapshot with contents; larger trees fall back to per-blob reads. */
const MAX_SNAPSHOT_INFLATED_BYTES = 32 * 1024 * 1024;
const MAX_SNAPSHOT_BLOB_BYTES = 16 * 1024 * 1024;

function entryType(mode: string): ArtifactsTreeEntryType {
  if (mode === "40000") return "tree";
  if (mode === "160000") return "gitlink";
  if (mode === "120000") return "symlink";
  if (mode === "100755") return "exec";
  return "blob";
}

/** Decodes a raw git tree object into the same shape `readTree` returns. */
export function parseTreeObject(content: Uint8Array): ArtifactsTreeEntry[] {
  const entries: ArtifactsTreeEntry[] = [];
  let offset = 0;
  while (offset < content.byteLength) {
    const space = content.indexOf(0x20, offset);