export interface IdentityRekeyDb { unsafe(sql: string, args?: unknown[]): Promise> & { count?: number; }>; transaction?(fn: (tx: IdentityRekeyDb) => Promise): Promise; } export type IdentityColumn = { table: string; column: string; mode?: "email" | "user-scope" | "secret-scope" | "user-share" | "group-json" | "state-session" | "email-user-id" | "viewer-consent" | "owner" | "typed-scope" | "custom-scope" | "scope-key" | "unsupported-oauth"; /** Email-change behavior. Omitted means rewrite the value to the new address. */ emailChange?: IdentityEmailChange; /** The other key columns. An email change refuses when the new address already holds the same key. */ uniqueWith?: readonly string[]; /** Offboarding behavior. Omitted means offboard.ts derives it from `mode`. */ offboard?: IdentityOffboard; /** How an organization-scoped offboard finds this organization's rows. Omitted means an `org_id` column. */ orgScope?: IdentityOrgScope; }; export type IdentityEmailChange = "rekey" | "delete" | "retain"; /** * `revoke` sets `revoked_at` (epoch milliseconds) on rows not yet revoked. Use * it for credential registries whose readers treat a missing row as "not * revoked", where deleting the row would re-enable the credential. */ export type IdentityOffboard = "transfer" | "delete" | "revoke" | "retain"; export type IdentityOrgScope = { column: string; } | { column: string; references: { table: string; column: string; orgColumn: string; }; }; /** * An app-owned column that matches the identity-column naming pattern. Every * such column must be declared, including ones that are not member * identities, so a new column cannot silently escape email change or * offboarding. */ export type AppIdentityColumn = { table: string; column: string; /** What an email change does to rows holding the old address. */ emailChange: IdentityEmailChange; /** What removing the member does to rows holding their address. */ offboard: IdentityOffboard; /** `secret-scope` values are scoped by a sibling `secret_scope` column, as in `integration_installations`. */ mode?: "email" | "secret-scope"; orgScope?: IdentityOrgScope; /** Why this policy is right for the data. Required so every declaration is a visible decision. */ reason: string; }; /** * Framework identity columns. `owner_email` rows transfer to the successor on * offboarding unless the entry says otherwise. Credentials say `delete` or * `revoke`, because a transferred credential lets the removed member act as * the successor. rekey.spec.ts pins that list. */ export declare const IDENTITY_REKEY_COLUMNS: readonly IdentityColumn[]; export declare const IDENTITY_REKEY_IGNORED_COLUMNS: Set; /** * Declare how an app's own identity-shaped columns behave on email change and * member offboarding. Call it at module load from the app's server graph * (beside `registerShareableResource`). Share tables made by * `createSharesTable()` are recognized from their shape and need no entry. */ export declare function registerIdentityColumns(columns: readonly AppIdentityColumn[]): void; /** Test-only reset for the app declaration registry. */ export declare function __resetAppIdentityColumnsForTests(): void; /** * The complete identity policy for one database schema: the framework * registry, app declarations, and share tables recognized by the * `createSharesTable()` shape. Throws on the first identity-shaped column * with no policy, so callers never run an incomplete migration. */ export declare function resolveIdentityColumns(rows: readonly Record[]): IdentityColumn[]; /** * The Better Auth session column holding the user id. Migrated databases use * `user_id`; older fixtures and hand-built schemas use `"userId"`. Null means * there is no session table; a session table without either column throws, * because skipping revocation would leave the old identity signed in. */ export declare function sessionUserColumn(sessionColumns: ReadonlySet): "user_id" | "userId" | null; export declare function assertIdentityColumnRows(rows: readonly Record[]): IdentityColumn[]; export declare function assertIdentityColumnsRegistered(db: IdentityRekeyDb): Promise; export interface IdentityRekeyResult { counts: Record; sessionCount: number; oauthRevokedCount: number; } export declare function __resetIdleIdentityRekeyProbeCacheForTests(): void; export type IdentityRekeyLedgerStatus = "pending" | "done"; export interface IdentityRekeyLedgerRow { id: string; oldEmail: string; newEmail: string; status: IdentityRekeyLedgerStatus; error: string | null; } export declare function listPendingIdentityRekeys(db: IdentityRekeyDb): Promise; export declare function ensureIdentityRekeyLedger(db: IdentityRekeyDb): Promise; export declare function beginIdentityRekey(db: IdentityRekeyDb, oldEmail: string, newEmail: string, actorEmail?: string | null): Promise; export declare function finishIdentityRekey(db: IdentityRekeyDb, ledgerId: string, result: IdentityRekeyResult): Promise; export declare function failIdentityRekey(db: IdentityRekeyDb, ledgerId: string, error: unknown): Promise; export declare function executeIdentityRekey(db: IdentityRekeyDb, oldEmail: string, newEmail: string, options?: Parameters[3]): Promise; export declare function resumePendingIdentityRekeys(db: IdentityRekeyDb, email: string, options?: { ensureLedger?: boolean; cacheIdle?: boolean; }): Promise; export interface VerifiedEmailChange { oldEmail: string; newEmail: string; } export declare function verifiedEmailChangeFromToken(token: string, secret: string, verifiedEmail: string): Promise; export declare function rekeyIdentityAfterEmailVerification(db: IdentityRekeyDb, args: { token: string; secret: string; verifiedEmail: string; actorEmail?: string | null; }): Promise; /** Run inside the caller's PostgreSQL transaction. It deliberately refuses credential and derived-key stores it cannot safely rewrite. */ export declare function rekeyIdentity(db: IdentityRekeyDb, oldEmailInput: string, newEmailInput: string, options?: { dryRun?: boolean; actorEmail?: string | null; caller?: "cli" | "email-verification"; accountAlreadyUpdated?: boolean; revokeSessions?: boolean; }): Promise; export declare const identityRekeySupportedTables: Set;