import type { Queryable } from "../db/connect.js"; import type { RoleModel, SchemaSnapshot, TableInfo, UserTableSource } from "../types.js"; export interface IntrospectOptions { /** Schemas to scan. Defaults to every non-system schema. */ schemas?: string[]; /** Explicit roles from the config file, for what inference cannot settle. */ roles?: { anonymous?: string; authenticated?: string; }; /** * The table that holds application users, from the config file. Wins over * inference; an unknown name is an error rather than a silent fallback, * because everything downstream keys off this table. */ userTable?: string; } export declare function introspect(db: Queryable, opts?: IntrospectOptions): Promise; /** * Work out which table holds the application's users. * * This used to be a four-name allowlist (`auth.users`, `public.users`, * `public.profiles`, `public.accounts`), and the allowlist was a single point * of silent failure: a `customers` or `app_users` table, or a users table in * any other schema, meant no ownership could be inferred for anything, every * table degraded to reference/unknown, and a wide-open database could still * exit 0. The name of the table is the least reliable thing about it. * * The structural signal is much stronger: the users table is the one that many * other tables point owner-shaped columns at (`user_id`, `author_id`, * `created_by`, …). Names, an email column, and credential columns are used as * corroboration, never on their own strength — except for the exact * conventional names the old allowlist accepted, which remain a fallback so a * two-table schema with nothing but `users` and `documents` still resolves. * * `auth.users` stays authoritative when present: it is Supabase's own table. * A configured name wins over everything, and a configured name that does not * exist is an error rather than a shrug. */ export declare function resolveUserTable(tables: TableInfo[], configured?: string): { id: string | null; source: UserTableSource; rationale: string; }; /** * Work out which roles to impersonate, from the database itself. * * A role qualifies only if the application actually exposes something to it — * a grant on one of its tables, or a mention in one of its policies. Names are * used to tell an anonymous role from a logged-in one, never to decide that a * role is worth testing. * * Two exclusions keep this from destroying precision. Superusers and BYPASSRLS * roles are never impersonated, and neither is a role that owns one of the * tables: any of those would sail through every policy and report a leak on * every table in the schema. Testing as too privileged a role is far worse than * testing as none. */ export declare function resolveRoles(db: Queryable, tables: TableInfo[], override?: { anonymous?: string; authenticated?: string; }): Promise;