/** * DatabaseService - Main ORM Service Class * * This is the primary entry point for all database operations in the Ductape SDK. * It provides a unified interface for querying, writing, transactions, schema management, * migrations, and database actions across multiple database types. * * Supported databases: PostgreSQL, MySQL, MongoDB, DynamoDB, MariaDB, Cassandra */ import { IConnectionConfig, IConnectionResult, IConnectionContext, IDatabaseConfig } from './types/connection.interface'; import { IQueryOptions, IQueryResult, IRawQueryOptions, IRawQueryResult } from './types/query.interface'; import { IInsertOptions, IInsertResult, IUpdateOptions, IUpdateResult, IDeleteOptions, IDeleteResult, IUpsertOptions, IUpsertResult } from './types/write.interface'; import { ITransactionOptions, ITransaction, ITransactionCallback } from './types/transaction.interface'; import { IAggregateOptions, IAggregateResult, ICountOptions, ISumOptions, IAvgOptions, IMinMaxOptions, IGroupByOptions, IGroupByResult } from './types/aggregation.interface'; import { ICreateTableOptions, ITableDefinition, IAlterTableOperation, ITableSchema, ITableInfo, ICreateIndexOptions, IDropIndexOptions, IListIndexesOptions, IIndexInfo, IIndexStatistics, SimpleFieldType, ISimpleFieldDefinition, ISimpleSchemaDefinition, ISimpleCreateOptions, ISimpleDropOptions, ISimpleIndexOptions } from './types/schema.interface'; import { IIndexFieldDefinition, IConstraintDefinition as IMigrationConstraintDefinition, IMigration, IMigrationHistory, IMigrationStatusResult, IMigrationResult } from './types/migration.interface'; import { ISchemaOperationResult } from './schema/schema-manager'; import { IActionDefinition, IActionCreateOptions, IActionUpdateOptions, IActionExecuteOptions } from './types/action.interface'; import { BaseAdapter } from './adapters/base.adapter'; import { TriggerProcessor } from './triggers/trigger-processor'; import { TriggerEvent, TriggerTiming, TriggerActionType, ITriggerDefinition, ITriggerContext, ITriggerResult } from './types/trigger.interface'; import ProductBuilder from '../products/services/products.service'; import { IProductDatabase, IProductDatabaseTrigger } from '../types'; import type { Redis as IORedisClient } from 'ioredis'; import { IDBActionDispatchInput, IDBOperationDispatchInput, IDispatchResult } from '../types/processor.types'; /** * Configuration options for DatabaseService initialization */ export interface IDatabaseServiceConfig { /** Workspace ID */ workspace_id: string; /** Public key for authentication */ public_key: string; /** User ID */ user_id: string; /** Authentication token */ token: string; /** Environment type (staging, production, local) */ env_type: string; private_key: string; /** Optional Redis client for caching */ redis_client?: IORedisClient; /** Reuse the Feature's initialized, tier-cached product metadata client. */ preInitializedProductBuilder?: ProductBuilder; /** Default product tag when omitted from connect/query payloads */ default_product?: string; /** Default environment slug when omitted from connect/query payloads */ default_env?: string; } /** * Normalized schema snapshot for a database connection context. * Used by payload/metadata generation to get full table+column+index context in one call. */ export interface IDatabaseSchemaSnapshot { database: string; env: string; tables: ITableSchema[]; generatedAt: string; } /** * Main Database Service class * Provides unified ORM interface for all supported databases */ export declare class DatabaseService { private adapters; private connectionContexts; private currentContext; private adapterFactory; private transactionManager; private schemaManagers; private migrationEngines; private actionManager; private _privateKey; /** Service configuration */ private config; /** ProductBuilder instances cache (keyed by product tag) */ private productBuilders; private preInitializedProductBuilder?; /** LogService instance for logging operations */ private logService; /** Current product ID for logging */ private productId; /** CacheManager for two-tier caching (Redis + remote) */ private cacheManager; /** Private keys cache for products (keyed by product tag) */ private privateKeys; /** Local cache for cache configurations to avoid repeated API calls (5 minute TTL) */ private cacheConfigCache; /** In-flight connect() promises per contextKey to deduplicate concurrent connects and avoid duplicate logs */ private connectPromises; private runtimeDefaults; /** * Create a new DatabaseService instance * @param config - Optional configuration for authentication and workspace context */ constructor(config?: IDatabaseServiceConfig & { access_key: string; }); /** * Get or create a SchemaManager for the given adapter key or current context */ private getSchemaManager; /** * Get or create a MigrationEngine for the given adapter key or current context */ private getMigrationEngine; /** * Create a new ProductBuilder instance */ private createNewProductBuilder; /** * Get or create a ProductBuilder without prefetching product metadata (use bootstrap on connect). */ private getOrCreateProductBuilder; private cacheBootstrapProductContext; /** * Get or create a ProductBuilder instance for the given product tag */ private getProductBuilder; /** * Initialize logging service */ private initializeLogService; /** * Create a new ProcessorService instance for job scheduling */ private createNewProcessor; /** * Register a new database configuration for a product * @param productTag - The product tag * @param data - The database configuration data */ registerDatabase(productTag: string, data: IProductDatabase): Promise; /** * Fetch all databases for a product * @param productTag - The product tag * @returns Array of database configurations */ fetchAllDatabases(productTag: string): Promise; /** * Fetch a specific database by tag * @param productTag - The product tag * @param databaseTag - The database tag * @returns The database configuration or null if not found */ fetchDatabase(productTag: string, databaseTag: string): Promise; /** Single bootstrap API call for connect — product + database config + private_key. */ private bootstrapDatabaseForConnect; /** * Update a database configuration * @param productTag - The product tag * @param databaseTag - The database tag * @param data - The data to update */ updateDatabase(productTag: string, databaseTag: string, data: Partial): Promise; /** * Create a database trigger * @param productTag - The product tag * @param data - The trigger configuration */ createTrigger(productTag: string, data: Partial): Promise; /** * Update a database trigger * @param productTag - The product tag * @param data - The trigger configuration */ updateTrigger(productTag: string, data: Partial): Promise; /** * Fetch a database trigger * @param productTag - The product tag * @param tag - The trigger tag (format: database_tag:trigger_tag) */ fetchTrigger(productTag: string, tag: string): Promise; /** * Fetch all triggers for a database * @param productTag - The product tag * @param databaseTag - The database tag */ fetchTriggers(productTag: string, databaseTag: string): Promise; /** * Delete a database trigger * @param productTag - The product tag * @param tag - The trigger tag (format: database_tag:trigger_tag) */ deleteTrigger(productTag: string, tag: string): Promise; /** * Get the current service configuration */ getConfig(): (IDatabaseServiceConfig & { access_key: string; }) | null; /** * Validate cache tag exists in product and return cache configuration * Uses local in-memory cache to avoid repeated API calls (5 minute TTL) */ private validateCache; /** * Update the service configuration (used after auth is complete) */ updateConfig(config: Partial): void; /** * Get workspace ID from config */ getWorkspaceId(): string | null; /** * Get user ID from config */ getUserId(): string | null; /** * Get authentication token from config */ getToken(): string | null; /** * Get environment type from config */ getEnvType(): string | null; /** * Create/register a new database configuration * * @example * // Persist to product * await ductape.database.create({ * product: 'my-product', * name: 'User Database', * tag: 'users-db', * type: 'postgresql', * description: 'Stores user accounts and profiles', * envs: [ * { slug: 'dev', connection_url: 'postgresql://localhost:5432/myapp_dev' }, * { slug: 'prd', connection_url: 'postgresql://prod-host:5432/myapp_prod' }, * ], * }); * * // Local only (not persisted) * await ductape.database.create({ * name: 'Temp Database', * tag: 'temp-db', * type: 'postgresql', * envs: [{ slug: 'dev', connection_url: 'postgresql://localhost:5432/temp' }], * }); */ create(config: IDatabaseConfig): Promise; /** * Create local adapter and connection context for a database configuration. * This is a lightweight operation that only sets up local state without API calls. * Use this when the database config is already fetched/decrypted from the API. * * @param config - The database configuration (already decrypted if from API) */ private createAdapter; /** * Generate a secret key for database connection URLs * Format: DB_{PRODUCT}_{ASSET_TAG}_{ENV}_{KEY} * * Where: * - PRODUCT = productTag.split('.')[1] (second part after workspace) * - ASSET_TAG = if dbTag starts with same workspace prefix, use second part; otherwise sanitize full tag * - All parts are automatically capitalized */ private generateDbSecretKey; private resolveRuntimeProductEnv; private mergeConnectionConfig; /** * Connect to a database * * @example * const result = await ductape.database.connect({ * env: 'dev', * product: 'my-app', * database: 'users-db', * }); * console.log('Connected:', result.connected); * console.log('Database Version:', result.version); * console.log('Latency:', result.latency, 'ms'); * * // With the returned connection object, you can use scoped operations: * const db = await ductape.databases.connect({ env: 'dev', product: 'my-app', database: 'users-db' }); * await db.triggers.create({ tag: 'my-trigger', ... }); */ connect(config: IConnectionConfig): Promise; /** * Disconnect any existing connection to this resource from the SDK (shared registry and this instance) before creating a fresh one. */ private disconnectExistingForResource; /** * Get or create adapter/context, connect, and register in shared registry. * Only used when connect() is scoped with workspace_id and product (no cross-tenant sharing). */ private connectAndRegisterShared; /** * Performs the actual connect (resolve URL, adapter.connect(), log). Called once per contextKey when not already connected. */ private runConnect; /** * Test database connection without establishing persistent connection * * @example * const result = await ductape.database.testConnection({ * env: 'dev', * product: 'my-app', * database: 'users-db', * }); * if (result.connected) { * console.log('Connection successful!'); * } else { * console.error('Connection failed:', result.error); * } */ testConnection(config: IConnectionConfig): Promise; /** * Disconnect from the current database */ disconnect(): Promise; /** * Close all database connections * * @example * await ductape.database.closeAll(); */ closeAll(): Promise; /** * Fetch all registered databases * * @example * const databases = await ductape.database.fetchAll(); * databases.forEach((db) => { * console.log(`${db.name} (${db.tag}): ${db.type}`); * }); */ fetchAll(product: string): Promise; /** * Fetch a specific database configuration * * @example * const usersDb = await ductape.database.fetch('users-db'); * console.log('Database:', usersDb.name); * console.log('Type:', usersDb.type); * console.log('Environments:', usersDb.envs); */ fetch(product: string, database: string): Promise; /** * Update a local database configuration * * @example * await ductape.database.updateLocalConfig('users-db', { * name: 'User Database v2', * description: 'Updated user storage', * envs: [ * { slug: 'dev', connection_url: 'postgresql://new-dev-host:5432/myapp' }, * ], * }); */ updateLocalConfig(tag: string, updates: Partial): Promise; /** * Query records from a table * * @example * // With established connection * const result = await ductape.database.query({ * table: 'users', * where: { status: 'active' }, * orderBy: { column: 'created_at', order: 'DESC' }, * limit: 10, * }); * * // With explicit connection params * const result = await ductape.database.query({ * env: 'prd', * product: 'my-app', * database: 'main-db', * table: 'users', * }); */ query(options: IQueryOptions): Promise>; /** * Execute a raw query * * @example * // PostgreSQL * const result = await ductape.database.raw({ * query: 'SELECT * FROM users WHERE created_at > $1 AND status = $2', * params: [new Date('2024-01-01'), 'active'], * }); * * // MySQL * const result = await ductape.database.raw({ * query: 'SELECT * FROM users WHERE created_at > ? AND status = ?', * params: [new Date('2024-01-01'), 'active'], * }); * * // MongoDB * const result = await ductape.database.raw({ * query: { status: 'active', created_at: { $gte: new Date('2024-01-01') } }, * collection: 'users', * }); */ raw(options: IRawQueryOptions): Promise>; /** * Insert one or more records * * @example * // Single record * const result = await ductape.database.insert({ * table: 'users', * data: { * name: 'Jane Doe', * email: 'jane@example.com', * status: 'active', * }, * returning: true, * }); * * // Multiple records * const result = await ductape.database.insert({ * table: 'users', * data: [ * { name: 'User 1', email: 'user1@example.com' }, * { name: 'User 2', email: 'user2@example.com' }, * ], * }); * * // With conflict handling (upsert) * const result = await ductape.database.insert({ * table: 'users', * data: { email: 'john@example.com', name: 'John' }, * onConflict: { * columns: ['email'], * action: 'update', * update: ['name'], * }, * }); */ insert(options: IInsertOptions): Promise>; /** * Update records matching conditions * * @example * // Simple update * const result = await ductape.database.update({ * table: 'users', * data: { status: 'inactive' }, * where: { last_login: { $LT: new Date('2023-01-01') } }, * }); * * // With increment/decrement * await ductape.database.update({ * table: 'products', * data: { stock: { $inc: 10 } }, * where: { id: productId }, * }); */ update(options: IUpdateOptions): Promise>; /** * Delete records matching conditions * * @example * const result = await ductape.database.delete({ * table: 'users', * where: { status: 'deleted' }, * }); * console.log('Deleted count:', result.count); */ delete(options: IDeleteOptions): Promise; /** * Insert or update a record based on conflict keys * * @example * const result = await ductape.database.upsert({ * table: 'user_preferences', * data: { * user_id: 123, * theme: 'dark', * language: 'en', * }, * conflictKeys: ['user_id'], * }); * console.log('Operation:', result.operation); // 'inserted' or 'updated' */ upsert(options: IUpsertOptions): Promise>; /** * Count records * * @example * const count = await ductape.database.count({ * table: 'users', * where: { status: 'active' }, * }); */ count(options: ICountOptions): Promise; /** * Sum values of a column * * @example * const totalRevenue = await ductape.database.sum({ * table: 'orders', * column: 'total', * where: { status: 'completed' }, * }); */ sum(options: ISumOptions): Promise; /** * Calculate average of a column * * @example * const avgOrderValue = await ductape.database.avg({ * table: 'orders', * column: 'total', * }); */ avg(options: IAvgOptions): Promise; /** * Get minimum value of a column * * @example * const minPrice = await ductape.database.min({ * table: 'products', * column: 'price', * }); */ min(options: IMinMaxOptions): Promise; /** * Get maximum value of a column * * @example * const maxPrice = await ductape.database.max({ * table: 'products', * column: 'price', * }); */ max(options: IMinMaxOptions): Promise; /** * Perform multiple aggregations in one query * * @example * const stats = await ductape.database.aggregate({ * table: 'orders', * operations: { * total_revenue: { $SUM: 'total' }, * order_count: { $COUNT: '*' }, * avg_order_value: { $AVG: 'total' }, * }, * where: { status: 'completed' }, * }); */ aggregate(options: IAggregateOptions): Promise; /** * Group records and perform aggregations * * @example * const salesByCategory = await ductape.database.groupBy({ * table: 'products', * groupBy: ['category'], * operations: { * total_sales: { $SUM: 'sales_count' }, * avg_price: { $AVG: 'price' }, * }, * having: { * total_sales: { $GT: 100 }, * }, * }); */ groupBy(options: IGroupByOptions): Promise[]>; /** * Execute operations within a transaction (callback API - recommended) * * @example * const order = await ductape.database.transaction({ * env: 'prd', * product: 'my-app', * database: 'main-db', * }, async (transaction) => { * const order = await ductape.database.insert({ * table: 'orders', * data: { customer_id: 123, total: 99.99 }, * transaction, * }); * * await ductape.database.insert({ * table: 'order_items', * data: items.map(item => ({ order_id: order.insertedIds[0], ...item })), * transaction, * }); * * return order; * }); */ transaction(options: ITransactionOptions, callback: ITransactionCallback): Promise; /** * Begin a transaction manually * * @example * const transaction = await ductape.database.beginTransaction({ * env: 'prd', * product: 'my-app', * database: 'main-db', * isolationLevel: 'REPEATABLE_READ', * }); * * try { * await ductape.database.insert({ table: 'accounts', data: {...}, transaction }); * await transaction.commit(); * } catch (error) { * await transaction.rollback(); * throw error; * } */ beginTransaction(options: ITransactionOptions): Promise; /** * Create a new table * * @example * import { SchemaHelpers } from '@ductape/sdk'; * * await ductape.database.createTable( * { env: 'dev', product: 'my-app', database: 'main-db' }, * { * name: 'products', * columns: [ * SchemaHelpers.id(), * SchemaHelpers.string('name', 255, false), * SchemaHelpers.decimal('price', 10, 2), * ...SchemaHelpers.timestamps(), * ], * }, * { ifNotExists: true } * ); */ createTable(connectionConfigOrDefinition: IConnectionConfig | ITableDefinition, tableDefinitionOrOptions?: ITableDefinition | ICreateTableOptions, options?: ICreateTableOptions): Promise; /** * Alter an existing table * * @example * import { ColumnAlterationType, ColumnType } from '@ductape/sdk'; * * // Add column * await ductape.database.alterTable( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'products', * [{ type: ColumnAlterationType.ADD, column: { name: 'sku', type: ColumnType.STRING, length: 50 } }] * ); * * // Drop column * await ductape.database.alterTable( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'products', * [{ type: ColumnAlterationType.DROP, columnName: 'old_field' }] * ); * * // Rename column * await ductape.database.alterTable( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'products', * [{ type: ColumnAlterationType.RENAME, oldName: 'old_name', newName: 'new_name' }] * ); */ alterTable(connectionConfigOrTableName: IConnectionConfig | string, tableNameOrAlterations: string | IAlterTableOperation[], alterations?: IAlterTableOperation[]): Promise; /** * Drop a table * * @example * await ductape.database.dropTable( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'old_table' * ); */ dropTable(connectionConfigOrTableName: IConnectionConfig | string, tableName?: string): Promise; /** * List all tables in the database * * @example * const tables = await ductape.database.listTables({ * env: 'dev', * product: 'my-app', * database: 'main-db', * }); */ listTables(connectionConfig?: IConnectionConfig): Promise; /** * List all tables with basic information including estimated row counts * * @example * const tables = await ductape.database.listTablesWithInfo({ * env: 'dev', * product: 'my-app', * database: 'main-db', * }); * // Returns: [{ name: 'users', estimatedRowCount: 1247, schema: 'public' }, ...] */ listTablesWithInfo(connectionConfig?: IConnectionConfig): Promise; /** * Check if a table exists * * @example * const exists = await ductape.database.tableExists( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'users' * ); */ tableExists(connectionConfigOrTableName: IConnectionConfig | string, tableName?: string): Promise; /** * Get the schema of a table * * @example * const schema = await ductape.database.getTableSchema( * { env: 'dev', product: 'my-app', database: 'main-db' }, * 'users' * ); * console.log('Table:', schema.name); * console.log('Columns:', schema.columns); * console.log('Indexes:', schema.indexes); */ getTableSchema(connectionConfigOrTableName: IConnectionConfig | string, tableName?: string): Promise; /** * Create an index * * @example * await ductape.database.createIndex({ * env: 'dev', * product: 'my-app', * database: 'main-db', * table: 'users', * index: { * name: 'idx_users_email', * table: 'users', * columns: [{ name: 'email' }], * unique: true, * }, * ifNotExists: true, * concurrent: true, // PostgreSQL: create without locking * }); */ createIndex(options: ICreateIndexOptions): Promise; /** * Drop an index * * @example * await ductape.database.dropIndex({ * env: 'dev', * product: 'my-app', * database: 'main-db', * table: 'users', * indexName: 'idx_users_old', * ifExists: true, * concurrent: true, * }); */ dropIndex(options: IDropIndexOptions): Promise; /** * List all indexes on a table * * @example * const indexes = await ductape.database.listIndexes({ * env: 'dev', * product: 'my-app', * database: 'main-db', * table: 'users', * includeSystem: false, * }); */ listIndexes(options: IListIndexesOptions): Promise; /** * Get index statistics * * @example * // All indexes on a table * const stats = await ductape.database.getIndexStatistics( * { env: 'prd', product: 'my-app', database: 'main-db' }, * 'users' * ); * * // Specific index * const emailStats = await ductape.database.getIndexStatistics( * { env: 'prd', product: 'my-app', database: 'main-db' }, * 'users', * 'idx_users_email' * ); */ getIndexStatistics(connectionConfig: IConnectionConfig, tableName: string, indexName?: string): Promise; /** * Get a normalized schema snapshot for the given database/env context. * * @example * const snapshot = await ductape.database.getSchemaSnapshot({ * env: 'prd', * product: 'my-product', * database: 'main-db', * }); */ getSchemaSnapshot(connectionConfig: IConnectionConfig, options?: { includeTables?: string[]; }): Promise; /** * Run a migration * * @example * const result = await ductape.database.runMigration([migration]); * console.log('Migrated:', result.size); */ runMigration(migrations: IMigration[], options?: { dryRun?: boolean; appliedBy?: string; }): Promise>; /** * Rollback migrations * * @example * const result = await ductape.database.rollbackMigration([migration], 1); * console.log('Rolled back:', result.size); */ rollbackMigration(migrations: IMigration[], count?: number): Promise>; /** * Get migration history (applied migrations) * * @example * const history = await ductape.database.getMigrationHistory(); * history.forEach((entry) => { * console.log('Tag:', entry.tag); * console.log('Applied at:', entry.appliedAt); * }); */ getMigrationHistory(): Promise; /** * Get status of all migrations * * @example * const status = await ductape.database.getMigrationStatus(allMigrations); * console.log('Pending:', status.pending); */ getMigrationStatus(migrations: IMigration[]): Promise; /** * Migration management sub-object * Provides CRUD operations for database migrations via ProductBuilder */ get migration(): { /** * Create a new database migration * * @example * await ductape.database.migration.create({ * product: 'my-product', * database: 'main-db', * data: { * name: 'Add users table', * tag: 'main-db:add-users-table', * value: { * up: ['CREATE TABLE users (id SERIAL PRIMARY KEY, email VARCHAR(255))'], * down: ['DROP TABLE users'], * }, * }, * }); */ create: (options: { product: string; database: string; data: { name: string; tag: string; description?: string; value: { up: string[]; down: string[]; }; }; }) => Promise; /** * Update an existing database migration * * @example * await ductape.database.migration.update({ * product: 'my-product', * tag: 'main-db:add-users-table', * data: { * description: 'Updated description', * }, * }); */ update: (options: { product: string; tag: string; data: { name?: string; description?: string; value?: { up: string[]; down: string[]; }; }; }) => Promise; /** * Fetch a specific database migration * * @example * const migration = await ductape.database.migration.fetch({ * product: 'my-product', * tag: 'main-db:add-users-table', * }); */ fetch: (options: { product: string; tag: string; }) => Promise; /** * Fetch all database migrations for a database * * @example * const migrations = await ductape.database.migration.fetchAll({ * product: 'my-product', * database: 'main-db', * }); */ fetchAll: (options: { product: string; database: string; }) => Promise; /** * Delete a database migration * * @example * await ductape.database.migration.delete({ * product: 'my-product', * tag: 'main-db:add-users-table', * }); */ delete: (options: { product: string; tag: string; }) => Promise; /** * Run a database migration (up) * @deprecated Migration execution has been deprecated */ run: (_options: { product: string; migration: string; env: string; }) => Promise; /** * Rollback a database migration (down) * @deprecated Migration execution has been deprecated */ rollback: (_options: { product: string; migration: string; env: string; }) => Promise; }; /** * Action management sub-object */ get action(): { /** * Create a new database action * * @example * await ductape.database.action.create({ * name: 'Get Users Paginated', * tag: 'postgresdb:get-users-paginated', * tableName: 'users', * operation: DatabaseActionTypes.QUERY, * template: { * where: { is_active: true }, * limit: '{{limit}}', * offset: '{{offset}}', * }, * }); */ create: (options: IActionCreateOptions) => Promise; /** * Update an existing action * * @example * await ductape.database.action.update({ * tag: 'postgresdb:get-users', * template: { where: { status: '{{status}}' } }, * }); */ update: (options: IActionUpdateOptions) => Promise; /** * Fetch a specific action * * @example * const action = await ductape.database.action.fetch('postgresdb:get-users'); */ fetch: (tag: string) => Promise; /** * Fetch all actions for a database * * @example * const actions = await ductape.database.action.fetchAll('postgresdb'); */ fetchAll: (databaseTag: string) => Promise; /** * Delete a database action * * @example * await ductape.database.action.delete('postgresdb:get-users'); */ delete: (tag: string) => Promise; /** * Dispatches a database action to run as a scheduled job. * @param {IDBActionDispatchInput} data - The database action dispatch input. * @returns {Promise} The dispatch result with job ID and status. * @example * // Schedule a database action to run in 1 hour * await ductape.databases.action.dispatch({ * product: 'my-product', * env: 'production', * database: 'users-db', * event: 'cleanup-inactive', * input: { query: { inactive: true } }, * schedule: { start_at: Date.now() + 3600000 } * }); * * // Run on a cron schedule * await ductape.databases.action.dispatch({ * product: 'my-product', * env: 'production', * database: 'analytics-db', * event: 'aggregate-daily', * input: { query: {} }, * schedule: { cron: '0 0 * * *' } // Daily at midnight * }); */ dispatch: (data: IDBActionDispatchInput) => Promise; }; /** * Execute a database action * * @example * const users = await ductape.database.execute({ * product: 'my-product', * env: 'prd', * database: 'postgresdb', * action: 'get-users-paginated', * input: { * limit: 25, * offset: 0, * }, * }); */ execute(options: IActionExecuteOptions): Promise; /** * Dispatches a database operation to run as a scheduled job. * Use this for direct database operations (query, insert, update, delete, etc.). * @param {IDBOperationDispatchInput} data - The database operation dispatch input. * @returns {Promise} The dispatch result with job ID and status. * @example * // Schedule a database operation to run in 1 hour * await ductape.databases.dispatch({ * product: 'my-product', * env: 'production', * database: 'users-db', * operation: 'deleteMany', * input: { filter: { inactive: true, lastLogin: { $lt: '2024-01-01' } } }, * schedule: { start_at: Date.now() + 3600000 } * }); * * // Run daily database cleanup * await ductape.databases.dispatch({ * product: 'my-product', * env: 'production', * database: 'logs-db', * operation: 'deleteMany', * input: { filter: { createdAt: { $lt: '$DateAdd($Now(), -30, "days")' } } }, * schedule: { cron: '0 3 * * *' } // Daily at 3 AM * }); */ dispatch(data: IDBOperationDispatchInput): Promise; /** * Get the appropriate adapter for the operation */ getAdapter(options?: { env?: string; database?: string; }): BaseAdapter; /** * Ensure an adapter exists for the given database/env/product (lazy connect on failure). * Use before getAdapter() when options include database+env+product (e.g. from proxy). */ private ensureAdapterFor; private isConnectionError; private withAdapterRetry; /** * Get the current connection context */ getCurrentContext(): IConnectionContext | null; /** * Get a connection-scoped interface for a specific database/environment. * This ensures schema operations are isolated to a specific connection. * * @example * const usersDb = db.connection('users-db', 'dev'); * await usersDb.schema.create('users', { ... }); * * const ordersDb = db.connection('orders-db', 'dev'); * await ordersDb.schema.create('orders', { ... }); // Different connection */ connection(database: string, env: string, product?: string): DatabaseConnection; /** * Build a context key from database and environment */ private buildContextKey; /** * Convert DatabaseTypes (from product API) to DatabaseType (ORM internal) */ private convertDatabaseType; /** * Simplified schema operations API (Mongoose-style) * * @example * await db.connect({ env: 'dev', product: 'my-app', database: 'users-db' }); * * // Create a table with Mongoose-style schema * await db.schema.create('users', { * id: { type: 'uuid', primaryKey: true }, * email: { type: 'string', length: 255, required: true, unique: true }, * name: { type: 'string', length: 100 }, * age: 'integer', // Shorthand * status: { type: 'enum', enum: ['active', 'inactive'], default: 'active' }, * }, { timestamps: true }); */ get schema(): { /** * Create a collection/table with Mongoose-style schema definition */ create: (name: string, definition: ISimpleSchemaDefinition, options?: ISimpleCreateOptions) => Promise; /** * Drop a collection/table */ drop: (name: string, options?: ISimpleDropOptions) => Promise; /** * Add a field to a collection */ addField: (collection: string, fieldName: string, definition: SimpleFieldType | ISimpleFieldDefinition) => Promise; /** * Drop a field from a collection */ dropField: (collection: string, fieldName: string) => Promise; /** * Rename a field in a collection */ renameField: (collection: string, oldName: string, newName: string) => Promise; /** * Modify a field's definition */ modifyField: (collection: string, fieldName: string, changes: Partial) => Promise; /** * Create an index on a collection */ createIndex: (collection: string, fields: string[] | IIndexFieldDefinition[], options?: ISimpleIndexOptions) => Promise; /** * Drop an index from a collection */ dropIndex: (collection: string, indexName: string) => Promise; /** * Add a constraint (SQL databases only) */ addConstraint: (collection: string, constraint: IMigrationConstraintDefinition) => Promise; /** * Drop a constraint (SQL databases only) */ dropConstraint: (collection: string, constraintName: string) => Promise; /** * Rename a collection/table */ rename: (oldName: string, newName: string) => Promise; /** * Check if a collection/table exists */ exists: (name: string) => Promise; /** * List all collections/tables */ list: (schemaName?: string) => Promise; /** * Get detailed schema information for a collection */ describe: (name: string) => Promise; /** * List indexes on a collection */ indexes: (collection: string) => Promise; }; /** * Create a collection with Mongoose-style definition */ private schemaCreate; /** * Drop a collection */ private schemaDrop; /** * Add a field to a collection */ private schemaAddField; /** * Drop a field from a collection */ private schemaDropField; /** * Rename a field */ private schemaRenameField; /** * Modify a field's definition */ private schemaModifyField; /** * Create an index */ private schemaCreateIndex; /** * Drop an index */ private schemaDropIndex; /** * Add a constraint (SQL only) */ private schemaAddConstraint; /** * Drop a constraint (SQL only) */ private schemaDropConstraint; /** * Rename a collection */ private schemaRename; /** * Check if collection exists */ private schemaExists; /** * List all collections */ private schemaList; /** * Describe a collection's schema */ private schemaDescribe; /** * List indexes on a collection */ private schemaListIndexes; /** * Convert Mongoose-style schema definition to IFieldDefinition array */ private convertToFieldDefinitions; /** * Convert a single field definition */ private convertFieldDefinition; /** * Normalize Mongoose-style type names to FieldType */ private normalizeFieldType; /** * Normalize index fields to IIndexFieldDefinition array */ private normalizeIndexFields; /** * Get adapter by context key (used by DatabaseConnection) * @internal */ getAdapterByContextKey(contextKey: string): BaseAdapter; /** * Fetch database dashboard metrics from logs * * @example * ```ts * const dashboard = await databases.fetchDashboard({ * product: 'my-product', * database: 'my-database', * env: 'production', * }); * console.log(dashboard.dau, dashboard.activityTimeline); * ``` */ fetchDashboard(options: { product: string; database: string; env?: string; }): Promise<{ dau: { current: number; previous: number; change: number; }; wau: { current: number; previous: number; change: number; }; mau: { current: number; previous: number; change: number; }; totalOperations: number; successfulOperations: number; failedOperations: number; newOperationsThisWeek: number; avgExecutionTime: { current: string; previous: string; change: number; }; activityTimeline: Array<{ date: string; sessions: number; }>; peakHours: Array<{ hour: string; count: number; }>; environmentBreakdown: Array<{ env: string; count: number; percentage: number; }>; methodBreakdown: Array<{ method: string; count: number; percentage: number; }>; }>; } /** * Connection-scoped database interface. * Provides isolated schema, query, and aggregation operations for a specific database/environment. */ export declare class DatabaseConnection { private service; private _database; private _env; private _product; private contextKey; private triggerProcessor; constructor(service: DatabaseService, database: string, env: string, product: string); /** Database tag */ get database(): string; /** Environment slug */ get env(): string; /** Product tag */ get product(): string; /** Get the adapter for this connection */ getAdapter(): BaseAdapter; /** * Schema operations scoped to this connection */ get schema(): { create: (name: string, definition: ISimpleSchemaDefinition, options?: ISimpleCreateOptions) => Promise; drop: (name: string, options?: ISimpleDropOptions) => Promise; addField: (collection: string, fieldName: string, definition: SimpleFieldType | ISimpleFieldDefinition) => Promise; dropField: (collection: string, fieldName: string) => Promise; renameField: (collection: string, oldName: string, newName: string) => Promise; modifyField: (collection: string, fieldName: string, changes: Partial) => Promise; createIndex: (collection: string, fields: string[] | IIndexFieldDefinition[], options?: ISimpleIndexOptions) => Promise; dropIndex: (collection: string, indexName: string) => Promise; addConstraint: (collection: string, constraint: IMigrationConstraintDefinition) => Promise; dropConstraint: (collection: string, constraintName: string) => Promise; rename: (oldName: string, newName: string) => Promise; exists: (name: string) => Promise; list: (schemaName?: string) => Promise; describe: (name: string) => Promise; indexes: (collection: string) => Promise; }; query(options: Omit): Promise>; insert(options: Omit): Promise>; update(options: Omit): Promise>; delete(options: Omit): Promise; upsert(options: Omit): Promise>; raw(options: Omit): Promise>; count(options: Omit): Promise; sum(options: Omit): Promise; avg(options: Omit): Promise; min(options: Omit): Promise; max(options: Omit): Promise; aggregate(options: Omit): Promise; groupBy(options: Omit): Promise[]>; /** * Trigger operations scoped to this connection */ get triggers(): { /** * Create a trigger and store it on the backend * @param data - Trigger configuration (tag should be just the trigger name, database tag is added automatically) */ create: (data: Partial) => Promise; /** * Update an existing trigger on the backend * @param data - Trigger configuration */ update: (data: Partial) => Promise; /** * Fetch a specific trigger from the backend * @param triggerTag - The trigger tag (without database prefix) */ fetch: (triggerTag: string) => Promise; /** * Fetch all triggers for this database from the backend * @deprecated Use list() instead */ fetchAll: () => Promise; /** * List all triggers for this database from the backend * @param options - Optional filters (table, event, enabled) */ list: (options?: { table?: string; event?: string; enabled?: boolean; }) => Promise; /** * Delete a trigger from the backend * @param triggerTag - The trigger tag (without database prefix) */ delete: (triggerTag: string) => Promise; /** * Register a trigger in memory for the current session (does not persist to backend) * @deprecated Use create() for persistent triggers */ register: (table: string, trigger: ITriggerDefinition) => void; /** * Register multiple triggers in memory (does not persist to backend) * @deprecated Use create() for persistent triggers */ registerAll: (triggers: Array<{ table: string; trigger: ITriggerDefinition; }>) => void; /** * Unregister a trigger from memory */ unregister: (table: string, triggerName: string) => boolean; /** * Get all triggers for a table and event */ getTriggersForEvent: (table: string, event: TriggerEvent) => ITriggerDefinition[]; /** * Execute all triggers for an event */ execute: (event: TriggerEvent, context: Omit) => Promise; /** * Load triggers from the backend and register them in memory */ load: () => Promise; /** * Set the Ductape instance for trigger actions */ setDuctapeInstance: (instance: any) => void; /** * Get the trigger processor instance */ getProcessor: () => TriggerProcessor; /** * Trigger action builders */ Trigger: { database: { insert: (table: string, data: Record | string) => import("./types/trigger.interface").ITriggerDatabaseAction; update: (table: string, data: Record | string, where: Record | string) => import("./types/trigger.interface").ITriggerDatabaseAction; delete: (table: string, where: Record | string) => import("./types/trigger.interface").ITriggerDatabaseAction; query: (table: string, where: Record | string) => import("./types/trigger.interface").ITriggerDatabaseAction; }; storage: { upload: (storage: string, path: string, options?: Partial) => import("./types/trigger.interface").ITriggerStorageAction; delete: (storage: string, path: string) => import("./types/trigger.interface").ITriggerStorageAction; copy: (storage: string, sourcePath: string, destinationPath: string) => import("./types/trigger.interface").ITriggerStorageAction; }; notification: { email: (notification: string, recipients: string | string[], options?: Partial) => import("./types/trigger.interface").ITriggerNotificationAction; sms: (notification: string, recipients: string | string[], options?: Partial) => import("./types/trigger.interface").ITriggerNotificationAction; push: (notification: string, recipients: string | string[], options?: Partial) => import("./types/trigger.interface").ITriggerNotificationAction; callback: (notification: string, options?: Partial) => import("./types/trigger.interface").ITriggerNotificationAction; }; broker: { publish: (event: string, message: Record | string, options?: Partial) => import("./types/trigger.interface").ITriggerBrokerAction; }; cache: { set: (cache: string, key: string, value: any, ttl?: number) => import("./types/trigger.interface").ITriggerCacheAction; invalidate: (cache: string, pattern: string) => import("./types/trigger.interface").ITriggerCacheAction; delete: (cache: string, key: string) => import("./types/trigger.interface").ITriggerCacheAction; }; feature: { execute: (feature: string, input?: Record | string) => import("./types/trigger.interface").ITriggerFeatureAction; dispatch: (feature: string, input?: Record | string) => import("./types/trigger.interface").ITriggerFeatureAction; }; action: { execute: (app: string, action: string, input?: Record | string) => import("./types/trigger.interface").ITriggerActionExecuteAction; }; agent: { run: (agent: string, prompt?: string, input?: Record | string) => import("./types/trigger.interface").ITriggerAgentAction; }; log: { debug: (message: string, data?: Record | string) => import("./types/trigger.interface").ITriggerLogAction; info: (message: string, data?: Record | string) => import("./types/trigger.interface").ITriggerLogAction; warn: (message: string, data?: Record | string) => import("./types/trigger.interface").ITriggerLogAction; error: (message: string, data?: Record | string) => import("./types/trigger.interface").ITriggerLogAction; }; http: { get: (url: string, options?: Partial) => import("./types/trigger.interface").ITriggerHttpAction; post: (url: string, body?: Record | string, options?: Partial) => import("./types/trigger.interface").ITriggerHttpAction; put: (url: string, body?: Record | string, options?: Partial) => import("./types/trigger.interface").ITriggerHttpAction; patch: (url: string, body?: Record | string, options?: Partial) => import("./types/trigger.interface").ITriggerHttpAction; delete: (url: string, options?: Partial) => import("./types/trigger.interface").ITriggerHttpAction; }; custom: (handler: (context: ITriggerContext) => Promise) => import("./types/trigger.interface").ITriggerCustomAction; when: { field: (field: string) => { equals: (value: any) => import("./types/trigger.interface").IConditionClause; notEquals: (value: any) => import("./types/trigger.interface").IConditionClause; greaterThan: (value: any) => import("./types/trigger.interface").IConditionClause; lessThan: (value: any) => import("./types/trigger.interface").IConditionClause; in: (values: any[]) => import("./types/trigger.interface").IConditionClause; contains: (value: string) => import("./types/trigger.interface").IConditionClause; isNull: () => import("./types/trigger.interface").IConditionClause; isNotNull: () => import("./types/trigger.interface").IConditionClause; changed: () => import("./types/trigger.interface").IConditionClause; changedTo: (value: any) => import("./types/trigger.interface").IConditionClause; changedFrom: (value: any) => import("./types/trigger.interface").IConditionClause; }; and: (...conditions: (import("./types/trigger.interface").IConditionClause | import("./types/trigger.interface").ITriggerCondition)[]) => import("./types/trigger.interface").ITriggerCondition; or: (...conditions: (import("./types/trigger.interface").IConditionClause | import("./types/trigger.interface").ITriggerCondition)[]) => import("./types/trigger.interface").ITriggerCondition; not: (condition: import("./types/trigger.interface").IConditionClause | import("./types/trigger.interface").ITriggerCondition) => import("./types/trigger.interface").ITriggerCondition; }; }; /** * Trigger events enum */ TriggerEvent: typeof TriggerEvent; /** * Trigger timing enum */ TriggerTiming: typeof TriggerTiming; /** * Trigger action types enum */ TriggerActionType: typeof TriggerActionType; }; } export declare const databaseService: DatabaseService;