import { AnyRelations } from 'drizzle-orm'; import { S as SupportedDialect, a as SqlParam, T as TableResolver } from './core-CVO7WYDj.cjs'; import { T as TransactionContext, e as TransactionOptions, D as DatabaseCapabilities, P as PoolStats, S as SelectOptions, I as InsertOptions, W as WhereClause, U as UpdateOptions, f as DeleteOptions, g as UpsertOptions, a as Migration, d as MigrationResult } from './migration-B41DME-y.cjs'; import { T as TableDefinition, C as CreateTableOptions, D as DropTableOptions, A as AlterTableOperation, a as AlterTableOptions } from './schema-BDn8WfSL.cjs'; import { D as DatabaseErrorKind, a as DatabaseError } from './error-um1d_3Uo.cjs'; /** * Base database adapter abstract class. * * @remarks * This abstract class provides the foundation for all dialect-specific database adapters * (PostgreSQL, MySQL, SQLite). It defines required abstract methods that must be implemented * by subclasses, while providing default implementations for CRUD operations and query building. * * Dialect adapters can override any default method to provide optimized implementations. * * @packageDocumentation */ declare abstract class DrizzleAdapter { /** * Database dialect identifier. * * @remarks * Must be set by subclass to identify the database type. */ abstract readonly dialect: SupportedDialect; /** * Establish connection to the database. * * @remarks * This method should be idempotent - calling it multiple times * should not create multiple connections. * * @throws {DatabaseError} If connection fails */ abstract connect(): Promise; /** * Close database connection and release resources. * * @remarks * This method should be idempotent - calling it multiple times * should be safe. */ abstract disconnect(): Promise; /** * Execute a raw SQL query. * * @param sql - SQL query to execute * @param params - Query parameters * @returns Array of result rows * * @throws {DatabaseError} If query execution fails */ abstract executeQuery(sql: string, params?: SqlParam[]): Promise; /** * Execute operations within a database transaction. * * @remarks * The transaction is automatically committed on success or rolled back on error. * Supports nested transactions via savepoints on databases that support them. * * @param callback - Function to execute within transaction * @param options - Transaction options * @returns Result from the callback * * @throws {DatabaseError} If transaction fails */ abstract transaction(callback: (ctx: TransactionContext) => Promise, options?: TransactionOptions): Promise; /** * Get database capabilities. * * @remarks * Returns static capability flags for this adapter's dialect. * Used by services to conditionally enable features or implement fallbacks. * * @returns Database capability flags */ abstract getCapabilities(): DatabaseCapabilities; /** * Get the raw Drizzle ORM instance for direct queries. * * @remarks * This method provides escape hatch access to the raw Drizzle instance. * Use this when you need to run complex queries that the adapter API * doesn't support, or for legacy code migration. * * **Note:** Prefer using adapter methods when possible as they provide: * - Database-agnostic API * - Consistent error handling * - Proper connection pooling * * @param relations - Optional drizzle v1 relations config (defineRelations * output) that enables the typed relational query API on the instance * @returns Raw Drizzle ORM database instance * * @example * ```typescript * // For legacy code that needs direct Drizzle access * const db = adapter.getDrizzle(myRelations); // defineRelations output * const result = await db.insert(users).values({ ... }).returning(); * ``` */ abstract getDrizzle(relations?: AnyRelations): T; /** * Table resolver for looking up Drizzle table objects by name. * When set, CRUD methods use Drizzle's query API instead of raw SQL. * Set via setTableResolver() after boot-time schema loading. */ protected tableResolver: TableResolver | null; /** * Set the table resolver for Drizzle query API support. * When a resolver is set, CRUD methods (select, insert, update, delete, upsert) * will use Drizzle's query API (db.select().from(), etc.) instead of raw SQL * string building. Falls back to raw SQL if the resolver doesn't have the table. * * @param resolver - TableResolver implementation (e.g. SchemaRegistry) */ setTableResolver(resolver: TableResolver): void; /** * Get a Drizzle table object by name from the resolver. * Returns null if no resolver is set or table is not found. */ protected getTableObject(tableName: string): unknown; /** * Map data keys from SQL column names (snake_case) to Drizzle JS property names (camelCase). * Drizzle schemas define columns as e.g. `createdAt: timestamp("created_at")` — the JS * property is `createdAt` but the SQL column is `created_at`. Services pass snake_case keys * because they match the DB column names. This method maps them to the JS names Drizzle expects. */ protected mapDataToColumnNames(tableObj: unknown, data: Record): Record; /** * Map data keys from Drizzle JS property names to SQL column names for the * raw-SQL transaction insert path. The transaction context builds INSERT * statements from Object.keys(data) used directly as column identifiers, so a * table whose Drizzle property names differ from its SQL column names * (camelCase core tables like nextly_versions) needs its keys translated * first. For tables whose property names already equal their column names * (the dynamic dc_/single_/comp_ tables) every lookup is identity, so * existing callers are unaffected. */ protected mapKeysToSqlColumns(tableObj: unknown, data: Record): Record; /** * Map a list of column identifiers (Drizzle property names) to their SQL * column names, for the raw-SQL transaction insert paths that build a * RETURNING clause from `options.returning`. Same identity behavior as * `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic * dc_/single_/comp_ tables) pass through unchanged. */ protected mapColumnNamesToSql(tableObj: unknown, names: string[]): string[]; /** * Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property * names, so the raw-SQL transaction insert paths return the same key casing * as the non-transactional (Drizzle) insert. Keys only - values are left * untouched, so this does not change how JSON/date columns are decoded. For * tables whose property names already equal their SQL columns (the dynamic * dc_/single_/comp_ tables) every lookup is identity, so existing callers see * no change. */ protected mapRowKeysToJs(tableObj: unknown, row: T): T; /** * Build a Drizzle column projection object (`{ propertyName: column }`) from a * requested column list, used by `select` (columns) and `insert` (returning). * A requested name resolves against either the Drizzle property name * (camelCase) or the SQL column name (snake_case); the projection is keyed by * the property name so the row shape matches a full select. Returns undefined * for `"*"` or when nothing resolves, so callers fall back to all columns. */ protected buildColumnProjection(tableObj: unknown, names: string[] | "*" | undefined): Record | undefined; /** * Check if the adapter is currently connected. * * @remarks * Default implementation returns false. Subclasses should override * to provide accurate connection status. * * @returns True if connected, false otherwise */ isConnected(): boolean; /** * Get connection pool statistics. * * @remarks * Returns null by default. Subclasses with connection pooling * should override to provide pool statistics. * * @returns Pool statistics or null if not applicable */ getPoolStats(): PoolStats | null; /** * Default query timeout in milliseconds. * * @remarks * This value is used by executeWithTimeout() when no explicit timeout * is provided. Subclasses should set this from their config. * * @default 15000 (15 seconds) * * @protected */ protected defaultQueryTimeoutMs: number; /** * Execute an async operation with a timeout. * * @remarks * Wraps an async operation with a timeout that aborts if the operation * exceeds the specified duration. Uses Promise.race for clean timeout * handling without memory leaks. * * When the timeout is reached, a DatabaseError with kind 'timeout' is thrown. * Note that this does NOT cancel the underlying database query - it only * prevents the calling code from waiting indefinitely. For true query * cancellation, use database-level statement timeouts (PostgreSQL) or * similar mechanisms. * * @param operation - Async operation to execute * @param timeoutMs - Timeout in milliseconds (defaults to defaultQueryTimeoutMs) * @returns Result of the operation * * @throws {DatabaseError} With kind 'timeout' if operation exceeds timeout * * @example * ```typescript * // Use default timeout * const result = await adapter.executeWithTimeout( * () => adapter.select('users', { limit: 1000 }) * ); * * // Use custom timeout for specific operation * const result = await adapter.executeWithTimeout( * () => adapter.select('large_table'), * 60000 // 60 seconds for large queries * ); * ``` * * @public */ executeWithTimeout(operation: () => Promise, timeoutMs?: number): Promise; /** * Set the default query timeout. * * @remarks * This method allows runtime configuration of the default timeout. * Subclasses should call this in their constructor or connect() method * based on their configuration. * * @param timeoutMs - Timeout in milliseconds (0 to disable) * * @protected */ protected setDefaultQueryTimeout(timeoutMs: number): void; /** * Get the current default query timeout. * * @returns Current default timeout in milliseconds * * @public */ getDefaultQueryTimeout(): number; /** * Select multiple records from a table. * * @remarks * Default implementation builds a SELECT query and executes it. * Subclasses can override for optimization or dialect-specific features. * * @param table - Table name * @param options - Select options (filtering, sorting, pagination) * @returns Array of matching records * * @throws {DatabaseError} If query fails * * @example * ```typescript * const users = await adapter.select('users', { * where: { and: [{ column: 'role', op: '=', value: 'admin' }] }, * orderBy: [{ column: 'created_at', direction: 'desc' }], * limit: 10 * }); * ``` */ select(table: string, options?: SelectOptions, executor?: unknown): Promise; /** * Select a single record from a table. * * @remarks * Default implementation uses `select()` with limit 1 and returns first result. * Returns null if no matching record is found. * * @param table - Table name * @param options - Select options * @returns First matching record or null * * @throws {DatabaseError} If query fails * * @example * ```typescript * const user = await adapter.selectOne('users', { * where: { and: [{ column: 'email', op: '=', value: 'user@example.com' }] } * }); * ``` */ selectOne(table: string, options?: SelectOptions, executor?: unknown): Promise; /** * Insert a single record into a table. * * @remarks * Default implementation handles databases with and without RETURNING support. * For databases without RETURNING (MySQL), performs INSERT followed by SELECT. * * @param table - Table name * @param data - Record data to insert * @param options - Insert options * @returns Inserted record (with RETURNING columns if specified) * * @throws {DatabaseError} If insert fails * * @example * ```typescript * const user = await adapter.insert('users', { * email: 'user@example.com', * name: 'John Doe' * }, { returning: ['id', 'email', 'created_at'] }); * ``` */ insert(table: string, data: Record, options?: InsertOptions): Promise; /** * Insert multiple records into a table. * * @remarks * Default implementation performs individual inserts in sequence. * Subclasses can override for bulk insert optimization (e.g., COPY in PostgreSQL). * * @param table - Table name * @param data - Array of records to insert * @param options - Insert options * @returns Inserted records (with RETURNING columns if specified) * * @throws {DatabaseError} If insert fails * * @example * ```typescript * const users = await adapter.insertMany('users', [ * { email: 'user1@example.com', name: 'User 1' }, * { email: 'user2@example.com', name: 'User 2' } * ], { returning: ['id'] }); * ``` */ insertMany(table: string, data: Record[], options?: InsertOptions): Promise; /** * Update records in a table. * * @remarks * Default implementation builds an UPDATE query with WHERE clause. * Returns updated records if RETURNING is supported and requested. * * @param table - Table name * @param data - Data to update * @param where - Conditions for records to update * @param options - Update options * @returns Updated records (with RETURNING columns if specified) * * @throws {DatabaseError} If update fails * * @example * ```typescript * const updated = await adapter.update('users', * { status: 'active' }, * { and: [{ column: 'id', op: '=', value: userId }] }, * { returning: ['id', 'status', 'updated_at'] } * ); * ``` */ update(table: string, data: Record, where: WhereClause, options?: UpdateOptions, executor?: unknown): Promise; /** * Delete records from a table. * * @remarks * Default implementation builds a DELETE query with WHERE clause. * Returns the number of deleted records. * * @param table - Table name * @param where - Conditions for records to delete * @param options - Delete options * @returns Number of deleted records * * @throws {DatabaseError} If delete fails * * @example * ```typescript * const count = await adapter.delete('users', { * and: [{ column: 'status', op: '=', value: 'inactive' }] * }); * console.log(`Deleted ${count} users`); * ``` */ delete(table: string, where: WhereClause, _options?: DeleteOptions, executor?: unknown): Promise; /** * Upsert (INSERT or UPDATE) a record. * * @remarks * Default implementation uses dialect-specific ON CONFLICT syntax. * PostgreSQL/SQLite: ON CONFLICT ... DO UPDATE * MySQL: ON DUPLICATE KEY UPDATE * * @param table - Table name * @param data - Record data * @param options - Upsert options (must specify conflict columns) * @returns Upserted record (with RETURNING columns if specified) * * @throws {DatabaseError} If upsert fails * * @example * ```typescript * const user = await adapter.upsert('users', { * email: 'user@example.com', * name: 'Updated Name' * }, { * conflictColumns: ['email'], * updateColumns: ['name'], * returning: ['id', 'email', 'name'] * }); * ``` */ upsert(table: string, data: Record, options: UpsertOptions, executor?: unknown): Promise; /** * Run pending migrations. * * @remarks * Default implementation is a placeholder. Subclasses should implement * migration tracking and execution logic. * * @param migrations - Array of migrations to run * @returns Migration result with applied and pending migrations * * @throws {DatabaseError} If migration fails */ migrate(_migrations: Migration[]): Promise; /** * Rollback the last migration. * * @remarks * Default implementation is a placeholder. Subclasses should implement * migration rollback logic. * * @returns Migration result after rollback * * @throws {DatabaseError} If rollback fails */ rollback(): Promise; /** * Get migration status. * * @remarks * Default implementation is a placeholder. Subclasses should implement * migration status checking. * * @returns Current migration status * * @throws {DatabaseError} If status check fails */ getMigrationStatus(): Promise; /** * Create a new table. * * @remarks * Default implementation is a placeholder. Subclasses should implement * table creation logic. * * @param definition - Table definition * @param options - Creation options * * @throws {DatabaseError} If table creation fails */ createTable(definition: TableDefinition, options?: CreateTableOptions): Promise; /** * Drop a table. * * @remarks * Default implementation is a placeholder. Subclasses should implement * table dropping logic. * * @param tableName - Name of table to drop * @param options - Drop options * * @throws {DatabaseError} If table drop fails */ dropTable(tableName: string, options?: DropTableOptions): Promise; /** * Alter an existing table. * * @remarks * Default implementation is a placeholder. Subclasses should implement * table alteration logic. * * @param tableName - Name of table to alter * @param operations - Alteration operations * @param options - Alter options * * @throws {DatabaseError} If table alteration fails */ alterTable(tableName: string, operations: AlterTableOperation[], _options?: AlterTableOptions): Promise; /** * Check if a table exists in the database. * * @remarks * Uses dialect-specific information schema queries to check table existence. * This is useful for development mode auto-sync to determine whether to * CREATE or DROP/CREATE tables. * * @param tableName - Name of table to check * @param schema - Optional schema name (defaults to 'public' for PostgreSQL) * @returns True if table exists, false otherwise * * @throws {DatabaseError} If query fails * * @example * ```typescript * const exists = await adapter.tableExists('users'); * if (exists) { * await adapter.dropTable('users'); * } * await adapter.createTable(userTableDef); * ``` */ tableExists(tableName: string, schema?: string): Promise; /** * Get list of all tables in the database. * * @remarks * Uses dialect-specific information schema queries to list tables. * Useful for detecting orphaned tables or validating schema state. * * @param schema - Optional schema name (defaults to 'public' for PostgreSQL) * @returns Array of table names * * @throws {DatabaseError} If query fails */ listTables(schema?: string): Promise; /** * Escape a table or column identifier. * * @remarks * Default uses double quotes (SQL standard). * MySQL adapter should override to use backticks. * * @param identifier - Identifier to escape * @returns Escaped identifier * * @protected */ protected escapeIdentifier(identifier: string): string; /** * Create a DatabaseError with proper error kind classification. * * @remarks * Protected helper for subclasses to create consistent errors. * Subclasses can override to add dialect-specific error classification. * * @param kind - Error kind * @param message - Error message * @param cause - Original error * @returns DatabaseError instance * * @protected */ protected createDatabaseError(kind: DatabaseErrorKind, message: string, cause?: Error): DatabaseError; /** * Handle query errors and convert to DatabaseError. * * @remarks * Protected helper for consistent error handling across CRUD operations. * Subclasses can override to add dialect-specific error classification. * * @param error - Original error * @param operation - Operation that failed * @param table - Table name * @returns DatabaseError instance * * @protected */ protected handleQueryError(error: unknown, operation: string, table: string): DatabaseError; } export { DrizzleAdapter as D };