{"version":3,"sources":["../src/extensions.ts"],"sourcesContent":["/**\n * Lazy Extension Loader for PostgreSQL\n *\n * Provides lazy loading of PostgreSQL extensions to improve cold start times.\n * Extensions are loaded on-demand when they are first needed, either through\n * explicit calls or automatic detection from SQL queries.\n *\n * @module extensions\n *\n * @example\n * ```typescript\n * import { LazyExtensionLoader, createLazyExtensionLoader } from 'postgres.do/extensions'\n *\n * // Create a loader with a query executor\n * const loader = new LazyExtensionLoader({\n *   query: async (sql) => pg.exec(sql)\n * })\n *\n * // Explicitly ensure an extension is loaded\n * await loader.ensureLoaded('vector')\n *\n * // Auto-detect and load extensions from SQL\n * await loader.executeWithAutoLoad(\"SELECT embedding <-> '[1,2,3]' FROM items\")\n * ```\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Supported PostgreSQL extension names\n */\nexport type ExtensionName =\n  | 'vector'\n  | 'pg_trgm'\n  | 'pgcrypto'\n  | 'hstore'\n  | 'citext'\n  | 'uuid-ossp'\n  | 'cube'\n  | 'earthdistance'\n  | 'fuzzystrmatch'\n  | 'intarray'\n  | 'ltree'\n  | 'tablefunc'\n  | 'unaccent'\n  | 'btree_gin'\n  | 'btree_gist'\n  | 'bloom'\n\n/**\n * Metadata for a PostgreSQL extension\n */\nexport interface ExtensionMetadata {\n  /** Extension identifier used in our registry */\n  name: ExtensionName\n  /** Display name for the extension */\n  displayName: string\n  /** The actual PostgreSQL extension name (used in CREATE EXTENSION) */\n  extensionName: string\n  /** Description of what the extension provides */\n  description: string\n  /** Whether the extension is available in PGLite WASM */\n  available: boolean\n  /** Dependencies that must be loaded first */\n  dependencies?: ExtensionName[]\n  /** Regex patterns to detect when this extension is needed in SQL */\n  detectionPatterns: RegExp[]\n  /** Common functions provided by this extension */\n  functions?: string[]\n  /** Custom types provided by this extension */\n  types?: string[]\n  /** Custom operators provided by this extension */\n  operators?: string[]\n}\n\n/**\n * Interface for executing queries\n */\nexport interface QueryExecutor {\n  query(sql: string, params?: unknown[]): Promise<{ rows: unknown[] }>\n}\n\n/**\n * Result of loading a single extension\n */\nexport interface ExtensionLoadResult {\n  /** Extension name */\n  name: ExtensionName\n  /** Whether the extension was loaded successfully */\n  success: boolean\n  /** Whether the extension was already loaded (cache hit) */\n  wasAlreadyLoaded: boolean\n  /** Time taken to load in milliseconds (0 if already loaded) */\n  loadTimeMs: number\n  /** Error message if loading failed */\n  error?: string\n}\n\n/**\n * Result of executing a query with auto-loading\n */\nexport interface ExecuteWithAutoLoadResult {\n  /** Extensions that were loaded for this query */\n  extensionsLoaded: ExtensionName[]\n  /** Query result */\n  result: { rows: unknown[] }\n  /** Total time spent loading extensions */\n  extensionLoadTimeMs: number\n}\n\n/**\n * Statistics about extension loading\n */\nexport interface ExtensionLoaderStats {\n  /** Number of extensions currently loaded */\n  totalLoaded: number\n  /** Names of loaded extensions */\n  loadedExtensions: ExtensionName[]\n  /** Number of cache hits (requests for already-loaded extensions) */\n  cacheHits: number\n  /** Load times for each extension in milliseconds */\n  loadTimes: Record<string, number>\n  /** Number of times auto-detection was run */\n  autoDetectionCount: number\n}\n\n/**\n * Options for the LazyExtensionLoader\n */\nexport interface LazyExtensionLoaderOptions {\n  /** Whether to log debug information */\n  debug?: boolean\n  /** Whether to disable automatic extension detection */\n  disableAutoDetection?: boolean\n  /** Timeout for loading each extension in milliseconds */\n  loadTimeoutMs?: number\n}\n\n// ============================================================================\n// Extension Registry\n// ============================================================================\n\n/**\n * Registry of supported PostgreSQL extensions with their metadata\n */\nexport const EXTENSION_REGISTRY: Record<ExtensionName, ExtensionMetadata> = {\n  vector: {\n    name: 'vector',\n    displayName: 'pgvector',\n    extensionName: 'vector',\n    description: 'Vector similarity search for PostgreSQL',\n    available: true,\n    detectionPatterns: [\n      /::vector\\b/i,\n      /\\bvector\\s*\\(/i,\n      /<->/,\n      /<=>/,\n      /<#>/,\n      /\\bl2_distance\\s*\\(/i,\n      /\\bcosine_distance\\s*\\(/i,\n      /\\binner_product\\s*\\(/i,\n      /\\bivfflat\\b/i,\n      /\\bhnsw\\b/i,\n    ],\n    functions: ['l2_distance', 'cosine_distance', 'inner_product', 'vector'],\n    types: ['vector'],\n    operators: ['<->', '<=>', '<#>'],\n  },\n\n  pg_trgm: {\n    name: 'pg_trgm',\n    displayName: 'pg_trgm',\n    extensionName: 'pg_trgm',\n    description: 'Trigram matching for text similarity',\n    available: true,\n    detectionPatterns: [\n      /\\bsimilarity\\s*\\(/i,\n      /\\bshow_trgm\\s*\\(/i,\n      /\\bword_similarity\\s*\\(/i,\n      /\\bstrict_word_similarity\\s*\\(/i,\n      /\\bgin_trgm_ops\\b/i,\n      /\\bgist_trgm_ops\\b/i,\n      /%>/,\n      /<%/,\n      /<<%/,\n      /%>>/,\n    ],\n    functions: ['similarity', 'show_trgm', 'word_similarity', 'strict_word_similarity'],\n    operators: ['%', '%>', '<%', '<<%', '%>>'],\n  },\n\n  pgcrypto: {\n    name: 'pgcrypto',\n    displayName: 'pgcrypto',\n    extensionName: 'pgcrypto',\n    description: 'Cryptographic functions',\n    available: true,\n    detectionPatterns: [\n      /\\bcrypt\\s*\\(/i,\n      /\\bgen_salt\\s*\\(/i,\n      /\\bgen_random_uuid\\s*\\(/i,\n      /\\bgen_random_bytes\\s*\\(/i,\n      /\\bdigest\\s*\\(/i,\n      /\\bhmac\\s*\\(/i,\n      /\\bencrypt\\s*\\(/i,\n      /\\bdecrypt\\s*\\(/i,\n      /\\bpgp_sym_encrypt\\s*\\(/i,\n      /\\bpgp_sym_decrypt\\s*\\(/i,\n      /\\bpgp_pub_encrypt\\s*\\(/i,\n      /\\bpgp_pub_decrypt\\s*\\(/i,\n      /\\barmor\\s*\\(/i,\n      /\\bdearmor\\s*\\(/i,\n    ],\n    functions: [\n      'crypt',\n      'gen_salt',\n      'gen_random_uuid',\n      'gen_random_bytes',\n      'digest',\n      'hmac',\n      'encrypt',\n      'decrypt',\n      'pgp_sym_encrypt',\n      'pgp_sym_decrypt',\n    ],\n  },\n\n  hstore: {\n    name: 'hstore',\n    displayName: 'hstore',\n    extensionName: 'hstore',\n    description: 'Key-value store data type',\n    available: true,\n    detectionPatterns: [\n      /::hstore\\b/i,\n      /\\bhstore\\s*\\(/i,\n      /\\bhstore_to_json\\s*\\(/i,\n      /\\bhstore_to_jsonb\\s*\\(/i,\n      /\\beach\\s*\\(/i,\n      /\\bexist\\s*\\(/i,\n      /\\bdefined\\s*\\(/i,\n      /\\bdelete\\s*\\(/i,\n      /\\bpopulate_record\\s*\\(/i,\n      /->\\s*'[^']+'/,\n      /=>/,\n    ],\n    functions: ['hstore', 'hstore_to_json', 'hstore_to_jsonb', 'each', 'exist', 'defined'],\n    types: ['hstore'],\n    operators: ['->', '=>', '?', '?&', '?|', '@>', '<@'],\n  },\n\n  citext: {\n    name: 'citext',\n    displayName: 'citext',\n    extensionName: 'citext',\n    description: 'Case-insensitive text data type',\n    available: true,\n    detectionPatterns: [/::citext\\b/i, /\\bcitext\\b/i],\n    types: ['citext'],\n  },\n\n  'uuid-ossp': {\n    name: 'uuid-ossp',\n    displayName: 'uuid-ossp',\n    extensionName: 'uuid-ossp',\n    description: 'UUID generation functions',\n    available: true,\n    detectionPatterns: [\n      /\\buuid_generate_v1\\s*\\(/i,\n      /\\buuid_generate_v1mc\\s*\\(/i,\n      /\\buuid_generate_v3\\s*\\(/i,\n      /\\buuid_generate_v4\\s*\\(/i,\n      /\\buuid_generate_v5\\s*\\(/i,\n      /\\buuid_nil\\s*\\(/i,\n      /\\buuid_ns_dns\\s*\\(/i,\n      /\\buuid_ns_url\\s*\\(/i,\n      /\\buuid_ns_oid\\s*\\(/i,\n      /\\buuid_ns_x500\\s*\\(/i,\n    ],\n    functions: [\n      'uuid_generate_v1',\n      'uuid_generate_v1mc',\n      'uuid_generate_v3',\n      'uuid_generate_v4',\n      'uuid_generate_v5',\n      'uuid_nil',\n    ],\n  },\n\n  cube: {\n    name: 'cube',\n    displayName: 'cube',\n    extensionName: 'cube',\n    description: 'Multi-dimensional cube data type',\n    available: true,\n    detectionPatterns: [/::cube\\b/i, /\\bcube\\s*\\(/i, /\\bcube_distance\\s*\\(/i, /\\bcube_dim\\s*\\(/i],\n    functions: ['cube', 'cube_distance', 'cube_dim', 'cube_ll_coord', 'cube_ur_coord'],\n    types: ['cube'],\n  },\n\n  earthdistance: {\n    name: 'earthdistance',\n    displayName: 'earthdistance',\n    extensionName: 'earthdistance',\n    description: 'Earth distance calculations using cube',\n    available: true,\n    dependencies: ['cube'],\n    detectionPatterns: [\n      /\\bearth\\s*\\(/i,\n      /\\bearth_distance\\s*\\(/i,\n      /\\bearth_box\\s*\\(/i,\n      /\\bll_to_earth\\s*\\(/i,\n      /\\blatitude\\s*\\(/i,\n      /\\blongitude\\s*\\(/i,\n      /\\bgc_to_sec\\s*\\(/i,\n      /\\bsec_to_gc\\s*\\(/i,\n    ],\n    functions: [\n      'earth',\n      'earth_distance',\n      'earth_box',\n      'll_to_earth',\n      'latitude',\n      'longitude',\n      'gc_to_sec',\n      'sec_to_gc',\n    ],\n  },\n\n  fuzzystrmatch: {\n    name: 'fuzzystrmatch',\n    displayName: 'fuzzystrmatch',\n    extensionName: 'fuzzystrmatch',\n    description: 'Fuzzy string matching functions',\n    available: true,\n    detectionPatterns: [\n      /\\bsoundex\\s*\\(/i,\n      /\\bdifference\\s*\\(/i,\n      /\\blevenshtein\\s*\\(/i,\n      /\\blevenshtein_less_equal\\s*\\(/i,\n      /\\bmetaphone\\s*\\(/i,\n      /\\bdmetaphone\\s*\\(/i,\n    ],\n    functions: ['soundex', 'difference', 'levenshtein', 'levenshtein_less_equal', 'metaphone', 'dmetaphone'],\n  },\n\n  intarray: {\n    name: 'intarray',\n    displayName: 'intarray',\n    extensionName: 'intarray',\n    description: 'Integer array functions and operators',\n    available: true,\n    detectionPatterns: [\n      /\\bicount\\s*\\(/i,\n      /\\bsort\\s*\\(/i,\n      /\\bsort_asc\\s*\\(/i,\n      /\\bsort_desc\\s*\\(/i,\n      /\\buniq\\s*\\(/i,\n      /\\bidx\\s*\\(/i,\n      /\\bsubarray\\s*\\(/i,\n      /\\bintset\\s*\\(/i,\n    ],\n    functions: ['icount', 'sort', 'sort_asc', 'sort_desc', 'uniq', 'idx', 'subarray', 'intset'],\n  },\n\n  ltree: {\n    name: 'ltree',\n    displayName: 'ltree',\n    extensionName: 'ltree',\n    description: 'Hierarchical tree-like data',\n    available: true,\n    detectionPatterns: [\n      /::ltree\\b/i,\n      /\\bltree\\s*\\(/i,\n      /\\blquery\\b/i,\n      /\\bltxtquery\\b/i,\n      /\\bsubltree\\s*\\(/i,\n      /\\bsubpath\\s*\\(/i,\n      /\\bnlevel\\s*\\(/i,\n      /\\bindex\\s*\\(/i,\n      /\\blca\\s*\\(/i,\n      /@>/,\n      /<@/,\n      /~/,\n      /\\?/,\n    ],\n    functions: ['subltree', 'subpath', 'nlevel', 'index', 'lca'],\n    types: ['ltree', 'lquery', 'ltxtquery'],\n  },\n\n  tablefunc: {\n    name: 'tablefunc',\n    displayName: 'tablefunc',\n    extensionName: 'tablefunc',\n    description: 'Table-returning functions including crosstab',\n    available: true,\n    detectionPatterns: [\n      /\\bcrosstab\\s*\\(/i,\n      /\\bcrosstab2\\s*\\(/i,\n      /\\bcrosstab3\\s*\\(/i,\n      /\\bcrosstab4\\s*\\(/i,\n      /\\bconnectby\\s*\\(/i,\n      /\\bnormal_rand\\s*\\(/i,\n    ],\n    functions: ['crosstab', 'crosstab2', 'crosstab3', 'crosstab4', 'connectby', 'normal_rand'],\n  },\n\n  unaccent: {\n    name: 'unaccent',\n    displayName: 'unaccent',\n    extensionName: 'unaccent',\n    description: 'Remove accents from text',\n    available: true,\n    detectionPatterns: [/\\bunaccent\\s*\\(/i],\n    functions: ['unaccent'],\n  },\n\n  btree_gin: {\n    name: 'btree_gin',\n    displayName: 'btree_gin',\n    extensionName: 'btree_gin',\n    description: 'GIN operator classes for btree-indexable data types',\n    available: true,\n    detectionPatterns: [/\\bUSING\\s+gin\\s*\\(/i, /\\bgin_btree_ops\\b/i],\n  },\n\n  btree_gist: {\n    name: 'btree_gist',\n    displayName: 'btree_gist',\n    extensionName: 'btree_gist',\n    description: 'GiST operator classes for btree-indexable data types',\n    available: true,\n    detectionPatterns: [/\\bUSING\\s+gist\\s*\\(/i, /\\bgist_btree_ops\\b/i],\n  },\n\n  bloom: {\n    name: 'bloom',\n    displayName: 'bloom',\n    extensionName: 'bloom',\n    description: 'Bloom filter index access method',\n    available: true,\n    detectionPatterns: [/\\bUSING\\s+bloom\\s*\\(/i, /\\bbloom_ops\\b/i],\n  },\n}\n\n// ============================================================================\n// Extension Detection\n// ============================================================================\n\n/**\n * Detect which extensions are needed for a SQL query\n *\n * Analyzes the SQL string and returns a list of extensions that should be\n * loaded before executing the query.\n *\n * @param sql - The SQL query to analyze\n * @returns Array of extension names that were detected\n *\n * @example\n * ```typescript\n * const extensions = detectExtensions(\"SELECT embedding <-> '[1,2,3]' FROM items\")\n * // Returns: ['vector']\n *\n * const extensions = detectExtensions(\"SELECT similarity(name, 'test') FROM users\")\n * // Returns: ['pg_trgm']\n * ```\n */\nexport function detectExtensions(sql: string): ExtensionName[] {\n  const detected = new Set<ExtensionName>()\n\n  for (const [name, metadata] of Object.entries(EXTENSION_REGISTRY)) {\n    for (const pattern of metadata.detectionPatterns) {\n      if (pattern.test(sql)) {\n        detected.add(name as ExtensionName)\n        break // No need to check more patterns for this extension\n      }\n    }\n  }\n\n  return Array.from(detected)\n}\n\n// ============================================================================\n// LazyExtensionLoader Class\n// ============================================================================\n\n/**\n * Lazy extension loader for PostgreSQL\n *\n * Loads extensions on-demand when they are first needed, improving cold start\n * times by avoiding loading all extensions at initialization.\n *\n * @example\n * ```typescript\n * const loader = new LazyExtensionLoader({\n *   query: async (sql) => pg.exec(sql)\n * })\n *\n * // Load a specific extension\n * await loader.ensureLoaded('vector')\n *\n * // Auto-detect and load from SQL\n * await loader.executeWithAutoLoad(\"SELECT embedding <-> '[1,2,3]' FROM items\")\n *\n * // Get statistics\n * console.log(loader.getStats())\n * ```\n */\nexport class LazyExtensionLoader {\n  private executor: QueryExecutor\n  private options: Required<LazyExtensionLoaderOptions>\n  private loadedExtensions: Set<ExtensionName> = new Set()\n  private loadingPromises: Map<ExtensionName, Promise<ExtensionLoadResult>> = new Map()\n  private loadTimes: Map<ExtensionName, number> = new Map()\n  private cacheHits: number = 0\n  private autoDetectionCount: number = 0\n\n  constructor(executor: QueryExecutor, options: LazyExtensionLoaderOptions = {}) {\n    this.executor = executor\n    this.options = {\n      debug: options.debug ?? false,\n      disableAutoDetection: options.disableAutoDetection ?? false,\n      loadTimeoutMs: options.loadTimeoutMs ?? 10000,\n    }\n  }\n\n  /**\n   * Ensure an extension is loaded, loading it if necessary\n   *\n   * @param name - The extension to load\n   * @returns Result of the load operation\n   */\n  async ensureLoaded(name: ExtensionName): Promise<ExtensionLoadResult> {\n    // Check if already loaded\n    if (this.loadedExtensions.has(name)) {\n      this.cacheHits++\n      return {\n        name,\n        success: true,\n        wasAlreadyLoaded: true,\n        loadTimeMs: 0,\n      }\n    }\n\n    // Check if currently loading (dedup concurrent requests)\n    const existingPromise = this.loadingPromises.get(name)\n    if (existingPromise) {\n      return existingPromise\n    }\n\n    // Start loading\n    const loadPromise = this.loadExtension(name)\n    this.loadingPromises.set(name, loadPromise)\n\n    try {\n      const result = await loadPromise\n      return result\n    } finally {\n      this.loadingPromises.delete(name)\n    }\n  }\n\n  /**\n   * Internal method to load an extension\n   */\n  private async loadExtension(name: ExtensionName): Promise<ExtensionLoadResult> {\n    const startTime = performance.now()\n\n    // Check if extension exists in registry\n    const metadata = EXTENSION_REGISTRY[name]\n    if (!metadata) {\n      return {\n        name,\n        success: false,\n        wasAlreadyLoaded: false,\n        loadTimeMs: performance.now() - startTime,\n        error: `Unknown extension: ${name}`,\n      }\n    }\n\n    // Load dependencies first\n    if (metadata.dependencies) {\n      for (const dep of metadata.dependencies) {\n        const depResult = await this.ensureLoaded(dep)\n        if (!depResult.success) {\n          return {\n            name,\n            success: false,\n            wasAlreadyLoaded: false,\n            loadTimeMs: performance.now() - startTime,\n            error: `Failed to load dependency \"${dep}\": ${depResult.error}`,\n          }\n        }\n      }\n    }\n\n    try {\n      // Create the extension\n      const sql = `CREATE EXTENSION IF NOT EXISTS \"${metadata.extensionName}\"`\n      await this.executor.query(sql)\n\n      const loadTimeMs = performance.now() - startTime\n      this.loadedExtensions.add(name)\n      this.loadTimes.set(name, loadTimeMs)\n\n      if (this.options.debug) {\n        console.log(`[LazyExtensionLoader] Loaded extension \"${name}\" in ${loadTimeMs.toFixed(2)}ms`)\n      }\n\n      return {\n        name,\n        success: true,\n        wasAlreadyLoaded: false,\n        loadTimeMs,\n      }\n    } catch (error) {\n      const errorMessage = error instanceof Error ? error.message : String(error)\n\n      if (this.options.debug) {\n        console.error(`[LazyExtensionLoader] Failed to load extension \"${name}\":`, error)\n      }\n\n      return {\n        name,\n        success: false,\n        wasAlreadyLoaded: false,\n        loadTimeMs: performance.now() - startTime,\n        error: errorMessage,\n      }\n    }\n  }\n\n  /**\n   * Load multiple extensions\n   *\n   * @param names - Array of extension names to load\n   * @returns Array of load results\n   */\n  async loadExtensions(names: ExtensionName[]): Promise<ExtensionLoadResult[]> {\n    // Sort by dependencies - extensions without dependencies first\n    const sorted = this.sortByDependencies(names)\n\n    const results: ExtensionLoadResult[] = []\n    for (const name of sorted) {\n      const result = await this.ensureLoaded(name)\n      results.push(result)\n    }\n\n    return results\n  }\n\n  /**\n   * Sort extensions by dependencies (topological sort)\n   */\n  private sortByDependencies(names: ExtensionName[]): ExtensionName[] {\n    const nameSet = new Set(names)\n    const sorted: ExtensionName[] = []\n    const visited = new Set<ExtensionName>()\n\n    const visit = (name: ExtensionName) => {\n      if (visited.has(name)) return\n      visited.add(name)\n\n      const metadata = EXTENSION_REGISTRY[name]\n      if (metadata?.dependencies) {\n        for (const dep of metadata.dependencies) {\n          if (nameSet.has(dep) || !this.loadedExtensions.has(dep)) {\n            nameSet.add(dep)\n            visit(dep)\n          }\n        }\n      }\n\n      sorted.push(name)\n    }\n\n    for (const name of names) {\n      visit(name)\n    }\n\n    return sorted\n  }\n\n  /**\n   * Detect extensions needed for a SQL query\n   *\n   * @param sql - The SQL query to analyze\n   * @returns Array of extension names detected\n   */\n  detectExtensions(sql: string): ExtensionName[] {\n    this.autoDetectionCount++\n    return detectExtensions(sql)\n  }\n\n  /**\n   * Execute a query with automatic extension loading\n   *\n   * Analyzes the SQL, loads any detected extensions, then executes the query.\n   *\n   * @param sql - The SQL query to execute\n   * @param params - Optional query parameters\n   * @returns Result including loaded extensions and query result\n   */\n  async executeWithAutoLoad(sql: string, params?: unknown[]): Promise<ExecuteWithAutoLoadResult> {\n    const extensionsLoaded: ExtensionName[] = []\n    let extensionLoadTimeMs = 0\n\n    // Detect and load extensions if auto-detection is enabled\n    if (!this.options.disableAutoDetection) {\n      const detected = this.detectExtensions(sql)\n\n      for (const name of detected) {\n        if (!this.loadedExtensions.has(name)) {\n          // Note: performance timing handled by ensureLoaded\n          const result = await this.ensureLoaded(name)\n          if (result.success && !result.wasAlreadyLoaded) {\n            extensionsLoaded.push(name)\n            extensionLoadTimeMs += result.loadTimeMs\n          }\n        }\n      }\n    }\n\n    // Execute the query\n    const result = await this.executor.query(sql, params)\n\n    return {\n      extensionsLoaded,\n      result,\n      extensionLoadTimeMs,\n    }\n  }\n\n  /**\n   * Preload a set of extensions\n   *\n   * Useful for preloading extensions you know will be needed.\n   *\n   * @param names - Array of extension names to preload\n   * @returns Array of load results\n   */\n  async preload(names: ExtensionName[]): Promise<ExtensionLoadResult[]> {\n    return this.loadExtensions(names)\n  }\n\n  /**\n   * Check if an extension is loaded\n   *\n   * @param name - The extension name to check\n   * @returns True if the extension is loaded\n   */\n  isLoaded(name: ExtensionName): boolean {\n    return this.loadedExtensions.has(name)\n  }\n\n  /**\n   * Get list of loaded extensions\n   *\n   * @returns Array of loaded extension names\n   */\n  getLoadedExtensions(): ExtensionName[] {\n    return Array.from(this.loadedExtensions)\n  }\n\n  /**\n   * Get loading statistics\n   *\n   * @returns Statistics about extension loading\n   */\n  getStats(): ExtensionLoaderStats {\n    return {\n      totalLoaded: this.loadedExtensions.size,\n      loadedExtensions: Array.from(this.loadedExtensions),\n      cacheHits: this.cacheHits,\n      loadTimes: Object.fromEntries(this.loadTimes),\n      autoDetectionCount: this.autoDetectionCount,\n    }\n  }\n\n  /**\n   * Reset the loader state\n   *\n   * Clears loaded extensions and statistics. Useful for testing or\n   * when you need to reload extensions (e.g., after a database reset).\n   */\n  reset(): void {\n    this.loadedExtensions.clear()\n    this.loadingPromises.clear()\n    this.loadTimes.clear()\n    this.cacheHits = 0\n    this.autoDetectionCount = 0\n  }\n\n  // ============================================================================\n  // Static Methods\n  // ============================================================================\n\n  /**\n   * Get metadata for an extension\n   *\n   * @param name - The extension name\n   * @returns Extension metadata or undefined if not found\n   */\n  static getMetadata(name: ExtensionName): ExtensionMetadata | undefined {\n    return EXTENSION_REGISTRY[name]\n  }\n\n  /**\n   * Get all available extensions\n   *\n   * @returns Array of extension metadata for available extensions\n   */\n  static getAvailableExtensions(): ExtensionMetadata[] {\n    return Object.values(EXTENSION_REGISTRY).filter((m) => m.available)\n  }\n\n  /**\n   * Check if a SQL query would trigger detection of a specific extension\n   *\n   * @param sql - The SQL query to check\n   * @param extensionName - The extension to check for\n   * @returns True if the extension would be detected\n   */\n  static wouldDetect(sql: string, extensionName: ExtensionName): boolean {\n    const detected = detectExtensions(sql)\n    return detected.includes(extensionName)\n  }\n}\n\n// ============================================================================\n// Factory Function\n// ============================================================================\n\n/**\n * Create a LazyExtensionLoader instance\n *\n * @param executor - Query executor for running SQL\n * @param options - Loader options\n * @returns New LazyExtensionLoader instance\n *\n * @example\n * ```typescript\n * const loader = createLazyExtensionLoader({\n *   query: async (sql) => pg.exec(sql)\n * })\n *\n * await loader.ensureLoaded('vector')\n * ```\n */\nexport function createLazyExtensionLoader(\n  executor: QueryExecutor,\n  options?: LazyExtensionLoaderOptions\n): LazyExtensionLoader {\n  return new LazyExtensionLoader(executor, options)\n}\n"],"mappings":"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","names":["result"]}