import type { Selectable } from 'kysely' import * as Id from '../../internal/Id.js' import type * as Db from '../Db.js' import type * as db_Schema from '../Schema.js' /** Columns of the `users` table, derived from `Schema.User`. */ export type Table = db_Schema.User /** A stored user row. */ export type Record = Selectable /** * Reads a user by id (`usr_…`). * * @param db - The database. * @param id - The user id. * @returns The record, or `undefined` when absent. */ export function get(db: Db.Db, id: string): Promise { return db.kysely.selectFrom('users').selectAll().where('id', '=', id).executeTakeFirst() } /** * Reads users by id for operational identity displays. * * @param db - The database. * @param ids - User ids to read. * @returns Matching user records. */ export function listByIds(db: Db.Db, ids: readonly string[]): Promise { if (ids.length === 0) return Promise.resolve([]) return db.kysely .selectFrom('users') .selectAll() .where('id', 'in', [...ids]) .execute() } /** * Reads a user by wallet address (compared lowercase). * * @param db - The database. * @param address - The wallet address. * @returns The record, or `undefined` when absent. */ export function getByAddress(db: Db.Db, address: string): Promise { return db.kysely .selectFrom('users') .selectAll() .where('address', '=', address.toLowerCase()) .executeTakeFirst() } /** * Inserts a user for `address` when absent and returns the stored record. * Existing users are returned unchanged, so repeated sign-ins are idempotent. * * @param db - The database. * @param input - The user to ensure. * @returns The stored record. */ export async function upsertByAddress(db: Db.Db, input: upsertByAddress.Input): Promise { const address = input.address.toLowerCase() const now = new Date().toISOString() const inserted = await db.kysely .insertInto('users') .values({ address, createdAt: now, email: null, id: Id.generate('usr'), updatedAt: now }) .onConflict((oc) => oc.column('address').doNothing()) .returningAll() .executeTakeFirst() if (inserted) return inserted return db.kysely .selectFrom('users') .selectAll() .where('address', '=', address) .executeTakeFirstOrThrow() } export declare namespace upsertByAddress { /** Fields accepted when ensuring a user. */ type Input = { /** Wallet address that signed in (stored lowercase). */ address: string } } /** * Sets a user's verified email, bumping `updatedAt`. * * @param db - The database. * @param id - The user id (`usr_…`). * @param email - The verified email. */ export async function setEmail(db: Db.Db, id: string, email: string): Promise { await db.kysely .updateTable('users') .set({ email, updatedAt: new Date().toISOString() }) .where('id', '=', id) .execute() }