import { waitUntil } from "cloudflare:workers";
import { all, chunks, first, placeholders } from "./db";
import { accountDb } from "./scoped-db";
import { repoReadableSql } from "./repos";
/**
* Notifications live in the recipient's AccountState; whether the recipient may still read a notification's
* repository is a D1 directory question. These helpers apply `inboxReadableSql`'s rule in code: account-level
* rows always show, repository rows only while the repository is visible to the recipient (site admins see all).
*/
export async function readableRepoIds(userId: string, repoIds: Iterable<string>): Promise<Set<string>> {
const ids = [...new Set(repoIds)];
if (ids.length === 0) return new Set();
const admin = await first<{ ok: number }>("SELECT 1 AS ok FROM users WHERE account_id = ? AND is_admin = 1", userId);
const out = new Set<string>();
if (admin) {
// Site admins read every repository that still exists.
for (const part of chunks(ids)) {
const rows = await all<{ id: string }>(
`SELECT id FROM repos WHERE id IN (${placeholders(part.length)})`,
...part,
);
for (const row of rows) out.add(row.id);
}
return out;
}
for (const part of chunks(ids)) {
const rows = await all<{ id: string }>(
`SELECT r.id FROM repos r WHERE r.id IN (${placeholders(part.length)}) AND r.hidden_at IS NULL
AND ${repoReadableSql("r", "?")}`,
...part,
...Array<string>(repoReadableSqlUserSlots).fill(userId),
);
for (const row of rows) out.add(row.id);
}
return out;
}
/** `repoReadableSql(alias, "?")` binds the user once per mention. */
const repoReadableSqlUserSlots = (repoReadableSql("r", "?").match(/\?/g) ?? []).length;