export type Dialect = "sqlite" | "postgres" | "d1"; export interface DbExecQuery { sql: string; args?: unknown[]; /** * Client-side wall-clock budget for this statement. Use only for idempotent * reads unless the caller can safely tolerate a late write completing. */ timeoutMs?: number; /** Maximum connection-level attempts for this statement, including the first. */ maxAttempts?: number; } export type DbExecStatement = string | DbExecQuery; export interface DbExec { execute(sql: DbExecStatement): Promise<{ rows: any[]; rowsAffected: number; }>; transaction?(fn: (tx: DbExec) => Promise): Promise; atomicBatch?(statements: readonly DbExecStatement[]): Promise>; /** * Release the underlying connection/pool held by this exec. * Only non-singleton execs created via `createDbExec()` (e.g. the migration * direct-endpoint exec) should call this. The global singleton exec (`getDbExec`) * is managed by `closeDbExec()` instead. */ close?(): Promise; } export interface DbExecConfig { url?: string; authToken?: string; d1Binding?: any; } /** Read the request-scoped Cloudflare binding without requiring every * consuming app's TypeScript program to include core's ambient Worker globals. */ export declare function getCloudflareD1Binding(): unknown; /** * Resolve the database URL for the current app. * * Checks for `_DATABASE_URL` first (e.g. `MAIL_DATABASE_URL`), * then falls back to `DATABASE_URL`, then Netlify's managed database env. This * allows multiple apps to run in the same process group (e.g. eager repo dev or * builder.io) with separate databases while still using the persistent Netlify * runtime database when `DATABASE_URL` was only exported for the build command. * * Set `APP_NAME=mail` in the child process env and * `MAIL_DATABASE_URL=postgres://...` in the shared env. */ export declare function getDatabaseUrl(fallback?: string): string; /** Same per-app resolution for DATABASE_AUTH_TOKEN (used by Turso/libsql). */ export declare function getDatabaseAuthToken(): string | undefined; /** * Database URL to use for migrations — identical to DATABASE_URL but with the * Neon connection-pooler suffix stripped. Neon's PgBouncer runs in transaction * mode, which resets session-level ownership after each statement and causes * `ALTER TABLE … ADD COLUMN` to fail with "must be owner of table " even * when the connecting role owns it. The direct endpoint bypasses PgBouncer so * DDL honours the role's actual ownership. * * Non-Neon URLs and already-direct Neon URLs are returned unchanged. */ export declare function getMigrationDatabaseUrl(): string; export declare function isLocalSqliteUrl(url: string): boolean; export declare function isPgliteUrl(url: string): boolean; export declare function pgliteDataDirFromUrl(url: string): string; export declare function pgliteRuntimeDataDir(dataDir: string): string; export declare function loadPglitePackage(): Promise<{ PGlite: any; }>; export declare function loadPgliteDrizzle(): Promise<{ PGlite: any; drizzle: any; }>; export declare function getPgliteClient(url: string): Promise; export declare function closePgliteClients(): Promise; export declare function closePgliteClient(url: string): Promise; export declare function prepareLocalSqliteUrl(url: string): Promise; export declare function sqliteFilenameFromUrl(url: string): string; /** * Parse a JSON-serialized column value defensively. A malformed row — from a * hand-edit, dirty migration, or a misbehaving agent that wrote raw SQL — * must not break an entire list endpoint. Callers supply a fallback for the * malformed path; null/undefined values also fall back. */ export declare function safeJsonParse(value: unknown, fallback: T): T; /** * Retry an async operation when it fails with SQLITE_BUSY. * Used during WAL initialization and migrations where a stale WAL from a * previous crash or HMR restart can briefly lock the database. */ export declare function retrySqliteBusy(fn: () => Promise, opts?: { maxAttempts?: number; baseDelayMs?: number; rethrow?: boolean; }): Promise; /** * Retry a DDL statement (CREATE TABLE, CREATE INDEX) once when it fails due * to a Postgres pg_catalog race. * * Postgres's `IF NOT EXISTS` check is NOT atomic with the `pg_type` / * `pg_class` catalog insert. When multiple processes boot concurrently and * issue the same CREATE, both can pass the existence check and one fails * with code 23505 on `pg_type_typname_nsp_index`, 42710 from `TypeCreate`, * or similar. The table does end up created by the winner, so rerunning the * same `IF NOT EXISTS` statement is a safe no-op. */ export declare function retryOnDdlRace(fn: () => Promise): Promise; /** * True when `e` is a UNIQUE / PRIMARY KEY constraint violation from any * supported driver (Postgres 23505, SQLite SQLITE_CONSTRAINT_PRIMARYKEY / * _UNIQUE, D1). Used by stores that accept caller-provided ids and want to * surface a clean "already exists" error instead of the raw SQL text. */ export declare function isUniqueViolation(e: any): boolean; export declare function getDialect(): Dialect; export declare function isPostgres(): boolean; /** * Returns true when the database is a local-only SQLite file (or unset, which * defaults to a local SQLite file). Returns false for Postgres, remote libsql * (Turso), and D1 — any backend that could be shared across developers. * * Used to gate local@localhost mode: that mode uses a single shared virtual * user with no per-machine scoping, so on any shared database two developers * would read and write each other's settings, oauth tokens, and app state. */ export declare function isLocalDatabase(): boolean; /** Returns BIGINT for Postgres (64-bit), INTEGER for SQLite (already 64-bit). */ export declare function intType(): string; export declare function sqliteToPostgresParams(sql: string): string; export declare function dbExecQueryBudget(statement: DbExecStatement): { timeoutMs: number; maxAttempts: number; }; export declare function isConnectionError(err: any): boolean; /** * Classify database failures that should temporarily shed request load. * Statement timeouts are not included in isConnectionError() because retrying * every timed-out mutation would not be safe, but request handlers can still * return a retryable service-unavailable response for them. */ export declare function isTransientDatabaseError(err: unknown): boolean; export declare function retryOnConnectionError(fn: () => Promise, maxAttempts?: number): Promise; /** * Max wall time for a single DB op (init or query) before we treat it as a * dead connection. A frozen→thawed serverless instance can leave the Neon * WebSocket (or a postgres.js socket) hung mid-flight: the promise neither * settles nor errors, so retryOnConnectionError() — which only retries thrown * errors — can't help and the request hangs until the platform kills the * function (~30s on Netlify). For authenticated requests that run a session * lookup on every navigation this surfaces as "the site won't load". Bounding * each op well under the platform function limit turns the silent hang into a * CONNECT_TIMEOUT that the existing retry and reject-reset paths already * handle. Override with DB_OP_TIMEOUT_MS. */ export declare function dbOpTimeoutMs(): number; /** * Race a DB op against {@link dbOpTimeoutMs}. Callers that own a cancellable * query or pooled client should pass onTimeout so the losing operation does * not keep occupying a scarce connection slot after the request has recovered. */ export declare function withDbTimeout(op: string, run: () => Promise, ms?: number, onTimeout?: () => void | Promise): Promise; /** * True on serverless function runtimes (Netlify / Vercel / AWS Lambda / * Cloudflare Pages Functions) where every concurrent request can spin up its * own frozen process. Connections cannot be shared across instances, so each * instance must keep its pool tiny — otherwise dozens of warm instances each * holding postgres.js's default 10-connection pool blow past Neon/Postgres' * connection cap and every `/_agent-native/*` route 500s with "Max client * connections reached". */ export declare function isServerlessRuntime(): boolean; /** * postgres.js pool options tuned per runtime. A serverless instance handles * one request at a time, so a single connection is enough. Keeping the * foreground pool at 1 is important because each instance can also open an * app and Better Auth pool; a max of 2 on each pool still exhausted Neon * under a burst of warm instances. idle_timeout is shortened on * serverless so a thawed-but-idle instance releases its connections quickly. * Long-lived Node servers keep the normal pool for throughput. */ export declare function pgPoolOptions(url: string): Record; /** * Connection cap for the @neondatabase/serverless `Pool`. Same instance * accumulation risk as postgres.js — one connection is enough for a * foreground serverless invocation and keeps the aggregate app/framework/auth * budget bounded. */ export declare function neonPoolMax(): number; /** * Inline mirror of `isInBackgroundFunctionRuntime()` * (agent/durable-background.ts), replicated here to avoid a `db` → `agent` * import cycle. Keep the signals in sync with that function. */ export declare function isBackgroundFunctionPoolContext(): boolean; /** * Render any rejection reason as a readable message. The Neon serverless * driver surfaces WebSocket failures as raw DOM-style ErrorEvent objects (not * Error instances), which stringify uselessly as "[object ErrorEvent]" — pull * the message off the event (or its nested `.error`) instead so logs carry * actual context. */ export declare function describeDbError(err: unknown): string; export declare function attachNeonPoolErrorLogger(pool: unknown, label?: string): void; export declare function createDbExec(config?: DbExecConfig): Promise; /** * Get the singleton database client. Returns a `DbExec` whose first * `execute()` call lazily initializes the underlying driver. */ export declare function getDbExec(): DbExec; /** Close the database connection (for scripts that need cleanup). */ export declare function closeDbExec(): Promise; //# sourceMappingURL=client.d.ts.map