import { env, waitUntil } from "cloudflare:workers";
import { all, chunks, MAX_IN_LIST, placeholders } from "./db";
import { withStore } from "./git/store";
import type { GitStore } from "./git/store";
import { diffTreesBounded } from "./git/diff";
import { compareCommits } from "./git/history";
import { globToRegExp } from "~/lib/glob";
import type { RepoRow } from "~/lib/types";
import type { PullRow } from "./pulls";
const CODEOWNERS_PATHS = [".github/CODEOWNERS", "CODEOWNERS", "docs/CODEOWNERS"];
const MAX_CODEOWNERS_BYTES = 64 * 1024;
const MAX_RULES = 500;
const MAX_OWNED_PATHS = 300;
const MAX_REQUESTED_OWNERS = 15;
const OWNED_CACHE_TTL_SECONDS = 60 * 60 * 24 * 7;
const USER_OWNER = /^@([A-Za-z0-9](?:[A-Za-z0-9-]{0,38}))$/;
const EMAIL_OWNER = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
const EMAIL_PREFIX = "email:";
const decoder = new TextDecoder();
type Rule = { pattern: RegExp; owners: string[] };
type ParsedCodeOwners = { rules: Rule[]; incomplete: string | null };
/** Owner tokens: lowercase login, `email:<address>`, or the raw token (teams, malformed) which never resolves. */
function ownerToken(owner: string): string {
const user = USER_OWNER.exec(owner);
if (user) return user[1].toLowerCase();
if (EMAIL_OWNER.test(owner)) return `${EMAIL_PREFIX}${owner.toLowerCase()}`;
return `unresolved:${owner}`;
}
function patternToRegExp(raw: string): RegExp {
const directoryOnly = raw.endsWith("/");
let pattern = directoryOnly ? raw.slice(0, -1) : raw;
const anchored = pattern.startsWith("/") || pattern.includes("/");
pattern = pattern.replace(/^\//, "");
const body = globToRegExp(pattern).source.slice(1, -1);
const suffix = directoryOnly ? "/.*" : pattern.endsWith("/*") ? "" : "(?:/.*)?";
return new RegExp(`${anchored ? "^" : "^(?:.*/)?"}${body}${suffix}$`);