import { Pool } from 'pg'; /** * Strip SSL-related parameters from DATABASE_URL to prevent pg-connection-string * from overriding our explicit ssl config. * * pg v8+ treats sslmode=require as verify-full internally, which breaks connections * to providers like Supabase that use self-signed certs in the chain. * By removing sslmode from the connection string and handling it via the explicit * `ssl` Pool option, we maintain full control over SSL behavior. */ export declare function stripSSLParams(databaseUrl: string): string; /** * Parse SSL mode from DATABASE_URL using proper URL parameter parsing * Supports: disable, allow, prefer, require, verify-ca, verify-full * * Security behavior: * - Production: Defaults to rejectUnauthorized: true (validates certificates) * - Development: Defaults to no SSL (localhost doesn't need it) * - Explicit sslmode in URL always takes precedence */ export declare function parseSSLConfig(databaseUrl: string): false | { rejectUnauthorized: boolean; }; declare const pool: Pool; declare const servicePool: Pool; /** * Validate userId format to prevent SQL injection in SET LOCAL commands * * Security behavior: * - Production: Requires valid UUID format (RFC 4122 compliant) * - Development: Allows test IDs but validates against SQL injection * * Valid test ID patterns (development only): * - Alphanumeric characters: a-z, A-Z, 0-9 * - Hyphens and underscores: -, _ * - Examples: "test-superadmin-001", "dev_user_123" * * Blocked in ALL environments: * - SQL injection characters: ' " \ ; * - Control characters: 0x00-0x1F * - Values exceeding 255 characters * * @security SEC-003 - SQL injection prevention in SET LOCAL commands * @internal Exported for testing only */ export declare function validateUserId(userId: string): void; export interface RLSOptions { /** Force the service (RLS-bypass) pool even if a userId is provided. */ service?: boolean; } /** * Execute a query with RLS context * Sets the current user ID for Row Level Security policies via app.user_id * * @param query - SQL query string * @param params - Query parameters * @param userId - User ID for RLS context (optional - if not provided, runs without RLS context) * @returns Query result rows */ export declare function queryWithRLS(query: string, params?: unknown[], userId?: string | null, options?: RLSOptions): Promise; /** * Execute a single query and return the first row * Useful for queries that return a single result */ export declare function queryOneWithRLS(query: string, params?: unknown[], userId?: string | null, options?: RLSOptions): Promise; /** * Execute a mutation (INSERT, UPDATE, DELETE) with RLS context * Returns the affected rows */ export declare function mutateWithRLS(query: string, params?: unknown[], userId?: string | null, options?: RLSOptions): Promise<{ rows: T[]; rowCount: number; }>; /** * Get a transaction client for multiple operations * Useful when you need to run multiple queries in the same RLS context * * @example * const tx = await getTransactionClient(userId); * try { * await tx.query('INSERT INTO ...'); * await tx.query('UPDATE ...'); * await tx.commit(); * } catch (error) { * await tx.rollback(); * throw error; * } */ export declare function getTransactionClient(userId?: string | null, options?: RLSOptions): Promise<{ query: (query: string, params?: unknown[]) => Promise; queryOne: (query: string, params?: unknown[]) => Promise; mutate: (query: string, params?: unknown[]) => Promise<{ rows: T[]; rowCount: number; }>; commit: () => Promise; rollback: () => Promise; }>; /** * Get a transaction client on the SERVICE (RLS-bypass) pool. * * Use for privileged system bootstraps that carry a userId but must bypass RLS * because they create the FIRST membership/subscription of a brand-new team * (which no membership-based policy can satisfy). Authorization for these * operations is enforced at the API/action layer, not by RLS. */ export declare function getServiceTransactionClient(): Promise<{ query: (query: string, params?: unknown[]) => Promise; queryOne: (query: string, params?: unknown[]) => Promise; mutate: (query: string, params?: unknown[]) => Promise<{ rows: T[]; rowCount: number; }>; commit: () => Promise; rollback: () => Promise; }>; /** * Execute a direct query without RLS context * Use this for Better Auth tables (user, session, account, verification) * * @param text - SQL query string * @param params - Query parameters * @returns Query result */ export declare function query(text: string, params?: unknown[]): Promise<{ rows: T[]; rowCount: number; }>; /** * Execute a direct query and return only the rows * Convenience function for SELECT queries */ export declare function queryRows(text: string, params?: unknown[]): Promise; /** * Execute a direct query and return the first row * Useful for queries that return a single result */ export declare function queryOne(text: string, params?: unknown[]): Promise; export { pool, servicePool }; /** * Get the shared database pool * Use this instead of creating new Pool instances * * @throws Error if the pool is shutting down or has been closed */ export declare function getPool(): Pool; /** * Check if the pool is healthy and accepting connections * * Checks: * - Pool is not shutting down * - Pool has available capacity (not at max with all busy) * - No excessive waiting queue */ export declare function isPoolHealthy(): boolean; /** * Get pool statistics for monitoring */ export declare function getPoolStats(): { total: number; idle: number; waiting: number; activeConnections: number; isShuttingDown: boolean; }; /** * Gracefully shutdown the database pool * Waits for all active connections to complete before closing * * @param timeoutMs Maximum time to wait for connections to finish (default: 30s) */ export declare function gracefulShutdown(timeoutMs?: number): Promise; export declare function checkDatabaseConnection(): Promise; //# sourceMappingURL=db.d.ts.map