{"version":3,"file":"executor.cjs","sources":["../../../src/fql/executor.ts"],"sourcesContent":["/**\n * FQL Executor\n * Executes FQL AST against a fuzzy index\n */\n\nimport type { FuzzyIndex, SuggestionResult, SearchOptions } from \"../core/types.js\";\nimport type { FQLNode } from \"./ast.js\";\nimport { isAndNode, isOrNode, isNotNode, isTermNode, isPhraseNode, isFilterNode, isFieldNode, isScoreNode, isLangNode } from \"./ast.js\";\nimport { getSuggestions } from \"../core/index.js\";\n\nexport class FQLTimeoutError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"FQLTimeoutError\";\n  }\n}\n\nexport class FQLExecutor {\n  private index: FuzzyIndex;\n  private options: SearchOptions;\n  private startTime: number = 0;\n  private timeout: number = 5000; // Default 5 seconds\n\n  constructor(index: FuzzyIndex, options: SearchOptions = {}) {\n    this.index = index;\n    this.options = options;\n    this.timeout = options.fqlOptions?.timeout || 5000;\n  }\n\n  /**\n   * Execute an FQL AST and return results\n   */\n  execute(ast: FQLNode): SuggestionResult[] {\n    this.startTime = Date.now();\n    return this.executeNode(ast);\n  }\n\n  private checkTimeout(): void {\n    if (Date.now() - this.startTime > this.timeout) {\n      throw new FQLTimeoutError(`Query execution timeout after ${this.timeout}ms`);\n    }\n  }\n\n  private executeNode(node: FQLNode): SuggestionResult[] {\n    this.checkTimeout();\n\n    if (isAndNode(node)) {\n      return this.executeAnd(node);\n    }\n\n    if (isOrNode(node)) {\n      return this.executeOr(node);\n    }\n\n    if (isNotNode(node)) {\n      return this.executeNot(node);\n    }\n\n    if (isTermNode(node)) {\n      return this.executeTerm(node.value);\n    }\n\n    if (isPhraseNode(node)) {\n      return this.executePhrase(node.value);\n    }\n\n    if (isFilterNode(node)) {\n      return this.executeFilter(node);\n    }\n\n    if (isFieldNode(node)) {\n      return this.executeField(node);\n    }\n\n    if (isScoreNode(node)) {\n      return this.executeScore(node);\n    }\n\n    if (isLangNode(node)) {\n      return this.executeLang(node);\n    }\n\n    return [];\n  }\n\n  /**\n   * Execute AND - intersection of results\n   */\n  private executeAnd(node: { left: FQLNode; right: FQLNode }): SuggestionResult[] {\n    const leftResults = this.executeNode(node.left);\n    const rightResults = this.executeNode(node.right);\n\n    // Intersection: items that appear in both\n    const rightDisplays = new Set(rightResults.map((r) => r.display));\n    const intersection = leftResults.filter((r) => rightDisplays.has(r.display));\n\n    // Sort by score\n    return intersection.sort((a, b) => b.score - a.score);\n  }\n\n  /**\n   * Execute OR - union of results\n   */\n  private executeOr(node: { left: FQLNode; right: FQLNode }): SuggestionResult[] {\n    const leftResults = this.executeNode(node.left);\n    const rightResults = this.executeNode(node.right);\n\n    // Union: combine and deduplicate\n    const resultMap = new Map<string, SuggestionResult>();\n\n    for (const result of leftResults) {\n      resultMap.set(result.display, result);\n    }\n\n    for (const result of rightResults) {\n      const existing = resultMap.get(result.display);\n      // Keep higher score\n      if (!existing || result.score > existing.score) {\n        resultMap.set(result.display, result);\n      }\n    }\n\n    // Sort by score\n    return Array.from(resultMap.values()).sort((a, b) => b.score - a.score);\n  }\n\n  /**\n   * Execute NOT - exclusion of results\n   */\n  private executeNot(node: { child: FQLNode }): SuggestionResult[] {\n    const childResults = this.executeNode(node.child);\n    const excludeDisplays = new Set(childResults.map((r) => r.display));\n\n    // Get all results and exclude\n    const allResults = getSuggestions(this.index, \"\", this.index.base.length, this.options);\n    return allResults.filter((r) => !excludeDisplays.has(r.display)).sort((a, b) => b.score - a.score);\n  }\n\n  /**\n   * Execute simple term search\n   */\n  private executeTerm(term: string): SuggestionResult[] {\n    return getSuggestions(this.index, term, this.index.base.length, this.options);\n  }\n\n  /**\n   * Execute phrase search\n   */\n  private executePhrase(phrase: string): SuggestionResult[] {\n    // Use existing phrase search with quotes\n    return getSuggestions(this.index, `\"${phrase}\"`, this.index.base.length, this.options);\n  }\n\n  /**\n   * Execute filter (EXACT, FUZZY, PHONETIC, etc.)\n   */\n  private executeFilter(node: { filterType: string; value: string }): SuggestionResult[] {\n    const { filterType, value } = node;\n\n    // Get all results for the value\n    const results = getSuggestions(this.index, value, this.index.base.length, this.options);\n\n    // Filter by match type\n    switch (filterType) {\n      case \"exact\":\n        return results.filter((r) => (r as any)._debug_matchType === \"exact\");\n\n      case \"fuzzy\":\n        return results.filter((r) => (r as any)._debug_matchType === \"fuzzy\");\n\n      case \"phonetic\":\n        return results.filter((r) => (r as any)._debug_matchType === \"phonetic\");\n\n      case \"prefix\":\n        return results.filter((r) => (r as any)._debug_matchType === \"prefix\");\n\n      case \"compound\":\n        return results.filter((r) => (r as any)._debug_matchType === \"compound\");\n\n      case \"regex\":\n        return this.executeRegex(value);\n\n      default:\n        return results;\n    }\n  }\n\n  /**\n   * Execute regex pattern\n   */\n  private executeRegex(pattern: string): SuggestionResult[] {\n    // Check if regex is allowed\n    if (!this.options.fqlOptions?.allowRegex) {\n      throw new Error(\"Regex not enabled. Set fqlOptions.allowRegex = true\");\n    }\n\n    try {\n      const regex = new RegExp(pattern);\n      const results: SuggestionResult[] = [];\n\n      for (const word of this.index.base) {\n        if (regex.test(word)) {\n          results.push({\n            display: word,\n            baseWord: word,\n            score: 1.0,\n            isSynonym: false,\n            language: \"unknown\",\n            _debug_matchType: \"regex\",\n          } as any);\n        }\n      }\n\n      return results;\n    } catch (error) {\n      throw new Error(`Invalid regex pattern: ${pattern}`);\n    }\n  }\n\n  /**\n   * Execute field selector\n   */\n  private executeField(node: { field: string; child: FQLNode }): SuggestionResult[] {\n    // Execute child query\n    const childResults = this.executeNode(node.child);\n\n    // Filter by field if multi-field index\n    if (!this.index.fieldData) {\n      // No field data, return all results\n      return childResults;\n    }\n\n    // Filter results that match the field\n    return childResults.filter((result) => {\n      if (result.field === node.field) {\n        return true;\n      }\n      return false;\n    });\n  }\n\n  /**\n   * Execute score filter\n   */\n  private executeScore(node: { operator: string; threshold: number; child: FQLNode }): SuggestionResult[] {\n    const childResults = this.executeNode(node.child);\n    const { operator, threshold } = node;\n\n    return childResults.filter((result) => {\n      switch (operator) {\n        case \">\":\n          return result.score > threshold;\n        case \"<\":\n          return result.score < threshold;\n        case \">=\":\n          return result.score >= threshold;\n        case \"<=\":\n          return result.score <= threshold;\n        default:\n          return true;\n      }\n    });\n  }\n\n  /**\n   * Execute language filter\n   */\n  private executeLang(node: { language: string; child: FQLNode }): SuggestionResult[] {\n    const childResults = this.executeNode(node.child);\n    const targetLang = node.language.toLowerCase();\n\n    return childResults.filter((result) => {\n      return result.language?.toLowerCase() === targetLang;\n    });\n  }\n}\n"],"names":["index","ast","isAndNode","isOrNode","isNotNode","isTermNode","isPhraseNode","isFilterNode","isFieldNode","isScoreNode","isLangNode","getSuggestions"],"mappings":"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"}