118 lines · 4.8 KB
import { env } from "cloudflare:workers";
import { kvCached, kvPut } from "./cache";
import { sha256Hex } from "../crypto";
import type { GitStore } from "../git/store";
import { toBrief } from "~/lib/code-intel";
import type { CommitBrief } from "~/lib/code-intel";
import type { CommitSummary } from "~/lib/types";

const PAGE_SIZE = 30;
const WALK_BUDGET_PER_REQUEST = 300;
const MAX_PATH_MATCHES = 1000;
const MAX_LAST_COMMIT_WALK = 400;
const MAX_INLINE_KEY_PATH = 200;

/** KV keys are capped at 512 bytes, so long paths are hashed. */
export async function pathKey(path: string): Promise<string> {
  return path.length <= MAX_INLINE_KEY_PATH ? path : `h:${await sha256Hex(path)}`;
}

async function pathSha(store: GitStore, commit: CommitSummary, path: string): Promise<string | null> {
  const entry = await store.entryAtPath(commit.treeSha, path);
  return entry?.sha ?? null;
}

type PathLogState = { matches: CommitBrief[]; cursor: string | null; cursorPathSha: string | null };

export type PathHistoryPage = { commits: CommitBrief[]; hasMore: boolean; capped: boolean };

/**
 * First-parent history of commits that changed `path`. The walk resumes from a KV-stored cursor so
 * each request reads at most WALK_BUDGET_PER_REQUEST commits, however deep the requested page.
 */
export async function pathHistory(
  store: GitStore,
  headSha: string,
  path: string,
  page: number,
): Promise<PathHistoryPage> {
  const key = `path-log:v1:${store.artifactsName}:${headSha}:${await pathKey(path)}`;
  const stored = await env.KV.get<PathLogState>(key, "json");