import { all, batch, chunks, MAX_IN_LIST, placeholders, stmt } from "../db";
import { loadRepoById } from "../repos";
import { withStore } from "../git/store";
import { manifestKind } from "./manifests";
import type { Dependency, Ecosystem } from "./manifests";
import { loadSecuritySettings } from "./settings";
const MAX_TREE_ENTRIES = 20_000;
const MAX_MANIFESTS = 50;
const MAX_MANIFEST_BYTES = 2 * 1024 * 1024;
const MAX_DEPENDENCIES = 5000;
const DEPENDENCY_COLUMNS = 7;
const STATEMENTS_PER_BATCH = 50;
const DEPENDENCY_PAGE = 100;
const MAX_NAME_LENGTH = 214;
const decoder = new TextDecoder();
type Parsed = { path: string; ecosystem: Ecosystem; dependencies: Dependency[] };
/** Rebuilds the dependency graph for a commit from its manifests and lockfiles, within fixed budgets. */
export async function scanDependencies(repoId: string, sha: string): Promise<number> {
const repo = await loadRepoById(repoId);
if (!repo || !(await loadSecuritySettings(repo.id)).dependencyGraph) return 0;
const parsed = await withStore(repo.artifacts_name, async (store) => {
const commit = await store.commit(sha);
if (!commit) return null;
const { entries } = await store.walk(commit.treeSha, MAX_TREE_ENTRIES);
const manifests = entries
.filter((entry) => entry.type === "blob" && manifestKind(entry.path))
.slice(0, MAX_MANIFESTS);
const out: Parsed[] = [];
let total = 0;
for (const entry of manifests) {
const kind = manifestKind(entry.path);
const blob = kind ? await store.blob(entry.sha) : null;
if (!kind || !blob || blob.byteLength > MAX_MANIFEST_BYTES) continue;
const unique = new Map<string, Dependency>();
for (const dependency of kind.parse(decoder.decode(blob))) {
const key = `${dependency.name}\0${dependency.scope}`;
if (!unique.has(key) && dependency.name.length <= MAX_NAME_LENGTH) unique.set(key, dependency);