import { DurableObject } from "cloudflare:workers";
import { batch, first, stmt as d1Stmt } from "./db";
import { backoffMs, ISOLATE_AFTER } from "./do-flush";
import { accountDb, repoDb, stmt, type ScopedDb, type ScopedStatement } from "./scoped-db";
import { locate } from "./scoped-db/locators";
import { afterHostingFlush } from "./sites/caps";
import { REPO_PACK_BYTES, REPO_STARS } from "./counter-keys";
import { forwardFlushed } from "./counters-forward";
/** How long increments sit in the object before D1 sees them; reads that must be exact go to the object. */
const FLUSH_DELAY_MS = 10_000;
const RETRY_DELAY_MS = 60_000;
const FLUSH_BATCH = 200;
/** Rows untouched this long are dropped once flushed, so an object only holds its working set. */
const IDLE_ROW_MS = 3 * 24 * 60 * 60 * 1000;
export type CounterKind =
| "site-usage"
| "release-downloads"
| "mcp-usage"
| "repo-counts"
| "platform-ai"
| "platform-sites";
export type Totals = [number, number, number];
type Row = { key: string; a: number; b: number; c: number; meta: string | null };
/**
* Pending writes of one flush, grouped by store: D1 directory statements (star counts) and statements for one
* repository's or account's object. Each group is its own transaction.
*/
type Writes = { d1: D1PreparedStatement[]; scoped: Array<{ db: ScopedDb; statements: ScopedStatement[] }> };
async function count(read: Promise<{ value: number } | null>): Promise<number> {
return (await read)?.value ?? 0;
}
async function repoExists(repoId: string): Promise<boolean> {
return (await first<{ ok: number }>("SELECT 1 AS ok FROM repos WHERE id = ?", repoId)) !== null;
}