import type { Selectable } from 'kysely' import type * as Db from '../Db.js' import type * as db_Schema from '../Schema.js' /** Columns of the `early_access` table, derived from `Schema.EarlyAccessEntry`. */ export type Table = db_Schema.EarlyAccessEntry /** A stored early-access allowlist row. */ export type Record = Selectable /** * Inserts an allowlist entry (a domain or exact email, normalized lowercase). * Returns the stored record, or `undefined` when the entry already exists. * * @param db - The database. * @param input - The entry to insert. * @returns The stored record, or `undefined` when already present. */ export function add(db: Db.Db, input: add.Input): Promise { return db.kysely .insertInto('early_access') .values({ createdAt: new Date().toISOString(), createdBy: input.createdBy, entry: input.entry.trim().toLowerCase(), }) .onConflict((oc) => oc.column('entry').doNothing()) .returningAll() .executeTakeFirst() } export declare namespace add { /** Fields accepted when inserting an allowlist entry. */ type Input = { /** Admin email adding the entry, or `migration` for seeded rows. */ createdBy: string /** Domain or exact email to grant early access; normalized lowercase. */ entry: string } } /** * Lists all allowlist entries, ordered by entry. * * @param db - The database. * @returns The records. */ export function list(db: Db.Db): Promise { return db.kysely.selectFrom('early_access').selectAll().orderBy('entry', 'asc').execute() } /** * Removes an allowlist entry. * * @param db - The database. * @param entry - The entry to remove (matched lowercase). * @returns The removed record, or `undefined` when absent. */ export function remove(db: Db.Db, entry: string): Promise { return db.kysely .deleteFrom('early_access') .where('entry', '=', entry.trim().toLowerCase()) .returningAll() .executeTakeFirst() } /** * Whether an allowlist entry matches `email`: the exact email or its domain. * Two primary-key lookups via `IN`. * * @param db - The database. * @param email - The email to match (case-insensitive). * @returns Whether an entry matches. */ export async function matches(db: Db.Db, email: string): Promise { const normalized = email.trim().toLowerCase() const domain = normalized.slice(normalized.lastIndexOf('@') + 1) const row = await db.kysely .selectFrom('early_access') .select('entry') .where('entry', 'in', [normalized, domain]) .limit(1) .executeTakeFirst() return row !== undefined }