import { accountDb } from "./scoped-db";
import { forgetLocator, forgetScope } from "./scoped-db/locators";
import { dropAccountState } from "./account-scope";
/**
* Removes the repository-scoped runners and pending runner registrations that `ownerId` holds for `repoId`. Run on
* deletion and on transfer: a runner registered under the old owner must not keep claiming the repository's jobs
* (and receiving the new owner's secrets) where the new owner can neither see nor revoke it.
*/
export async function dropRepoRunners(ownerId: string, repoId: string): Promise<void> {
const owner = accountDb(ownerId);
const registrations = await owner.all<{ token_hash: string }>(
"DELETE FROM runner_registration_tokens WHERE repo_id = ? RETURNING token_hash",
repoId,
);
const runners = await owner.all<{ id: string; token_hash: string; previous_token_hash: string | null }>(
"DELETE FROM runners WHERE repo_id = ? RETURNING id, token_hash, previous_token_hash",
repoId,
);
await Promise.all([
...registrations.map((row) => forgetLocator("runner_registration", row.token_hash)),
...runners.flatMap((runner) => [
forgetLocator("runner", runner.id),
forgetLocator("runner_token", runner.token_hash),
...(runner.previous_token_hash ? [forgetLocator("runner_token", runner.previous_token_hash)] : []),
]),
]);
}
/**
* Account-side leftovers of a deleted repository: the owner's runners and runner registrations for it, the
* repository's AI key (kept in the AccountState named by the repository id), and every locator pointing into it.
* Other users' notifications and digest items about it are not chased through every account: reads filter rows
* whose repository is gone and prune them (notification-store `filterReadable`), and token grants naming a
* deleted repository id match nothing since ids are never reused.
*/
export async function purgeDeletedRepoFromAccounts(repo: { id: string; owner_id: string }): Promise<void> {
await dropRepoRunners(repo.owner_id, repo.id);
await dropAccountState(repo.id);
await forgetScope(repo.id);