{"version":3,"file":"index.cjs","sources":["../../../src/core/index.ts"],"sourcesContent":["import type {\n  //\n  FuzzyIndex,\n  FuzzyConfig,\n  SuggestionResult,\n  SearchMatch,\n  BuildIndexOptions,\n  SearchOptions,\n  LanguageProcessor,\n} from \"./types.js\";\nimport {\n  //\n  mergeConfig,\n  validateConfig,\n  DEFAULT_MATCH_TYPE_SCORES,\n  DEFAULT_SCORING_MODIFIERS,\n} from \"./config.js\";\nimport {\n  //\n  LanguageRegistry,\n} from \"../languages/index.js\";\nimport { calculateLevenshteinDistance, calculateNgramSimilarity } from \"../algorithms/levenshtein.js\";\nimport {\n  findExactMatches,\n  findPrefixMatches,\n  findSubstringMatches,\n  findPhoneticMatches,\n  findSynonymMatches,\n  findNgramMatches,\n  findFuzzyMatches,\n} from \"./matchers.js\";\nimport {\n  buildInvertedIndex,\n  searchInvertedIndex,\n  calculateBM25Scores,\n} from \"./inverted-index.js\";\nimport {\n  //\n  calculateHighlights,\n} from \"./highlighting.js\";\nimport {\n  //\n  SearchCache,\n} from \"./cache.js\";\nimport { removeAccents } from \"../utils/accent-normalization.js\";\nimport { extractFieldValues, normalizeFieldWeights } from \"./field-weighting.js\";\nimport { filterStopWords } from \"../utils/stop-words.js\";\nimport { parseQuery } from \"../utils/phrase-parser.js\";\nimport { matchPhrase } from \"./phrase-matching.js\";\nimport { detectLanguages, sampleTextForDetection } from \"../utils/language-detection.js\";\nimport { isFQLQuery, executeFQLQuery } from \"../fql/index.js\";\nimport { isAlphanumeric, extractAlphaPart, extractNumericPart } from \"../utils/alphanumeric-segmenter.js\";\nimport { applyFilters } from \"./filters.js\";\nimport { applySorting } from \"./sorting.js\";\nimport { StringPool } from \"../utils/string-pool.js\";\n\n/**\n * Builds a fuzzy search index from an array of words or objects.\n * \n * This is the primary function for creating a searchable index. It processes each word/object\n * through language-specific processors, builds various indices (phonetic, n-gram, synonym),\n * and automatically enables optimizations like inverted index for large datasets (10k+ items).\n * \n * @param words - Array of strings to index, or objects with fields to search across\n * @param options - Configuration options for index building\n * @param options.config - Fuzzy search configuration (languages, features, thresholds)\n * @param options.languageProcessors - Custom language processors (overrides default)\n * @param options.onProgress - Callback for tracking indexing progress (processed, total)\n * @param options.useInvertedIndex - Force inverted index usage (auto-enabled for 10k+ words)\n * @param options.fields - Field names for multi-field search (required when indexing objects)\n * @param options.fieldWeights - Weight multipliers for field scoring (e.g., {title: 2.0, description: 1.0})\n * \n * @returns A searchable fuzzy index containing all processed data and metadata\n * \n * @throws {Error} If no language processors found for specified languages\n * @throws {Error} If objects are provided without specifying fields via options.fields\n * \n * @example\n * ```typescript\n * // Simple string array\n * const index = buildFuzzyIndex(['apple', 'banana', 'cherry'], {\n *   config: { languages: ['english'], performance: 'fast' }\n * });\n * \n * // Multi-field objects\n * const products = [\n *   { name: 'iPhone', description: 'Smartphone', price: 999 },\n *   { name: 'MacBook', description: 'Laptop', price: 1999 }\n * ];\n * const index = buildFuzzyIndex(products, {\n *   fields: ['name', 'description'],\n *   fieldWeights: { name: 2.0, description: 1.0 }\n * });\n * \n * // With progress tracking\n * const index = buildFuzzyIndex(largeDataset, {\n *   onProgress: (processed, total) => {\n *     console.log(`Indexing: ${(processed/total*100).toFixed(1)}%`);\n *   }\n * });\n * ```\n * \n * @see {@link getSuggestions} for searching the index\n * @see {@link BuildIndexOptions} for all configuration options\n * @see {@link FuzzyConfig} for fuzzy search settings\n */\nexport function buildFuzzyIndex(words: (string | any)[] = [], options: BuildIndexOptions = {}): FuzzyIndex {\n  // AUTO-DETECTION: Detect languages if not explicitly specified\n  const userSpecifiedLanguages = options.config?.languages;\n  const shouldAutoDetect = !userSpecifiedLanguages || userSpecifiedLanguages.includes('auto');\n  \n  const config = mergeConfig(options.config);\n  \n  if (shouldAutoDetect) {\n    const sampleText = sampleTextForDetection(words, 100);\n    const detectedLanguages = detectLanguages(sampleText);\n    config.languages = detectedLanguages;\n  }\n  \n  validateConfig(config);\n\n  // Convert features array to Set for O(1) lookup performance\n  const featureSet = new Set(config.features);\n\n  const languageProcessors = options.languageProcessors || LanguageRegistry.getProcessors(config.languages);\n\n  if (languageProcessors.length === 0) {\n    throw new Error(`No language processors found for: ${config.languages.join(\", \")}`);\n  }\n\n  // Check if we're doing multi-field search\n  const hasFields = options.fields && options.fields.length > 0;\n  const isObjectArray = words.length > 0 && typeof words[0] === \"object\" && words[0] !== null;\n\n  // Validate: if objects are provided, fields must be specified\n  if (isObjectArray && !hasFields) {\n    throw new Error(\"When indexing objects, you must specify which fields to index via options.fields\");\n  }\n\n  // OPTIMIZATION: String pooling for memory deduplication\n  // Reuses string instances across the index to reduce memory footprint\n  const stringPool = new StringPool();\n\n  const index: FuzzyIndex = {\n    base: [],\n    variantToBase: new Map(),\n    phoneticToBase: new Map(),\n    ngramIndex: new Map(),\n    synonymMap: new Map(),\n    languageProcessors: new Map(),\n    config,\n  };\n\n  // Store field configuration if provided\n  if (hasFields) {\n    index.fields = options.fields;\n    index.fieldWeights = normalizeFieldWeights(options.fields!, options.fieldWeights);\n    index.fieldData = new Map();\n  }\n\n  // Store language processors\n  languageProcessors.forEach((processor) => {\n    index.languageProcessors.set(processor.language, processor);\n  });\n\n  // OPTIMIZATION 2: Decide early whether to use inverted index to avoid building redundant structures\n  // Use inverted index for large datasets (10k+) or when explicitly requested\n  // Inverted index provides better memory efficiency and query speed for large datasets\n  const shouldUseInvertedIndex = options.useInvertedIndex || config.useInvertedIndex || config.useBM25 || config.useBloomFilter || words.length >= 10000;\n\n  const processedWords = new Set<string>();\n  let processed = 0;\n\n  // OPTIMIZATION 2: Only build hash maps if NOT using inverted index\n  // This avoids storing the same data twice in different structures\n  if (!shouldUseInvertedIndex) {\n    for (const item of words) {\n      if (!item) continue;\n\n      // Handle multi-field objects\n      if (hasFields && isObjectArray) {\n        const fieldValues = extractFieldValues(item, options.fields);\n        if (!fieldValues) continue;\n\n        // Generate a unique ID for this object (use first field value as base)\n        const baseId = Object.values(fieldValues)[0] || `item_${processed}`;\n\n        // Store field data\n        index.fieldData!.set(baseId, fieldValues);\n\n        // Index each field separately\n        for (const [fieldName, fieldValue] of Object.entries(fieldValues)) {\n          if (!fieldValue || fieldValue.trim().length < config.minQueryLength) continue;\n\n          const trimmedValue = fieldValue.trim();\n\n          // Add to base if not already there\n          if (!processedWords.has(baseId.toLowerCase())) {\n            processedWords.add(baseId.toLowerCase());\n            index.base.push(baseId);\n          }\n\n          // Process this field value with each language processor\n          for (const processor of languageProcessors) {\n            processWordWithProcessorAndField(trimmedValue, baseId, fieldName, processor, index, config, featureSet);\n          }\n        }\n      } else {\n        // Handle simple string array (backwards compatible)\n        const word = typeof item === \"string\" ? item : String(item);\n        if (word.trim().length < config.minQueryLength) continue;\n\n        const trimmedWord = word.trim();\n        if (processedWords.has(trimmedWord.toLowerCase())) continue;\n\n        processedWords.add(trimmedWord.toLowerCase());\n        // Use string pool to deduplicate strings and reduce memory\n        index.base.push(stringPool.intern(trimmedWord));\n\n        // Process with each language processor\n        for (const processor of languageProcessors) {\n          processWordWithProcessor(trimmedWord, processor, index, config, featureSet);\n        }\n      }\n\n      processed++;\n      if (options.onProgress) {\n        options.onProgress(processed, words.length);\n      }\n    }\n  }\n\n  // INVERTED INDEX: Build for large datasets (contains all the data we need)\n  if (shouldUseInvertedIndex) {\n    const { invertedIndex, documents } = buildInvertedIndex(words, languageProcessors, config, featureSet);\n    index.invertedIndex = invertedIndex;\n    index.documents = documents;\n    \n    // Populate base array from documents for compatibility\n    index.base = documents.map(doc => doc.word);\n  }\n\n  // CACHE: Initialize search result cache if enabled (default: true)\n  if (config.enableCache !== false) {\n    const cacheSize = config.cacheSize || 100;\n    index._cache = new SearchCache(cacheSize);\n  }\n\n  return index;\n}\n\n/**\n * Process a word with a specific language processor\n */\nfunction processWordWithProcessor(word: string, processor: LanguageProcessor, index: FuzzyIndex, config: FuzzyConfig, featureSet: Set<string>): void {\n  const normalized = processor.normalize(word);\n\n  // OPTIMIZATION: Store only lowercase normalized form to eliminate duplicates\n  // All case variations (apple, Apple, APPLE) map to same lowercase key\n  addToVariantMap(index.variantToBase, normalized.toLowerCase(), word);\n\n  // Add accent-insensitive variants (also normalized to lowercase)\n  const accentFreeWord = removeAccents(word);\n  if (accentFreeWord !== word) {\n    const normalizedAccentFree = processor.normalize(accentFreeWord).toLowerCase();\n    // Only add if different from the already-stored normalized form\n    if (normalizedAccentFree !== normalized.toLowerCase()) {\n      addToVariantMap(index.variantToBase, normalizedAccentFree, word);\n    }\n  }\n\n  // Generate and index variants (normalized to lowercase)\n  if (featureSet.has(\"partial-words\")) {\n    const variants = processor.getWordVariants(word, config.performance);\n    variants.forEach((variant) => {\n      addToVariantMap(index.variantToBase, variant.toLowerCase(), word);\n    });\n  }\n\n  // Generate phonetic codes\n  if (featureSet.has(\"phonetic\") && processor.supportedFeatures.includes(\"phonetic\")) {\n    const phoneticCode = processor.getPhoneticCode(word);\n    if (phoneticCode) {\n      addToVariantMap(index.phoneticToBase, phoneticCode, word);\n    }\n  }\n\n  // Generate n-grams for partial matching (normalized to lowercase)\n  // OPTIMIZATION 3: Skip n-grams for very short words (≤3 chars) - fuzzy matching handles these well\n  // Also limit n-gram generation in fast mode to reduce index size\n  if (normalized.length > 3) {\n    const shouldLimitNgrams = config.performance === 'fast' && normalized.length > 10;\n    const ngramSource = shouldLimitNgrams ? normalized.substring(0, 15) : normalized;\n    const ngrams = generateNgrams(ngramSource.toLowerCase(), config.ngramSize);\n    ngrams.forEach((ngram: string) => {\n      addToVariantMap(index.ngramIndex, ngram, word);\n    });\n  }\n\n  // Handle compound words (normalized to lowercase)\n  if (featureSet.has(\"compound\") && processor.supportedFeatures.includes(\"compound\")) {\n    const compoundParts = processor.splitCompoundWords(word);\n    compoundParts.forEach((part) => {\n      if (part !== word) {\n        addToVariantMap(index.variantToBase, processor.normalize(part).toLowerCase(), word);\n      }\n    });\n  }\n\n  // Add synonyms (normalized to lowercase)\n  if (featureSet.has(\"synonyms\")) {\n    const synonyms = processor.getSynonyms(normalized);\n    synonyms.forEach((synonym) => {\n      addToVariantMap(index.synonymMap, synonym.toLowerCase(), word);\n    });\n\n    // Add custom synonyms\n    if (config.customSynonyms) {\n      const customSynonyms = config.customSynonyms[normalized.toLowerCase()];\n      if (customSynonyms) {\n        customSynonyms.forEach((synonym) => {\n          addToVariantMap(index.synonymMap, synonym.toLowerCase(), word);\n        });\n      }\n    }\n  }\n}\n\n/**\n * Process a word with field information for multi-field search\n */\nfunction processWordWithProcessorAndField(fieldValue: string, baseId: string, fieldName: string, processor: LanguageProcessor, index: FuzzyIndex, config: FuzzyConfig, featureSet: Set<string>): void {\n  const normalized = processor.normalize(fieldValue);\n\n  // OPTIMIZATION: Store only lowercase normalized form to eliminate duplicates\n  addToVariantMapWithField(index.variantToBase, normalized.toLowerCase(), baseId, fieldName);\n\n  // Add accent-insensitive variants (normalized to lowercase)\n  const accentFreeWord = removeAccents(fieldValue);\n  if (accentFreeWord !== fieldValue) {\n    const normalizedAccentFree = processor.normalize(accentFreeWord).toLowerCase();\n    if (normalizedAccentFree !== normalized.toLowerCase()) {\n      addToVariantMapWithField(index.variantToBase, normalizedAccentFree, baseId, fieldName);\n    }\n  }\n\n  // Generate and index variants (normalized to lowercase)\n  if (featureSet.has(\"partial-words\")) {\n    const variants = processor.getWordVariants(fieldValue, config.performance);\n    variants.forEach((variant) => {\n      addToVariantMapWithField(index.variantToBase, variant.toLowerCase(), baseId, fieldName);\n    });\n  }\n\n  // Generate phonetic codes\n  if (featureSet.has(\"phonetic\") && processor.supportedFeatures.includes(\"phonetic\")) {\n    const phoneticCode = processor.getPhoneticCode(fieldValue);\n    if (phoneticCode) {\n      addToVariantMapWithField(index.phoneticToBase, phoneticCode, baseId, fieldName);\n    }\n  }\n\n  // Generate n-grams for partial matching (normalized to lowercase)\n  // OPTIMIZATION 3: Skip n-grams for very short words (≤3 chars) - fuzzy matching handles these well\n  // Also limit n-gram generation in fast mode to reduce index size\n  if (normalized.length > 3) {\n    const shouldLimitNgrams = config.performance === 'fast' && normalized.length > 15;\n    const ngramSource = shouldLimitNgrams ? normalized.substring(0, 15) : normalized;\n    const ngrams = generateNgrams(ngramSource.toLowerCase(), config.ngramSize);\n    ngrams.forEach((ngram: string) => {\n      addToVariantMapWithField(index.ngramIndex, ngram, baseId, fieldName);\n    });\n  }\n\n  // Handle compound words (normalized to lowercase)\n  if (featureSet.has(\"compound\") && processor.supportedFeatures.includes(\"compound\")) {\n    const parts = processor.splitCompoundWords(fieldValue);\n    parts.forEach((part) => {\n      if (part.length >= config.minQueryLength) {\n        addToVariantMapWithField(index.variantToBase, processor.normalize(part).toLowerCase(), baseId, fieldName);\n      }\n    });\n  }\n\n  // Add synonyms (normalized to lowercase)\n  if (featureSet.has(\"synonyms\")) {\n    const synonyms = processor.getSynonyms(normalized);\n    synonyms.forEach((synonym) => {\n      addToVariantMapWithField(index.synonymMap, synonym.toLowerCase(), baseId, fieldName);\n    });\n\n    // Add custom synonyms\n    if (config.customSynonyms) {\n      const customSynonyms = config.customSynonyms[normalized.toLowerCase()];\n      if (customSynonyms) {\n        customSynonyms.forEach((synonym) => {\n          addToVariantMapWithField(index.synonymMap, synonym.toLowerCase(), baseId, fieldName);\n        });\n      }\n    }\n  }\n}\n\n/**\n * Helper function to add mappings to variant maps with field information\n */\nfunction addToVariantMapWithField(map: Map<string, Set<string>>, key: string, value: string, _fieldName: string): void {\n  // For now, we'll use a simple approach: store the value with field metadata\n  // The field information will be tracked separately in the index\n  // _fieldName is prefixed with _ to indicate it's reserved for future use\n  if (!map.has(key)) {\n    map.set(key, new Set());\n  }\n  map.get(key)!.add(value);\n}\n\n/**\n * Helper function to add mappings to variant maps\n */\nfunction addToVariantMap(map: Map<string, Set<string>>, key: string, value: string): void {\n  if (!map.has(key)) {\n    map.set(key, new Set());\n  }\n  map.get(key)!.add(value);\n}\n\n/**\n * Searches multiple queries at once with automatic deduplication.\n * \n * This function efficiently processes multiple search queries by deduplicating identical\n * queries and leveraging the search cache. Perfect for batch processing or multi-field forms.\n * \n * @param index - The fuzzy search index to search against\n * @param queries - Array of search query strings\n * @param maxResults - Maximum results per query (optional, defaults to index config)\n * @param options - Search options to apply to all queries\n * \n * @returns Object mapping each unique query to its search results\n * \n * @example\n * ```typescript\n * const results = batchSearch(index, ['apple', 'banana', 'apple', 'cherry']);\n * // Returns: { apple: [...], banana: [...], cherry: [...] }\n * // Note: 'apple' only searched once despite appearing twice\n * \n * // With options\n * const results = batchSearch(index, ['app', 'ban'], 5, {\n *   includeHighlights: true,\n *   fuzzyThreshold: 0.8\n * });\n * ```\n * \n * @see {@link getSuggestions} for single query search\n */\nexport function batchSearch(index: FuzzyIndex, queries: string[], maxResults?: number, options: SearchOptions = {}): Record<string, SuggestionResult[]> {\n  const results: Record<string, SuggestionResult[]> = {};\n  const uniqueQueries = [...new Set(queries)]; // Deduplicate\n\n  for (const query of uniqueQueries) {\n    results[query] = getSuggestions(index, query, maxResults, options);\n  }\n\n  return results;\n}\n\n/**\n * Searches the index for fuzzy matches to the query string.\n * \n * This is the primary search function. It automatically selects the optimal search strategy\n * (inverted index for large datasets, hash-based for smaller ones), handles phrase search,\n * FQL queries, stop word filtering, and caching for performance.\n * \n * @param index - The fuzzy search index to search against\n * @param query - The search query string (supports phrases in quotes, FQL with fql() wrapper)\n * @param maxResults - Maximum number of results to return (optional, defaults to index config)\n * @param options - Search options to customize behavior\n * @param options.fuzzyThreshold - Override fuzzy matching threshold (0-1, higher = stricter)\n * @param options.languages - Filter to specific languages\n * @param options.matchTypes - Filter to specific match types (exact, fuzzy, phonetic, etc.)\n * @param options.debug - Include debug information in results\n * @param options.includeHighlights - Include match position highlights for UI rendering\n * @param options.enableFQL - Enable Fuzzy Query Language support (AND, OR, NOT operators)\n * \n * @returns Array of suggestion results sorted by relevance score (highest first)\n * \n * @example\n * ```typescript\n * // Basic search\n * const results = getSuggestions(index, 'hospitl', 5);\n * // Returns: [{ display: 'Hospital', score: 0.92, ... }]\n * \n * // With highlights for UI\n * const results = getSuggestions(index, 'app', 10, {\n *   includeHighlights: true\n * });\n * // Results include highlight positions for rendering\n * \n * // Phrase search\n * const results = getSuggestions(index, '\"new york\"');\n * // Finds multi-word phrases\n * \n * // FQL query\n * const results = getSuggestions(index, 'fql(doctor AND berlin)', 10, {\n *   enableFQL: true\n * });\n * \n * // With debug info\n * const results = getSuggestions(index, 'query', 5, { debug: true });\n * // Results include timing and match details\n * ```\n * \n * @see {@link buildFuzzyIndex} for creating the index\n * @see {@link batchSearch} for searching multiple queries\n */\nexport function getSuggestions(index: FuzzyIndex, query: string, maxResults?: number, options: SearchOptions = {}): SuggestionResult[] {\n  const config = index.config;\n  const limit = maxResults || options.maxResults || config.maxResults;\n  const threshold = options.fuzzyThreshold || config.fuzzyThreshold;\n\n  if (!query || query.trim().length < config.minQueryLength) {\n    return [];\n  }\n\n  // FQL: Check if FQL is enabled and query is FQL\n  if (options.enableFQL && isFQLQuery(query)) {\n    return executeFQLQuery(index, query, limit, options);\n  }\n\n  // PHRASE SEARCH: Check if query contains phrases\n  const parsedQuery = parseQuery(query);\n  \n  // If query has phrases, use phrase search\n  if (parsedQuery.hasPhrases) {\n    return searchWithPhrases(index, parsedQuery, limit, threshold, options);\n  }\n\n  // STOP WORDS: Filter stop words from query if enabled\n  let processedQuery = query;\n  if (config.enableStopWords && config.stopWords && config.stopWords.length > 0) {\n    processedQuery = filterStopWords(query, config.stopWords);\n  }\n\n  // Early return if processed query is empty after filtering\n  if (!processedQuery || processedQuery.trim().length === 0) {\n    return [];\n  }\n\n  // CACHE: Check cache first (use processed query for cache key)\n  if (index._cache) {\n    const cached = index._cache.get(processedQuery, limit, options);\n    if (cached) {\n      return cached; // Cache hit - return immediately!\n    }\n  }\n\n  // Get active language processors\n  const activeLanguages = options.languages || config.languages;\n  const processors = activeLanguages.map((lang) => index.languageProcessors.get(lang)).filter((p): p is LanguageProcessor => p !== undefined);\n\n  if (processors.length === 0) {\n    return [];\n  }\n\n  // AUTO-DETECTION: Use inverted index if available\n  if (index.invertedIndex && index.documents) {\n    const results = getSuggestionsInverted(index, processedQuery, limit, threshold, processors, options);\n    // Cache the results\n    if (index._cache) {\n      index._cache.set(processedQuery, results, limit, options);\n    }\n    return results;\n  }\n\n  // CLASSIC: Use hash-based approach (existing implementation)\n  const matches = new Map<string, SearchMatch>();\n\n  // Process query with each language processor\n  for (const processor of processors) {\n    const normalizedQuery = processor.normalize(processedQuery.trim());\n\n    // Find matches using different strategies\n    findExactMatches(normalizedQuery, index, matches, processor.language);\n    \n    // Early termination: If we found perfect exact matches and have enough results\n    const exactMatches = Array.from(matches.values()).filter(m => m.matchType === 'exact');\n    if (exactMatches.length >= limit && exactMatches.some(m => m.word === normalizedQuery)) {\n      break; // Perfect match found, no need for other strategies\n    }\n    \n    findPrefixMatches(normalizedQuery, index, matches, processor.language);\n    findSubstringMatches(normalizedQuery, index, matches, processor.language);\n    \n    // Early termination: If we have enough high-quality matches (exact + prefix + substring)\n    const highQualityMatches = Array.from(matches.values()).filter(m => \n      m.matchType === 'exact' || m.matchType === 'prefix' || m.matchType === 'substring'\n    );\n    if (highQualityMatches.length >= limit * 2) {\n      // Continue with other strategies but be more selective\n      findPhoneticMatches(normalizedQuery, processor, index, matches);\n      findSynonymMatches(normalizedQuery, index, matches);\n      \n      // Only add n-gram and fuzzy if we still need more results\n      if (matches.size < limit * 3) {\n        findNgramMatches(normalizedQuery, index, matches, processor.language, config.ngramSize);\n\n        if (config.features.includes(\"missing-letters\") || config.features.includes(\"extra-letters\") || config.features.includes(\"transpositions\")) {\n          findFuzzyMatches(normalizedQuery, index, matches, processor, config);\n        }\n      }\n    } else {\n      // Normal flow when we don't have enough matches yet\n      findPhoneticMatches(normalizedQuery, processor, index, matches);\n      findSynonymMatches(normalizedQuery, index, matches);\n      findNgramMatches(normalizedQuery, index, matches, processor.language, config.ngramSize);\n\n      if (config.features.includes(\"missing-letters\") || config.features.includes(\"extra-letters\") || config.features.includes(\"transpositions\")) {\n        findFuzzyMatches(normalizedQuery, index, matches, processor, config);\n      }\n    }\n  }\n\n  // Convert matches to results and rank them\n  let results = Array.from(matches.values())\n    .map((match) => createSuggestionResult(match, processedQuery, threshold, index, options))\n    .filter((result): result is SuggestionResult => result !== null);\n\n  // Debug logging if enabled\n  if (options.debug) {\n    console.log(`\\n🔍 Debug: Query \"${processedQuery}\"`);\n    console.log(`📊 Total matches found: ${matches.size}`);\n    console.log(`📊 After threshold filter: ${results.length}`);\n    console.log(`\\nTop 10 matches before sorting:`);\n    results.slice(0, 10).forEach((r, i) => {\n      // @ts-ignore - _debug_matchType is added dynamically\n      console.log(`  ${i + 1}. ${r.display} (score: ${r.score.toFixed(2)}, type: ${r._debug_matchType})`);\n    });\n  }\n\n  // Apply filters if provided\n  if (options.filters) {\n    results = applyFilters(results, options.filters);\n  }\n\n  // Apply custom sorting if provided\n  if (options.sort) {\n    results = applySorting(results, options.sort);\n  } else {\n    // Default: sort by relevance\n    results = results.sort((a, b) => b.score - a.score);\n  }\n\n  // Limit results\n  results = results.slice(0, limit);\n\n  // Cache the results\n  if (index._cache) {\n    index._cache.set(processedQuery, results, limit, options);\n  }\n\n  return results;\n}\n\n/**\n * Create a suggestion result from a search match\n */\nfunction createSuggestionResult(match: SearchMatch, originalQuery: string, threshold: number, index: FuzzyIndex, options?: SearchOptions): SuggestionResult | null {\n  let score = calculateMatchScore(match, originalQuery, index.config);\n\n  // Combine with BM25 score if available\n  if (match.bm25Score !== undefined && index.config.useBM25) {\n    const bm25Weight = index.config.bm25Weight || 0.6;\n    const fuzzyWeight = 1 - bm25Weight;\n    score = bm25Weight * match.bm25Score + fuzzyWeight * score;\n  }\n\n  // Apply field weight if present\n  if (match.fieldWeight) {\n    score = Math.min(1.0, score * match.fieldWeight);\n  }\n\n  if (score < threshold) {\n    return null;\n  }\n\n  const result: SuggestionResult = {\n    display: match.word,\n    baseWord: match.word,\n    isSynonym: match.matchType === \"synonym\",\n    score,\n    language: match.language,\n    // @ts-ignore - temporary debug property\n    _debug_matchType: match.matchType,\n  };\n\n  // Add field information if this is a multi-field search\n  if (index.fieldData && index.fieldData.has(match.word)) {\n    result.fields = index.fieldData.get(match.word);\n    result.field = match.field;\n  }\n\n  // Add highlights if requested\n  if (options?.includeHighlights) {\n    result.highlights = calculateHighlights(match, originalQuery, match.word);\n  }\n\n  return result;\n}\n\n/**\n * Calculate match score (0-1, higher is better)\n * Enhanced scoring with better granularity and differentiation\n */\nfunction calculateMatchScore(\n  //\n  match: SearchMatch,\n  query: string,\n  config?: FuzzyConfig\n): number {\n  // Get scoring configuration with defaults - merge with defaults to ensure all values are present\n  const scores = {\n    ...DEFAULT_MATCH_TYPE_SCORES,\n    ...(config?.matchTypeScores || {}),\n  };\n  const modifiers = {\n    ...DEFAULT_SCORING_MODIFIERS,\n    ...(config?.scoringModifiers || {}),\n  };\n  \n  const queryLen = query.length;\n  const wordLen = match.word.length;\n  const maxLen = Math.max(queryLen, wordLen);\n  const minLen = Math.min(queryLen, wordLen);\n\n  let score = modifiers.baseScore;\n\n  switch (match.matchType) {\n    case \"exact\":\n      score = scores.exact;\n      break;\n      \n    case \"prefix\":\n      // Length-aware prefix scoring: better scores for closer length matches\n      const lengthRatio = queryLen / wordLen;\n      const basePrefix = scores.prefix;\n      \n      // Scale score based on how much of the word the query covers\n      // Query \"New Yor\" vs \"New York\" (7/8 = 0.875) should score ~0.95\n      // Query \"util_c\" vs \"util_controller_340\" (6/21 = 0.29) should score ~0.65\n      // Use a more aggressive curve: 0.2 + 0.8 * ratio for better differentiation\n      score = basePrefix * (0.2 + 0.8 * lengthRatio);\n      \n      // Additional boost for very close length matches (within 1-3 chars)\n      if (wordLen - queryLen <= 3) {\n        score = Math.min(1.0, score + 0.08);\n      }\n      break;\n      \n    case \"substring\":\n      // Position-aware substring scoring with aggressive penalties\n      const substringPos = match.normalized.toLowerCase().indexOf(query.toLowerCase());\n      const baseSubstring = scores.substring;\n      \n      if (substringPos !== -1) {\n        const relativePos = substringPos / match.normalized.length;\n        \n        // Coverage: how much of the word does the query represent?\n        const coverage = queryLen / wordLen;\n        \n        // Position penalty: cubic function for more aggressive late-match penalty\n        // Position 0%: no penalty\n        // Position 30%: ~2.7% penalty\n        // Position 50%: ~12.5% penalty\n        // Position 70%: ~34% penalty\n        // Position 90%: ~73% penalty\n        const positionPenalty = 0.50 * Math.pow(relativePos, 3.0);\n        \n        // Start with base score, apply coverage boost, then position penalty\n        score = baseSubstring * (0.4 + 0.6 * coverage) - positionPenalty;\n      } else {\n        score = baseSubstring * 0.5; // Fallback if position not found\n      }\n      break;\n      \n    case \"phonetic\":\n      // Phonetic matches should consider length similarity\n      const phoneticLengthRatio = minLen / maxLen;\n      score = scores.phonetic * (0.5 + 0.5 * phoneticLengthRatio);\n      break;\n      \n    case \"fuzzy\":\n      if (match.editDistance !== undefined) {\n        // Use segment-aware scoring for alphanumeric strings if enabled\n        if (config?.enableAlphanumericSegmentation && isAlphanumeric(query) && isAlphanumeric(match.word)) {\n          score = calculateAlphanumericScore(query, match.word, config);\n        } else {\n          // Enhanced fuzzy scoring based on edit distance ratio\n          const editRatio = match.editDistance / maxLen;\n          const lengthSimilarity = minLen / maxLen;\n          \n          // Better formula: penalize edit distance more heavily\n          // editRatio 0.0 (perfect): score = fuzzy base (0.65)\n          // editRatio 0.1: score ~0.58\n          // editRatio 0.2: score ~0.50\n          // editRatio 0.3+: score approaches fuzzyMin\n          score = Math.max(\n            scores.fuzzyMin,\n            scores.fuzzy * lengthSimilarity * (1.0 - editRatio * 1.5)\n          );\n          \n          // SHORT WORD BOOST: For very short words (≤4 chars), boost fuzzy scores\n          // This ensures \"teh\"→\"the\", \"cat\"→\"act\" score above threshold (0.33)\n          if (maxLen <= 4 && match.editDistance <= 1) {\n            score = Math.min(1.0, score + 0.15); // Boost by 0.15 for 1-edit short words\n          } else if (maxLen <= 4 && match.editDistance <= 2) {\n            score = Math.min(1.0, score + 0.08); // Smaller boost for 2-edit short words\n          }\n        }\n      }\n      break;\n      \n    case \"synonym\":\n      // Synonyms should also consider length similarity\n      const synonymLengthRatio = minLen / maxLen;\n      score = scores.synonym * (0.6 + 0.4 * synonymLengthRatio);\n      break;\n      \n    case \"compound\":\n      // Compound matches get a boost for length similarity\n      const compoundLengthRatio = minLen / maxLen;\n      score = scores.compound * (0.7 + 0.3 * compoundLengthRatio);\n      break;\n      \n    case \"ngram\":\n      score = calculateNgramSimilarity(query.toLowerCase(), match.normalized, 3) * scores.ngram;\n      break;\n  }\n\n  // Apply short word boost if configured\n  // Boost words that are close in length to the query (prefer concise matches)\n  // Don't boost exact matches (already at 1.0) or words much longer than query\n  if (wordLen <= queryLen + modifiers.shortWordMaxDiff && match.matchType !== \"exact\") {\n    score += modifiers.shortWordBoost;\n  }\n\n  // Exact matches should use the configured score, defaulting to 1.0\n  if (match.matchType === \"exact\") {\n    return Math.min(1.0, Math.max(0.0, scores.exact)); // Clamp to [0, 1] range\n  }\n\n  return Math.min(1.0, Math.max(0.0, score));\n}\n\n/**\n * Calculate score for alphanumeric strings using segment-aware matching\n * Prioritizes alphabetic accuracy over numeric accuracy\n */\nfunction calculateAlphanumericScore(\n  query: string,\n  target: string,\n  config: FuzzyConfig\n): number {\n  // Extract alphabetic and numeric parts\n  const queryAlpha = extractAlphaPart(query).toLowerCase();\n  const targetAlpha = extractAlphaPart(target).toLowerCase();\n  const queryNumeric = extractNumericPart(query);\n  const targetNumeric = extractNumericPart(target);\n\n  const alphaWeight = config.alphanumericAlphaWeight || 0.7;\n  const numericWeight = config.alphanumericNumericWeight || 0.3;\n\n  let alphaScore = 0;\n  let numericScore = 0;\n\n  // Calculate alphabetic score\n  if (queryAlpha.length > 0 && targetAlpha.length > 0) {\n    const alphaMaxLen = Math.max(queryAlpha.length, targetAlpha.length);\n    const alphaDistance = calculateLevenshteinDistance(queryAlpha, targetAlpha, config.maxEditDistance);\n    alphaScore = Math.max(0, 1.0 - alphaDistance / alphaMaxLen);\n  } else if (queryAlpha.length === 0 && targetAlpha.length === 0) {\n    alphaScore = 1.0; // Both have no alpha parts\n  }\n\n  // Calculate numeric score (more lenient)\n  if (queryNumeric.length > 0 && targetNumeric.length > 0) {\n    if (queryNumeric === targetNumeric) {\n      numericScore = 1.0;\n    } else {\n      // Check if one contains the other\n      if (targetNumeric.includes(queryNumeric) || queryNumeric.includes(targetNumeric)) {\n        const shorter = queryNumeric.length < targetNumeric.length ? queryNumeric : targetNumeric;\n        const longer = queryNumeric.length < targetNumeric.length ? targetNumeric : queryNumeric;\n        numericScore = shorter.length / longer.length;\n      } else {\n        // Use edit distance with multiplier (more lenient for numbers)\n        const numericMaxLen = Math.max(queryNumeric.length, targetNumeric.length);\n        const multiplier = config.alphanumericNumericEditDistanceMultiplier || 1.5;\n        const numericDistance = calculateLevenshteinDistance(queryNumeric, targetNumeric, Math.ceil(config.maxEditDistance * multiplier));\n        numericScore = Math.max(0, 1.0 - numericDistance / numericMaxLen);\n      }\n    }\n  } else if (queryNumeric.length === 0 && targetNumeric.length === 0) {\n    numericScore = 1.0; // Both have no numeric parts\n  } else {\n    // One has numbers, one doesn't - partial penalty\n    numericScore = 0.3;\n  }\n\n  // Weighted combination\n  const combinedScore = alphaScore * alphaWeight + numericScore * numericWeight;\n\n  // Ensure minimum score of 0.3 for fuzzy matches\n  return Math.max(0.3, combinedScore);\n}\n\n/**\n * Generate n-grams from a string\n */\nfunction generateNgrams(\n  //\n  str: string,\n  n: number\n): string[] {\n  if (str.length < n) return [str];\n\n  const ngrams: string[] = [];\n  for (let i = 0; i <= str.length - n; i++) {\n    ngrams.push(str.slice(i, i + n));\n  }\n  return ngrams;\n}\n\n/**\n * Get suggestions using inverted index (for large datasets)\n * This is a wrapper that converts inverted index results to the same format\n */\nfunction getSuggestionsInverted(\n  //\n  index: FuzzyIndex,\n  query: string,\n  limit: number,\n  threshold: number,\n  processors: LanguageProcessor[],\n  options?: SearchOptions\n): SuggestionResult[] {\n  if (!index.invertedIndex || !index.documents) {\n    throw new Error(\"Inverted index not available\");\n  }\n\n  // Use inverted index search\n  let matches = searchInvertedIndex(index.invertedIndex, index.documents, query, processors, index.config);\n\n  // Calculate BM25 scores if enabled\n  if (index.config.useBM25) {\n    const queryTerms = query.toLowerCase().split(/\\s+/).filter(t => t.length > 0);\n    matches = calculateBM25Scores(matches, queryTerms, index.invertedIndex, index.documents, index.config);\n  }\n\n  // Convert to suggestion results (same as classic approach)\n  let results = matches\n    .map((match) => createSuggestionResult(match, query, threshold, index, options))\n    .filter((result): result is SuggestionResult => result !== null);\n\n  // Apply filters if provided\n  if (options?.filters) {\n    results = applyFilters(results, options.filters);\n  }\n\n  // Apply custom sorting if provided\n  if (options?.sort) {\n    results = applySorting(results, options.sort);\n  } else {\n    // Default: sort by relevance\n    results = results.sort((a, b) => b.score - a.score);\n  }\n\n  // Limit results\n  results = results.slice(0, limit);\n\n  return results;\n}\n\n/**\n * Search with phrase support\n * Handles queries containing quoted phrases\n */\nfunction searchWithPhrases(\n  index: FuzzyIndex,\n  parsedQuery: ReturnType<typeof parseQuery>,\n  limit: number,\n  threshold: number,\n  options: SearchOptions\n): SuggestionResult[] {\n  const config = index.config;\n  const useTranspositions = config.features.includes('transpositions');\n  \n  // Get phrase match options\n  const phraseOptions = {\n    exactMatch: false,\n    maxEditDistance: 1,\n    proximityBonus: 1.5,\n    maxProximityDistance: 3,\n    useTranspositions,\n  };\n\n  // Search all base words for phrase matches\n  const phraseMatches = new Map<string, { score: number; phraseCount: number }>();\n\n  // For each phrase, find matching words\n  for (const phrase of parsedQuery.phrases) {\n    for (const word of index.base) {\n      const match = matchPhrase(word, phrase, phraseOptions);\n      \n      if (match.matched) {\n        const existing = phraseMatches.get(word);\n        const newScore = match.score * phraseOptions.proximityBonus;\n        \n        if (existing) {\n          // Multiple phrases matched - boost even more\n          phraseMatches.set(word, {\n            score: Math.max(existing.score, newScore),\n            phraseCount: existing.phraseCount + 1,\n          });\n        } else {\n          phraseMatches.set(word, { score: newScore, phraseCount: 1 });\n        }\n      }\n    }\n  }\n\n  // If we have regular terms too, search for them\n  let termMatches = new Map<string, SearchMatch>();\n  \n  if (parsedQuery.terms.length > 0) {\n    const termQuery = parsedQuery.terms.join(' ');\n    const processors = config.languages\n      .map((lang) => index.languageProcessors.get(lang))\n      .filter((p): p is LanguageProcessor => p !== undefined);\n\n    for (const processor of processors) {\n      const normalizedQuery = processor.normalize(termQuery);\n      \n      // Use existing search strategies for terms\n      findExactMatches(normalizedQuery, index, termMatches, processor.language);\n      findPrefixMatches(normalizedQuery, index, termMatches, processor.language);\n      findPhoneticMatches(normalizedQuery, processor, index, termMatches);\n      findNgramMatches(normalizedQuery, index, termMatches, processor.language, config.ngramSize);\n      \n      if (config.features.includes(\"missing-letters\") || config.features.includes(\"extra-letters\") || config.features.includes(\"transpositions\")) {\n        findFuzzyMatches(normalizedQuery, index, termMatches, processor, config);\n      }\n    }\n  }\n\n  // Combine phrase and term matches\n  const combinedResults = new Map<string, SuggestionResult>();\n\n  // Add phrase matches\n  for (const [word, phraseData] of phraseMatches.entries()) {\n    const result: SuggestionResult = {\n      display: word,\n      baseWord: word,\n      isSynonym: false,\n      score: phraseData.score,\n    };\n    \n    // If word also matched terms, boost score even more\n    const termMatch = termMatches.get(word);\n    if (termMatch) {\n      result.score = Math.min(1.0, result.score * 1.2);\n    }\n    \n    combinedResults.set(word, result);\n  }\n\n  // Add term matches that didn't match phrases (with lower priority)\n  for (const [word, match] of termMatches.entries()) {\n    if (!combinedResults.has(word)) {\n      const result = createSuggestionResult(match, parsedQuery.terms.join(' '), threshold, index, options);\n      if (result) {\n        // Reduce score slightly since it didn't match the phrase\n        result.score *= 0.8;\n        combinedResults.set(word, result);\n      }\n    }\n  }\n\n  // Sort and limit results\n  const results = Array.from(combinedResults.values())\n    .filter(r => r.score >= threshold)\n    .sort((a, b) => b.score - a.score)\n    .slice(0, limit);\n\n  // Cache the results\n  if (index._cache) {\n    index._cache.set(parsedQuery.original, results, limit, options);\n  }\n\n  return results;\n}\n\n/**\n * Update an existing index by adding new items\n * Much faster than rebuilding the entire index\n * \n * @param index - Existing fuzzy index to update\n * @param newItems - New items to add (strings or objects)\n * @param options - Optional configuration (uses index's existing config by default)\n * @returns Updated index (mutates the original)\n * \n * @example\n * const index = buildFuzzyIndex(['apple', 'banana']);\n * updateIndex(index, ['cherry', 'date']);\n * // Index now contains: apple, banana, cherry, date\n */\nexport function updateIndex(\n  index: FuzzyIndex,\n  newItems: (string | any)[] = [],\n  options: Partial<BuildIndexOptions> = {}\n): FuzzyIndex {\n  if (!index || !index.config) {\n    throw new Error('Invalid index provided');\n  }\n\n  if (!newItems || newItems.length === 0) {\n    return index;\n  }\n\n  // Use existing index configuration\n  const config = index.config;\n  const featureSet = new Set(config.features);\n  \n  // Get language processors from index\n  const languageProcessors = Array.from(index.languageProcessors.values());\n  \n  if (languageProcessors.length === 0) {\n    throw new Error('No language processors found in index');\n  }\n\n  // Check if we're doing multi-field search\n  const hasFields = index.fields && index.fields.length > 0;\n  const isObjectArray = newItems.length > 0 && typeof newItems[0] === 'object' && newItems[0] !== null;\n\n  // Validate: if objects are provided, fields must be specified\n  if (isObjectArray && !hasFields) {\n    throw new Error('Index was not built with fields, cannot add objects');\n  }\n\n  // Track existing words to avoid duplicates\n  const existingWords = new Set(index.base.map(w => w.toLowerCase()));\n  let processed = 0;\n\n  for (const item of newItems) {\n    if (!item) continue;\n\n    // Handle multi-field objects\n    if (hasFields && isObjectArray) {\n      const fieldValues = extractFieldValues(item, index.fields);\n      if (!fieldValues) continue;\n\n      // Generate a unique ID for this object\n      const baseId = Object.values(fieldValues)[0] || `item_${index.base.length + processed}`;\n\n      // Skip if already exists\n      if (existingWords.has(baseId.toLowerCase())) continue;\n\n      // Store field data\n      if (index.fieldData) {\n        index.fieldData.set(baseId, fieldValues);\n      }\n\n      // Add to base\n      existingWords.add(baseId.toLowerCase());\n      index.base.push(baseId);\n\n      // Index each field separately\n      for (const [fieldName, fieldValue] of Object.entries(fieldValues)) {\n        if (!fieldValue || fieldValue.trim().length < config.minQueryLength) continue;\n\n        const trimmedValue = fieldValue.trim();\n\n        // Process this field value with each language processor\n        for (const processor of languageProcessors) {\n          processWordWithProcessorAndField(trimmedValue, baseId, fieldName, processor, index, config, featureSet);\n        }\n      }\n    } else {\n      // Handle simple string array\n      const word = typeof item === 'string' ? item : String(item);\n      if (word.trim().length < config.minQueryLength) continue;\n\n      const trimmedWord = word.trim();\n      \n      // Skip if already exists\n      if (existingWords.has(trimmedWord.toLowerCase())) continue;\n\n      existingWords.add(trimmedWord.toLowerCase());\n      index.base.push(trimmedWord);\n\n      // Process with each language processor\n      for (const processor of languageProcessors) {\n        processWordWithProcessor(trimmedWord, processor, index, config, featureSet);\n      }\n    }\n\n    processed++;\n    if (options.onProgress) {\n      options.onProgress(processed, newItems.length);\n    }\n  }\n\n  // Update inverted index if it exists\n  if (index.invertedIndex && index.documents) {\n    const { invertedIndex, documents } = buildInvertedIndex(\n      index.base,\n      languageProcessors,\n      config,\n      featureSet\n    );\n    index.invertedIndex = invertedIndex;\n    index.documents = documents;\n  }\n\n  // Clear cache since index has changed\n  if (index._cache) {\n    index._cache.clear();\n  }\n\n  return index;\n}\n\n/**\n * Remove items from an existing index\n * \n * @param index - Existing fuzzy index to update\n * @param itemsToRemove - Items to remove (exact matches)\n * @returns Updated index (mutates the original)\n * \n * @example\n * const index = buildFuzzyIndex(['apple', 'banana', 'cherry']);\n * removeFromIndex(index, ['banana']);\n * // Index now contains: apple, cherry\n */\nexport function removeFromIndex(\n  index: FuzzyIndex,\n  itemsToRemove: string[] = []\n): FuzzyIndex {\n  if (!index || !index.config) {\n    throw new Error('Invalid index provided');\n  }\n\n  if (!itemsToRemove || itemsToRemove.length === 0) {\n    return index;\n  }\n\n  // Create set of items to remove (case-insensitive)\n  const toRemove = new Set(itemsToRemove.map(item => item.toLowerCase()));\n\n  // Remove from base array\n  index.base = index.base.filter(word => !toRemove.has(word.toLowerCase()));\n\n  // Remove from variant maps\n  for (const [variant, baseWords] of index.variantToBase.entries()) {\n    const filtered = new Set(Array.from(baseWords).filter((word: string) => !toRemove.has(word.toLowerCase())));\n    if (filtered.size === 0) {\n      index.variantToBase.delete(variant);\n    } else {\n      index.variantToBase.set(variant, filtered);\n    }\n  }\n\n  // Remove from phonetic map\n  for (const [phonetic, baseWords] of index.phoneticToBase.entries()) {\n    const filtered = new Set(Array.from(baseWords).filter((word: string) => !toRemove.has(word.toLowerCase())));\n    if (filtered.size === 0) {\n      index.phoneticToBase.delete(phonetic);\n    } else {\n      index.phoneticToBase.set(phonetic, filtered);\n    }\n  }\n\n  // Remove from ngram index\n  for (const [ngram, baseWords] of index.ngramIndex.entries()) {\n    const filtered = new Set(Array.from(baseWords).filter((word: string) => !toRemove.has(word.toLowerCase())));\n    if (filtered.size === 0) {\n      index.ngramIndex.delete(ngram);\n    } else {\n      index.ngramIndex.set(ngram, filtered);\n    }\n  }\n\n  // Remove from synonym map\n  for (const [synonym, baseWords] of index.synonymMap.entries()) {\n    const filtered = new Set(Array.from(baseWords).filter((word: string) => !toRemove.has(word.toLowerCase())));\n    if (filtered.size === 0) {\n      index.synonymMap.delete(synonym);\n    } else {\n      index.synonymMap.set(synonym, filtered);\n    }\n  }\n\n  // Remove from field data if exists\n  if (index.fieldData) {\n    for (const item of itemsToRemove) {\n      index.fieldData.delete(item);\n    }\n  }\n\n  // Rebuild inverted index if it exists\n  if (index.invertedIndex && index.documents) {\n    const config = index.config;\n    const featureSet = new Set(config.features);\n    const languageProcessors = Array.from(index.languageProcessors.values());\n    \n    const { invertedIndex, documents } = buildInvertedIndex(\n      index.base,\n      languageProcessors,\n      config,\n      featureSet\n    );\n    index.invertedIndex = invertedIndex;\n    index.documents = documents;\n  }\n\n  // Clear cache since index has changed\n  if (index._cache) {\n    index._cache.clear();\n  }\n\n  return index;\n}\n"],"names":["config","mergeConfig","sampleTextForDetection","detectLanguages","validateConfig","LanguageRegistry","stringPool","StringPool","index","normalizeFieldWeights","extractFieldValues","invertedIndex","buildInvertedIndex","SearchCache","removeAccents","isFQLQuery","executeFQLQuery","parseQuery","filterStopWords","results","findExactMatches","findPrefixMatches","findSubstringMatches","findPhoneticMatches","findSynonymMatches","findNgramMatches","findFuzzyMatches","applyFilters","applySorting","calculateHighlights","DEFAULT_MATCH_TYPE_SCORES","DEFAULT_SCORING_MODIFIERS","isAlphanumeric","calculateNgramSimilarity","extractAlphaPart","extractNumericPart","calculateLevenshteinDistance","searchInvertedIndex","calculateBM25Scores","matchPhrase"],"mappings":"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"}