import { env } from "cloudflare:workers";
import { all, chunks } from "../db";
import { stmt } from "../scoped-db";
import { claimHost, domainSites, forgetSite, loadSiteRow, releaseHost, siteDb, siteScopesFrom } from "./store";
import { HttpError } from "../errors";
import { loadRepoById } from "../repos";
import { customDomainsEnabled, deleteCustomHostname } from "./domains";
import { customHostKey, labelUrl, metaKey, publishMeta, sitesDomain, slotKey } from "./config";
import { assertLabelFree, loadSiteById, loadSiteForRepo } from "./deploy";
import { siteLabel } from "./naming";
import { refreshSiteDomain, releaseSiteDomain } from "./site";
import { aliasLabels, dropSiteAliases } from "./pr-aliases";
import { customDomainCount, notifyDomainOwners, syncDomainAddonQuantity } from "../billing/domain-addon";
import { CUSTOM_DOMAIN_LIMIT } from "~/lib/pricing";
import type { SiteMeta, SiteRow } from "./config";
const OWNER_BATCH = 200;
const RELABEL_BLOCKED = "Site host";
const RECONCILE_BATCH = 100;
const DOMAIN_BATCH = 10;
const META_PAGE = 500;
const CURSOR_KEY = "sites:reconcile:cursor";
const META_CURSOR_KEY = "sites:reconcile:meta-cursor";
/** Unpublishes everything a site serves (meta, every label, custom host, SaaS hostname) from KV-only state. */
async function teardown(
meta: Pick<SiteMeta, "id" | "customDomain" | "customHostnameId">,
labels: string[],
): Promise<void> {
await Promise.all(labels.map((label) => env.KV.delete(slotKey(label))));
if (meta.customDomain) await env.KV.delete(customHostKey(meta.customDomain));
if (meta.customHostnameId && customDomainsEnabled()) {
await deleteCustomHostname(meta.customHostnameId).catch((error: unknown) =>
console.warn("Custom hostname cleanup failed", meta.id, error),
);
}
await env.KV.delete(metaKey(meta.id));
}
async function servedLabels(site: SiteRow): Promise<string[]> {