import { advertisement, artifactsSource, relayPack, RelayScreenRejected } from "../git/relay";
import type { RelaySource } from "../git/relay";
import { receivePackBody } from "../git/ref-sync-protocol";
import type { AdvertisedRef, RefCommand } from "../git/ref-sync-protocol";
import { triggerPush } from "../events/push-trigger";
import { isPolicyRefusal, screenMirrorPush, withoutHiddenRefs } from "./mirror-screen";
import type { MirrorPolicy } from "./mirror-screen";
import { pushToArtifacts } from "../git/writer";
import { hasPermission, loadRepoById, repoPermission } from "../repos";
import { loadUserById, toViewer } from "../auth";
import { badRequest } from "../errors";
import { gitAuthorization, githubApi, installationToken } from "./github-app";
import type { OutboundResult } from "./outbound";
import { parseConfig } from "./store";
import type { IntegrationRow } from "./store";
import type { DeliverInput } from "./deliver";
import type { RepoRow } from "~/lib/types";
const MAX_MIRROR_REFS = 1000;
const MAX_MIRROR_BYTES = 1024 * 1024 * 1024;
const ZERO_SHA = "0".repeat(40);
const GITHUB_REPO = /^[A-Za-z0-9-]{1,39}\/[A-Za-z0-9._-]{1,100}$/;
export type MirrorResult = { updated: number; rejected: string[]; skipped: number };
export function isGithubRepoName(value: string): boolean {
return GITHUB_REPO.test(value) && !value.includes("..");
}
/** Refs to send so `target` matches `source`; never deletes, and caps the count so one sync stays bounded. */
export function planMirror(
source: AdvertisedRef[],
target: AdvertisedRef[],
): { commands: RefCommand[]; skipped: number } {
const current = new Map(target.map((ref) => [ref.name, ref.sha]));
const changed = source.filter((ref) => current.get(ref.name) !== ref.sha);
const commands = changed.slice(0, MAX_MIRROR_REFS).map((ref) => ({
name: ref.name,
sha: ref.sha,
oldSha: current.get(ref.name) ?? ZERO_SHA,