/** * Lazy Extension Loader for PostgreSQL * * Provides lazy loading of PostgreSQL extensions to improve cold start times. * Extensions are loaded on-demand when they are first needed, either through * explicit calls or automatic detection from SQL queries. * * @module extensions * * @example * ```typescript * import { LazyExtensionLoader, createLazyExtensionLoader } from 'postgres.do/extensions' * * // Create a loader with a query executor * const loader = new LazyExtensionLoader({ * query: async (sql) => pg.exec(sql) * }) * * // Explicitly ensure an extension is loaded * await loader.ensureLoaded('vector') * * // Auto-detect and load extensions from SQL * await loader.executeWithAutoLoad("SELECT embedding <-> '[1,2,3]' FROM items") * ``` */ /** * Supported PostgreSQL extension names */ type ExtensionName = 'vector' | 'pg_trgm' | 'pgcrypto' | 'hstore' | 'citext' | 'uuid-ossp' | 'cube' | 'earthdistance' | 'fuzzystrmatch' | 'intarray' | 'ltree' | 'tablefunc' | 'unaccent' | 'btree_gin' | 'btree_gist' | 'bloom'; /** * Metadata for a PostgreSQL extension */ interface ExtensionMetadata { /** Extension identifier used in our registry */ name: ExtensionName; /** Display name for the extension */ displayName: string; /** The actual PostgreSQL extension name (used in CREATE EXTENSION) */ extensionName: string; /** Description of what the extension provides */ description: string; /** Whether the extension is available in PGLite WASM */ available: boolean; /** Dependencies that must be loaded first */ dependencies?: ExtensionName[]; /** Regex patterns to detect when this extension is needed in SQL */ detectionPatterns: RegExp[]; /** Common functions provided by this extension */ functions?: string[]; /** Custom types provided by this extension */ types?: string[]; /** Custom operators provided by this extension */ operators?: string[]; } /** * Interface for executing queries */ interface QueryExecutor { query(sql: string, params?: unknown[]): Promise<{ rows: unknown[]; }>; } /** * Result of loading a single extension */ interface ExtensionLoadResult { /** Extension name */ name: ExtensionName; /** Whether the extension was loaded successfully */ success: boolean; /** Whether the extension was already loaded (cache hit) */ wasAlreadyLoaded: boolean; /** Time taken to load in milliseconds (0 if already loaded) */ loadTimeMs: number; /** Error message if loading failed */ error?: string; } /** * Result of executing a query with auto-loading */ interface ExecuteWithAutoLoadResult { /** Extensions that were loaded for this query */ extensionsLoaded: ExtensionName[]; /** Query result */ result: { rows: unknown[]; }; /** Total time spent loading extensions */ extensionLoadTimeMs: number; } /** * Statistics about extension loading */ interface ExtensionLoaderStats { /** Number of extensions currently loaded */ totalLoaded: number; /** Names of loaded extensions */ loadedExtensions: ExtensionName[]; /** Number of cache hits (requests for already-loaded extensions) */ cacheHits: number; /** Load times for each extension in milliseconds */ loadTimes: Record; /** Number of times auto-detection was run */ autoDetectionCount: number; } /** * Options for the LazyExtensionLoader */ interface LazyExtensionLoaderOptions { /** Whether to log debug information */ debug?: boolean; /** Whether to disable automatic extension detection */ disableAutoDetection?: boolean; /** Timeout for loading each extension in milliseconds */ loadTimeoutMs?: number; } /** * Registry of supported PostgreSQL extensions with their metadata */ declare const EXTENSION_REGISTRY: Record; /** * Detect which extensions are needed for a SQL query * * Analyzes the SQL string and returns a list of extensions that should be * loaded before executing the query. * * @param sql - The SQL query to analyze * @returns Array of extension names that were detected * * @example * ```typescript * const extensions = detectExtensions("SELECT embedding <-> '[1,2,3]' FROM items") * // Returns: ['vector'] * * const extensions = detectExtensions("SELECT similarity(name, 'test') FROM users") * // Returns: ['pg_trgm'] * ``` */ declare function detectExtensions(sql: string): ExtensionName[]; /** * Lazy extension loader for PostgreSQL * * Loads extensions on-demand when they are first needed, improving cold start * times by avoiding loading all extensions at initialization. * * @example * ```typescript * const loader = new LazyExtensionLoader({ * query: async (sql) => pg.exec(sql) * }) * * // Load a specific extension * await loader.ensureLoaded('vector') * * // Auto-detect and load from SQL * await loader.executeWithAutoLoad("SELECT embedding <-> '[1,2,3]' FROM items") * * // Get statistics * console.log(loader.getStats()) * ``` */ declare class LazyExtensionLoader { private executor; private options; private loadedExtensions; private loadingPromises; private loadTimes; private cacheHits; private autoDetectionCount; constructor(executor: QueryExecutor, options?: LazyExtensionLoaderOptions); /** * Ensure an extension is loaded, loading it if necessary * * @param name - The extension to load * @returns Result of the load operation */ ensureLoaded(name: ExtensionName): Promise; /** * Internal method to load an extension */ private loadExtension; /** * Load multiple extensions * * @param names - Array of extension names to load * @returns Array of load results */ loadExtensions(names: ExtensionName[]): Promise; /** * Sort extensions by dependencies (topological sort) */ private sortByDependencies; /** * Detect extensions needed for a SQL query * * @param sql - The SQL query to analyze * @returns Array of extension names detected */ detectExtensions(sql: string): ExtensionName[]; /** * Execute a query with automatic extension loading * * Analyzes the SQL, loads any detected extensions, then executes the query. * * @param sql - The SQL query to execute * @param params - Optional query parameters * @returns Result including loaded extensions and query result */ executeWithAutoLoad(sql: string, params?: unknown[]): Promise; /** * Preload a set of extensions * * Useful for preloading extensions you know will be needed. * * @param names - Array of extension names to preload * @returns Array of load results */ preload(names: ExtensionName[]): Promise; /** * Check if an extension is loaded * * @param name - The extension name to check * @returns True if the extension is loaded */ isLoaded(name: ExtensionName): boolean; /** * Get list of loaded extensions * * @returns Array of loaded extension names */ getLoadedExtensions(): ExtensionName[]; /** * Get loading statistics * * @returns Statistics about extension loading */ getStats(): ExtensionLoaderStats; /** * Reset the loader state * * Clears loaded extensions and statistics. Useful for testing or * when you need to reload extensions (e.g., after a database reset). */ reset(): void; /** * Get metadata for an extension * * @param name - The extension name * @returns Extension metadata or undefined if not found */ static getMetadata(name: ExtensionName): ExtensionMetadata | undefined; /** * Get all available extensions * * @returns Array of extension metadata for available extensions */ static getAvailableExtensions(): ExtensionMetadata[]; /** * Check if a SQL query would trigger detection of a specific extension * * @param sql - The SQL query to check * @param extensionName - The extension to check for * @returns True if the extension would be detected */ static wouldDetect(sql: string, extensionName: ExtensionName): boolean; } /** * Create a LazyExtensionLoader instance * * @param executor - Query executor for running SQL * @param options - Loader options * @returns New LazyExtensionLoader instance * * @example * ```typescript * const loader = createLazyExtensionLoader({ * query: async (sql) => pg.exec(sql) * }) * * await loader.ensureLoaded('vector') * ``` */ declare function createLazyExtensionLoader(executor: QueryExecutor, options?: LazyExtensionLoaderOptions): LazyExtensionLoader; export { EXTENSION_REGISTRY, type ExecuteWithAutoLoadResult, type ExtensionLoadResult, type ExtensionLoaderStats, type ExtensionMetadata, type ExtensionName, LazyExtensionLoader, type LazyExtensionLoaderOptions, type QueryExecutor, createLazyExtensionLoader, detectExtensions };