/** * Nuvex - PostgreSQL Storage Layer (L3) * Next-gen Unified Vault Experience * * PostgreSQL persistent storage layer serving as the source of truth for all data. * Provides ACID-compliant, durable storage with full data integrity guarantees. * * Features: * - ACID-compliant persistent storage * - Source of truth for all data * - JSON support for flexible data structures * - Automatic TTL-based expiration * - Connection pooling for optimal performance * - Health monitoring with SELECT 1 queries * * @author Waren Gonzaga, WG Technology Labs * @since 2025 */ import type { Pool as PoolType } from 'pg'; import type { StorageLayerInterface, Logger } from '../interfaces/index.js'; import type { PostgresConfig } from '../types/index.js'; /** * PostgreSQL Storage Layer - L3 Persistent Storage * * Implements persistent storage using PostgreSQL. This is the authoritative * source of truth for all data in the system. All writes must succeed here * for the operation to be considered successful. * * **Key Features:** * - ACID compliance for data integrity * - Durable storage that survives restarts * - JSON/JSONB support for complex objects * - TTL-based automatic expiration * - Connection pooling for performance * - Transaction support for complex operations * * **Storage Schema:** * - Table: nuvex_storage * - Columns: id, key (unique), value (JSONB), expires_at, created_at, updated_at * - Indexes: key, expires_at, key pattern (trigram) * * **Performance Characteristics:** * - Get: O(log n) with index lookup * - Set: O(log n) with index update * - Latency: 5-50ms typical (storage + network) * * **Error Handling:** * - Returns null on read errors (graceful degradation) * - Logs errors for monitoring * - Throws on critical connection failures * * @implements {StorageLayerInterface} * * @example * ```typescript * // Create PostgreSQL layer * const postgres = new PostgresStorage({ * host: 'localhost', * port: 5432, * database: 'myapp', * user: 'postgres', * password: 'password' * }); * * // Connect (creates pool) * await postgres.connect(); * * // Store data (source of truth) * await postgres.set('user:123', userData, 86400); * * // Retrieve data * const data = await postgres.get('user:123'); * * // Check health * const isHealthy = await postgres.ping(); * ``` * * @class PostgresStorage * @since 1.0.0 */ export declare class PostgresStorage implements StorageLayerInterface { /** PostgreSQL connection pool */ private pool; /** Database configuration or existing pool */ private readonly config; /** Whether the pool is connected */ private connected; /** Optional logger for debugging and monitoring */ private logger; /** Whether we created the pool (vs. received existing one) */ private readonly ownsPool; /** Table name for storage */ private readonly tableName; /** Key column name */ private readonly keyColumn; /** Value/data column name */ private readonly valueColumn; /** Whether schema should be created automatically during startup */ private readonly autoSetupSchema; /** Whether pg_trgm support should be enabled during schema setup */ private readonly enableTrigram; /** Whether cleanup job should be enabled during schema setup */ private readonly enableCleanupJob; /** Last known readiness state for the configured Nuvex schema */ private schemaReady; /** Tracks the last schema issue message that was emitted at warn level */ private lastSchemaIssue; /** * Creates a new PostgresStorage instance * * Accepts either a PostgreSQL configuration object or an existing Pool instance. * If a Pool is provided, the caller is responsible for managing its lifecycle. * * Schema configuration is extracted from the config object when creating a new pool. * When using an existing pool, schema defaults to standard Nuvex naming. * * @param config - PostgreSQL configuration or existing Pool instance * @param logger - Optional logger for debugging * * @example * ```typescript * // With configuration (supports schema customization) * const postgres = new PostgresStorage({ * host: 'localhost', * database: 'myapp', * user: 'postgres', * password: 'password', * schema: { * tableName: 'storage_cache', * columns: { key: 'key', value: 'value' } * } * }); * * // With existing pool (uses default schema) * const existingPool = new Pool({ ... }); * const postgres = new PostgresStorage(existingPool); * ``` */ constructor(config: PostgresConfig | PoolType, logger?: Logger | null); /** * Establish connection to PostgreSQL * * Creates a connection pool (if not already provided) and tests the connection. * Should be called before any storage operations. * * @throws {Error} If connection test fails * * @example * ```typescript * try { * await postgres.connect(); * console.log('PostgreSQL connected'); * } catch (error) { * console.error('PostgreSQL connection failed:', error); * } * ``` */ connect(): Promise; /** * Close PostgreSQL connection pool * * Only closes the pool if we created it. If an existing pool was provided, * the caller is responsible for closing it. * * @example * ```typescript * await postgres.disconnect(); * ``` */ disconnect(): Promise; /** * Retrieve a value from PostgreSQL * * Queries the nuvex_storage table and automatically filters out expired entries. * Deserializes the JSON-stored value. * * @param key - The key to retrieve * @returns Promise resolving to the value or null if not found/expired * * @example * ```typescript * const userData = await postgres.get('user:123'); * if (userData !== null) { * console.log('Found in PostgreSQL'); * } * ``` */ get(key: string): Promise; /** * Store a value in PostgreSQL * * Inserts or updates the value in the nuvex_storage table. Uses UPSERT * (INSERT ... ON CONFLICT) to handle existing keys efficiently. * * **Note:** This is the authoritative write. If this fails, the entire * write operation should be considered failed. * * @param key - The key to store * @param value - The value to store (will be JSON serialized) * @param ttlSeconds - Optional TTL in seconds * * @example * ```typescript * // Store with 24 hour TTL * await postgres.set('user:123', userData, 86400); * * // Store without TTL (persists until deleted) * await postgres.set('config:app', configData); * ``` */ set(key: string, value: unknown, ttlSeconds?: number): Promise; /** * Delete a value from PostgreSQL * * Permanently removes the key from the nuvex_storage table. * * @param key - The key to delete * * @example * ```typescript * await postgres.delete('user:123'); * ``` */ delete(key: string): Promise; /** * Check if a key exists in PostgreSQL * * Queries for the key and verifies it hasn't expired. * * @param key - The key to check * @returns Promise resolving to true if the key exists and is not expired * * @example * ```typescript * if (await postgres.exists('user:123')) { * console.log('Key exists in PostgreSQL'); * } * ``` */ exists(key: string): Promise; /** * Clear all keys from PostgreSQL * * **WARNING:** This operation deletes all data from nuvex_storage table. * Use with extreme caution in production environments. * * @example * ```typescript * await postgres.clear(); // Deletes all data * ``` */ clear(): Promise; /** * Health check for PostgreSQL connectivity * * Executes a simple SELECT 1 query to verify connectivity and database * responsiveness. This is a lightweight connectivity check only and does * not guarantee that the configured Nuvex schema exists or is writable. * * @returns Promise resolving to true if PostgreSQL is healthy and responsive * * @example * ```typescript * const isHealthy = await postgres.ping(); * if (!isHealthy) { * console.error('PostgreSQL connection is down'); * } * ``` */ ping(): Promise; /** * Check whether the configured Nuvex PostgreSQL schema is ready for use. * * Verifies that the configured storage table exists with the expected * columns and that writes can succeed inside a rolled-back transaction. * * @returns Promise resolving to true when storage is usable */ isReady(): Promise; /** * Check if PostgreSQL is connected * * @returns True if connected */ isConnected(): boolean; /** * Get the PostgreSQL connection pool * * Useful for executing custom queries or transactions. * * @returns The connection pool or null if not connected * * @example * ```typescript * const pool = postgres.getPool(); * if (pool) { * const result = await pool.query('SELECT * FROM custom_table'); * } * ``` */ getPool(): PoolType | null; /** * Atomically increment a numeric value * * Uses PostgreSQL UPDATE with row-level locking for true atomic increments. * If the key doesn't exist, it's created with the delta value. * * This operation is safe for concurrent access across multiple instances. * * @param key - The key to increment * @param delta - The amount to increment by * @param ttlSeconds - Optional TTL in seconds * @returns Promise resolving to the new value after increment * * @example * ```typescript * // Atomic increment - safe for concurrent access * const newValue = await postgres.increment('counter', 1, 86400); * ``` */ increment(key: string, delta: number, ttlSeconds?: number): Promise; /** * Log a message if logger is configured * * @private * @param level - Log level * @param message - Log message * @param meta - Optional metadata */ private log; private handleQueryError; /** * Mark the configured schema as not ready and log the issue. * * Emits a warning when readiness transitions from ready to unready or when a * new schema issue message is observed. Repeated identical schema problems are * downgraded to debug level to reduce noisy logs during high read volume. * * @param message - Schema readiness log message * @param meta - Structured metadata for diagnostics */ private markSchemaUnready; } //# sourceMappingURL=postgres.d.ts.map