{"version":3,"sources":["../../src/utils/text-processing.ts","../../src/adapters/base-search.ts","../../src/adapters/memory.ts","../../src/adapters/redis.ts","../../src/adapters/redis-clients.ts","../../src/adapters/redis-factory.ts"],"names":["index","config"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Text processing utilities for search functionality\r\n * Following DRY principle - shared across all adapters\r\n */\r\n\r\n/**\r\n * Build n-grams from text for fuzzy search\r\n * @param text - Input text to process\r\n * @param n - N-gram size (e.g., 3 for trigrams)\r\n * @returns Array of unique n-grams\r\n */\r\nexport function buildNGrams(text: string, n: number): string[] {\r\n  const normalized = text.toLowerCase().replace(/[^\\w\\s]/g, ' ').replace(/\\s+/g, ' ').trim();\r\n  const words = normalized.split(' ');\r\n  const ngrams: string[] = [];\r\n\r\n  for (const word of words) {\r\n    if (word.length >= n) {\r\n      for (let i = 0; i <= word.length - n; i++) {\r\n        ngrams.push(word.substring(i, i + n));\r\n      }\r\n    }\r\n  }\r\n\r\n  return [...new Set(ngrams)];\r\n}\r\n\r\n/**\r\n * Build edge-grams from text for prefix/partial matching\r\n * @param text - Input text to process\r\n * @param minGram - Minimum gram length\r\n * @param maxGram - Maximum gram length\r\n * @returns Array of unique edge-grams\r\n */\r\nexport function buildEdgeGrams(text: string, minGram: number, maxGram: number): string[] {\r\n  if (minGram > maxGram) {\r\n    return [];\r\n  }\r\n\r\n  const normalized = text.toLowerCase().replace(/[^\\w\\s]/g, ' ').replace(/\\s+/g, ' ').trim();\r\n  const words = normalized.split(' ');\r\n  const edgegrams: string[] = [];\r\n\r\n  for (const word of words) {\r\n    for (let len = minGram; len <= Math.min(maxGram, word.length); len++) {\r\n      edgegrams.push(word.substring(0, len));\r\n    }\r\n  }\r\n\r\n  return [...new Set(edgegrams)];\r\n}\r\n\r\n/**\r\n * Generate text highlights for search results\r\n * @param content - Content to highlight\r\n * @param searchTerms - Terms to highlight\r\n * @param maxHighlights - Maximum number of highlights (default: 3)\r\n * @param contextLength - Characters around match (default: 30)\r\n * @returns Array of highlighted snippets\r\n */\r\nexport function generateHighlights(\r\n  content: string, \r\n  searchTerms: string[], \r\n  maxHighlights: number = 3,\r\n  contextLength: number = 30\r\n): string[] {\r\n  const highlights: string[] = [];\r\n  const contentLower = content.toLowerCase();\r\n\r\n  for (const term of searchTerms) {\r\n    const termLower = term.toLowerCase();\r\n    let index = contentLower.indexOf(termLower);\r\n    \r\n    while (index !== -1 && highlights.length < maxHighlights) {\r\n      const start = Math.max(0, index - contextLength);\r\n      const end = Math.min(content.length, index + term.length + contextLength);\r\n      const snippet = content.substring(start, end);\r\n      \r\n      const highlightedSnippet = snippet.replace(\r\n        new RegExp(term, 'gi'),\r\n        `<mark>$&</mark>`\r\n      );\r\n      \r\n      highlights.push(\r\n        (start > 0 ? '...' : '') + \r\n        highlightedSnippet + \r\n        (end < content.length ? '...' : '')\r\n      );\r\n      \r\n      index = contentLower.indexOf(termLower, index + 1);\r\n    }\r\n  }\r\n\r\n  return highlights;\r\n}\r\n","// Base search functionality shared across all adapters\r\n\r\nimport {\r\n  SearchQuery,\r\n  SearchResponse,\r\n  SearchResult,\r\n} from '../types';\r\nimport { buildNGrams, buildEdgeGrams, generateHighlights } from '../utils/text-processing';\r\n\r\nexport interface SearchConfig {\r\n  ngramSize: number;\r\n  minEdgegram: number;\r\n  maxEdgegram: number;\r\n  exactMatchBoost: number;\r\n  ngramWeight: number;\r\n  edgegramWeight: number;\r\n  minScore: number;\r\n}\r\n\r\nexport const DEFAULT_SEARCH_CONFIG: SearchConfig = {\r\n  ngramSize: 3,\r\n  minEdgegram: 2,\r\n  maxEdgegram: 10,\r\n  exactMatchBoost: 100,\r\n  ngramWeight: 0.5,\r\n  edgegramWeight: 1.0,\r\n  minScore: 0.1,\r\n};\r\n\r\nexport interface DocumentData {\r\n  content: string;\r\n  metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Base class providing common search functionality\r\n * This eliminates duplication across different adapter implementations\r\n */\r\nexport abstract class BaseSearchAdapter {\r\n  protected searchConfig: SearchConfig;\r\n\r\n  constructor(searchConfig: Partial<SearchConfig> = {}) {\r\n    this.searchConfig = { ...DEFAULT_SEARCH_CONFIG, ...searchConfig };\r\n  }\r\n\r\n  /**\r\n   * Generate search terms for indexing a document\r\n   */\r\n  protected generateSearchTerms(content: string): {\r\n    ngrams: string[];\r\n    edgegrams: string[];\r\n  } {\r\n    const ngrams = buildNGrams(content, this.searchConfig.ngramSize);\r\n    const edgegrams = buildEdgeGrams(\r\n      content,\r\n      this.searchConfig.minEdgegram,\r\n      this.searchConfig.maxEdgegram\r\n    );\r\n\r\n    return { ngrams, edgegrams };\r\n  }\r\n\r\n  /**\r\n   * Calculate relevance score for a document based on search terms\r\n   */\r\n  protected calculateRelevanceScore(\r\n    documentContent: string,\r\n    searchTerms: string[],\r\n    ngramMatches: number,\r\n    edgegramMatches: number\r\n  ): number {\r\n    let score = 0;\r\n\r\n    // Exact word matches get the highest boost\r\n    const contentLower = documentContent.toLowerCase();\r\n    const words = contentLower.split(/\\s+/);\r\n\r\n    for (const term of searchTerms) {\r\n      if (words.includes(term.toLowerCase())) {\r\n        score += this.searchConfig.exactMatchBoost;\r\n      }\r\n    }\r\n\r\n    // Add n-gram and edge-gram scores\r\n    score += ngramMatches * this.searchConfig.ngramWeight;\r\n    score += edgegramMatches * this.searchConfig.edgegramWeight;\r\n\r\n    return score;\r\n  }\r\n\r\n  /**\r\n   * Process search results: score, sort, and paginate\r\n   */\r\n  protected processSearchResults(\r\n    query: SearchQuery,\r\n    searchTerms: string[],\r\n    documentScores: Map<string, { score: number; data: DocumentData }>,\r\n    startTime: number\r\n  ): SearchResponse {\r\n    // Filter out documents with very low scores\r\n    const filteredResults: SearchResult[] = [];\r\n\r\n    for (const [docId, { score, data }] of documentScores.entries()) {\r\n      if (score < this.searchConfig.minScore) continue;\r\n\r\n      // Generate highlights\r\n      const highlights = generateHighlights(data.content, searchTerms);\r\n\r\n      filteredResults.push({\r\n        id: docId,\r\n        score,\r\n        data: { content: data.content, ...data.metadata },\r\n        highlights,\r\n      });\r\n    }\r\n\r\n    // Sort by score (descending)\r\n    filteredResults.sort((a, b) => b.score - a.score);\r\n\r\n    // Apply pagination\r\n    const offset = query.offset || 0;\r\n    const limit = query.limit || 10;\r\n    const paginatedResults = filteredResults.slice(offset, offset + limit);\r\n\r\n    return {\r\n      query: query.query,\r\n      results: paginatedResults,\r\n      total: filteredResults.length,\r\n      took: Date.now() - startTime,\r\n      hasMore: offset + limit < filteredResults.length,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Validate and normalize search query\r\n   */\r\n  protected normalizeSearchQuery(query: SearchQuery): {\r\n    searchTerms: string[];\r\n    isValid: boolean;\r\n  } {\r\n    const searchTerms = query.query\r\n      .toLowerCase()\r\n      .split(/\\s+/)\r\n      .filter(term => term.length > 0);\r\n\r\n    return {\r\n      searchTerms,\r\n      isValid: searchTerms.length > 0,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Create empty search response for invalid queries\r\n   */\r\n  protected createEmptyResponse(query: SearchQuery, startTime: number): SearchResponse {\r\n    return {\r\n      query: query.query,\r\n      results: [],\r\n      total: 0,\r\n      took: Date.now() - startTime,\r\n      hasMore: false,\r\n    };\r\n  }\r\n}\r\n","// Memory adapter for development and testing\r\n\r\nimport {\r\n  IAdapter,\r\n  SessionMetadata,\r\n  SearchQuery,\r\n  SearchResponse,\r\n} from '../types';\r\nimport { BaseSearchAdapter, SearchConfig, DocumentData } from './base-search';\r\n\r\nexport interface MemoryAdapterConfig {\r\n  searchConfig?: Partial<SearchConfig>;\r\n  maxDocuments?: number;\r\n  sessionCleanupInterval?: number; // hours\r\n}\r\n\r\n/**\r\n * In-memory adapter for development and testing\r\n * Not suitable for production use with multiple server instances\r\n */\r\nexport class MemoryAdapter extends BaseSearchAdapter implements IAdapter {\r\n  private sessions = new Map<string, SessionMetadata>();\r\n  private documents = new Map<string, DocumentData>();\r\n  private documentAccessOrder = new Map<string, number>(); // Track access order for LRU\r\n  private ngramIndex = new Map<string, Set<string>>(); // ngram -> document IDs\r\n  private edgegramIndex = new Map<string, Set<string>>(); // edgegram -> document IDs\r\n  private maxDocuments: number;\r\n  private sessionCleanupHours: number;\r\n  private accessCounter = 0; // Monotonic counter for access order\r\n\r\n  constructor(config: MemoryAdapterConfig = {}) {\r\n    super(config.searchConfig);\r\n    this.maxDocuments = config.maxDocuments || 10000;\r\n    this.sessionCleanupHours = config.sessionCleanupInterval || 24; // hours\r\n  }\r\n\r\n  async setSession(sessionId: string, metadata: SessionMetadata): Promise<void> {\r\n    this.sessions.set(sessionId, { ...metadata });\r\n  }\r\n\r\n  async getSession(sessionId: string): Promise<SessionMetadata | null> {\r\n    const session = this.sessions.get(sessionId);\r\n    return session ? { ...session } : null;\r\n  }\r\n\r\n  async deleteSession(sessionId: string): Promise<void> {\r\n    this.sessions.delete(sessionId);\r\n  }\r\n\r\n  async getAllSessions(): Promise<SessionMetadata[]> {\r\n    return Array.from(this.sessions.values()).map(session => ({ ...session }));\r\n  }\r\n\r\n  async updateLastSeen(sessionId: string): Promise<void> {\r\n    const session = this.sessions.get(sessionId);\r\n    if (session) {\r\n      session.lastSeenAt = new Date();\r\n    }\r\n  }\r\n\r\n  async indexDocument(\r\n    id: string,\r\n    content: string,\r\n    metadata?: Record<string, unknown>\r\n  ): Promise<void> {\r\n    // Remove old index entries for this document\r\n    await this.removeFromIndex(id);\r\n\r\n    // Check document limit and remove LRU documents if necessary\r\n    while (this.documents.size >= this.maxDocuments) {\r\n      const lruDocumentId = this.findLRUDocument();\r\n      if (lruDocumentId) {\r\n        await this.removeDocument(lruDocumentId);\r\n      } else {\r\n        break; // Shouldn't happen, but prevent infinite loop\r\n      }\r\n    }\r\n\r\n    // Store document and update access order\r\n    this.documents.set(id, { content, ...(metadata && { metadata }) });\r\n    this.documentAccessOrder.set(id, ++this.accessCounter);\r\n\r\n    // Generate search terms using base class\r\n    const { ngrams, edgegrams } = this.generateSearchTerms(content);\r\n\r\n    // Index n-grams\r\n    for (const ngram of ngrams) {\r\n      if (!this.ngramIndex.has(ngram)) {\r\n        this.ngramIndex.set(ngram, new Set());\r\n      }\r\n      this.ngramIndex.get(ngram)!.add(id);\r\n    }\r\n\r\n    // Index edge-grams\r\n    for (const edgegram of edgegrams) {\r\n      if (!this.edgegramIndex.has(edgegram)) {\r\n        this.edgegramIndex.set(edgegram, new Set());\r\n      }\r\n      this.edgegramIndex.get(edgegram)!.add(id);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Find the least recently used document for eviction\r\n   */\r\n  private findLRUDocument(): string | undefined {\r\n    let lruId: string | undefined;\r\n    let lruAccessTime = Infinity;\r\n\r\n    for (const [docId, accessTime] of this.documentAccessOrder.entries()) {\r\n      if (accessTime < lruAccessTime) {\r\n        lruAccessTime = accessTime;\r\n        lruId = docId;\r\n      }\r\n    }\r\n\r\n    return lruId;\r\n  }\r\n\r\n  /**\r\n   * Update access time for a document (for LRU tracking)\r\n   */\r\n  private updateDocumentAccess(id: string): void {\r\n    if (this.documents.has(id)) {\r\n      this.documentAccessOrder.set(id, ++this.accessCounter);\r\n    }\r\n  }\r\n\r\n  async removeDocument(id: string): Promise<void> {\r\n    // Remove from document store\r\n    this.documents.delete(id);\r\n    this.documentAccessOrder.delete(id);\r\n\r\n    // Remove from indexes\r\n    await this.removeFromIndex(id);\r\n  }\r\n\r\n  private async removeFromIndex(id: string): Promise<void> {\r\n    // Remove from all index entries\r\n    for (const [ngram, docIds] of this.ngramIndex.entries()) {\r\n      docIds.delete(id);\r\n      if (docIds.size === 0) {\r\n        this.ngramIndex.delete(ngram);\r\n      }\r\n    }\r\n\r\n    for (const [edgegram, docIds] of this.edgegramIndex.entries()) {\r\n      docIds.delete(id);\r\n      if (docIds.size === 0) {\r\n        this.edgegramIndex.delete(edgegram);\r\n      }\r\n    }\r\n  }\r\n\r\n  async search(query: SearchQuery): Promise<SearchResponse> {\r\n    const startTime = Date.now();\r\n    const { searchTerms, isValid } = this.normalizeSearchQuery(query);\r\n    \r\n    if (!isValid) {\r\n      return this.createEmptyResponse(query, startTime);\r\n    }\r\n\r\n    // Find matching documents using both n-grams and edge-grams\r\n    const documentScores = new Map<string, { score: number; data: DocumentData }>();\r\n\r\n    for (const term of searchTerms) {\r\n      // Generate search terms for this query term\r\n      const { ngrams, edgegrams } = this.generateSearchTerms(term);\r\n\r\n      // Score based on n-gram matches\r\n      for (const ngram of ngrams) {\r\n        const matchingDocs = this.ngramIndex.get(ngram);\r\n        if (matchingDocs) {\r\n          for (const docId of matchingDocs) {\r\n            if (!documentScores.has(docId)) {\r\n              const doc = this.documents.get(docId);\r\n              if (doc) {\r\n                documentScores.set(docId, { score: 0, data: doc });\r\n              }\r\n            }\r\n            // Increment score for each n-gram match\r\n            const current = documentScores.get(docId);\r\n            if (current) {\r\n              current.score += this.searchConfig.ngramWeight;\r\n            }\r\n          }\r\n        }\r\n      }\r\n\r\n      // Score based on edge-gram matches\r\n      for (const edgegram of edgegrams) {\r\n        const matchingDocs = this.edgegramIndex.get(edgegram);\r\n        if (matchingDocs) {\r\n          for (const docId of matchingDocs) {\r\n            if (!documentScores.has(docId)) {\r\n              const doc = this.documents.get(docId);\r\n              if (doc) {\r\n                documentScores.set(docId, { score: 0, data: doc });\r\n              }\r\n            }\r\n            // Increment score for each edge-gram match\r\n            const current = documentScores.get(docId);\r\n            if (current) {\r\n              current.score += this.searchConfig.edgegramWeight;\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    // Calculate final relevance scores using base class method\r\n    for (const [docId, scoreData] of documentScores.entries()) {\r\n      // Update access time for LRU tracking\r\n      this.updateDocumentAccess(docId);\r\n      \r\n      const finalScore = this.calculateRelevanceScore(\r\n        scoreData.data.content,\r\n        searchTerms,\r\n        scoreData.score, // Use actual calculated match score\r\n        0 // No additional boost for memory adapter\r\n      );\r\n      \r\n      documentScores.set(docId, { ...scoreData, score: finalScore });\r\n    }\r\n\r\n    return this.processSearchResults(query, searchTerms, documentScores, startTime);\r\n  }\r\n\r\n  async cleanup(): Promise<void> {\r\n    // Remove sessions older than configured interval\r\n    const cutoffTime = new Date(Date.now() - this.sessionCleanupHours * 60 * 60 * 1000);\r\n    \r\n    for (const [sessionId, session] of this.sessions.entries()) {\r\n      if (session.lastSeenAt < cutoffTime) {\r\n        this.sessions.delete(sessionId);\r\n      }\r\n    }\r\n  }\r\n\r\n  async disconnect(): Promise<void> {\r\n    // Clear all data\r\n    this.sessions.clear();\r\n    this.documents.clear();\r\n    this.documentAccessOrder.clear();\r\n    this.ngramIndex.clear();\r\n    this.edgegramIndex.clear();\r\n    this.accessCounter = 0;\r\n  }\r\n}\r\n","// Unified Redis adapter that works with both ioredis and Upstash Redis\r\n\r\nimport {\r\n  IAdapter,\r\n  SessionMetadata,\r\n  SearchQuery,\r\n  SearchResponse,\r\n  Logger,\r\n} from '../types';\r\nimport { BaseSearchAdapter, SearchConfig, DocumentData } from './base-search';\r\nimport { UnifiedRedisInterface } from './redis-clients';\r\n\r\nexport interface UnifiedRedisAdapterConfig {\r\n  redis: UnifiedRedisInterface;\r\n  keyPrefix?: string;\r\n  searchConfig?: Partial<SearchConfig>;\r\n  logger?: Logger;\r\n  ttl?: {\r\n    session: number;\r\n    document: number;\r\n    index: number;\r\n  };\r\n}\r\n\r\n/**\r\n * Unified Redis adapter that eliminates duplication between Redis implementations\r\n * Works with both regular Redis (ioredis) and Upstash Redis (HTTP-based)\r\n */\r\nexport class UnifiedRedisAdapter extends BaseSearchAdapter implements IAdapter {\r\n  private redis: UnifiedRedisInterface;\r\n  private keyPrefix: string;\r\n  private logger: Logger | undefined;\r\n  private ttl: {\r\n    session: number;\r\n    document: number;\r\n    index: number;\r\n  };\r\n\r\n  constructor(config: UnifiedRedisAdapterConfig) {\r\n    super(config.searchConfig);\r\n    this.redis = config.redis;\r\n    this.keyPrefix = config.keyPrefix || 'pnp-ws:';\r\n    this.logger = config.logger;\r\n    this.ttl = {\r\n      session: 24 * 60 * 60, // 24 hours\r\n      document: 7 * 24 * 60 * 60, // 7 days\r\n      index: 7 * 24 * 60 * 60, // 7 days\r\n      ...config.ttl,\r\n    };\r\n  }\r\n\r\n  private getKey(type: string, id: string): string {\r\n    return `${this.keyPrefix}${type}:${id}`;\r\n  }\r\n\r\n  // Session Management\r\n  async setSession(sessionId: string, metadata: SessionMetadata): Promise<void> {\r\n    const key = this.getKey('session', sessionId);\r\n    const flatData: string[] = [];\r\n    \r\n    // Flatten data for HSET\r\n    flatData.push('id', metadata.id);\r\n    flatData.push('connectedAt', metadata.connectedAt.toISOString());\r\n    flatData.push('lastSeenAt', metadata.lastSeenAt.toISOString());\r\n    \r\n    if (metadata.userId) flatData.push('userId', metadata.userId);\r\n    if (metadata.tabId) flatData.push('tabId', metadata.tabId);\r\n    if (metadata.userAgent) flatData.push('userAgent', metadata.userAgent);\r\n    if (metadata.ip) flatData.push('ip', metadata.ip);\r\n    if (metadata.metadata) flatData.push('metadata', JSON.stringify(metadata.metadata));\r\n    \r\n    await this.redis.pipeline([\r\n      ['HSET', key, ...flatData],\r\n      ['EXPIRE', key, this.ttl.session.toString()],\r\n      ['SADD', this.getKey('sessions', 'active'), sessionId],\r\n    ]);\r\n  }\r\n\r\n  async getSession(sessionId: string): Promise<SessionMetadata | null> {\r\n    const key = this.getKey('session', sessionId);\r\n    const data = await this.redis.hgetall(key);\r\n    \r\n    if (!data || Object.keys(data).length === 0 || !data.id) {\r\n      return null;\r\n    }\r\n\r\n    return {\r\n      id: data.id,\r\n      ...(data.userId && { userId: data.userId }),\r\n      ...(data.tabId && { tabId: data.tabId }),\r\n      ...(data.userAgent && { userAgent: data.userAgent }),\r\n      ...(data.ip && { ip: data.ip }),\r\n      connectedAt: new Date(data.connectedAt || Date.now()),\r\n      lastSeenAt: new Date(data.lastSeenAt || Date.now()),\r\n      ...(data.metadata && { metadata: JSON.parse(data.metadata) }),\r\n    };\r\n  }\r\n\r\n  async deleteSession(sessionId: string): Promise<void> {\r\n    const key = this.getKey('session', sessionId);\r\n    await this.redis.pipeline([\r\n      ['DEL', key],\r\n      ['SREM', this.getKey('sessions', 'active'), sessionId],\r\n    ]);\r\n  }\r\n\r\n  async getAllSessions(): Promise<SessionMetadata[]> {\r\n    const sessionIds = await this.redis.smembers(this.getKey('sessions', 'active'));\r\n    const sessions: SessionMetadata[] = [];\r\n    \r\n    for (const sessionId of sessionIds) {\r\n      const session = await this.getSession(sessionId);\r\n      if (session) {\r\n        sessions.push(session);\r\n      } else {\r\n        // Cleanup stale session ID\r\n        await this.redis.srem(this.getKey('sessions', 'active'), sessionId);\r\n      }\r\n    }\r\n    \r\n    return sessions;\r\n  }\r\n\r\n  async updateLastSeen(sessionId: string): Promise<void> {\r\n    const key = this.getKey('session', sessionId);\r\n    await this.redis.pipeline([\r\n      ['HSET', key, 'lastSeenAt', new Date().toISOString()],\r\n      ['EXPIRE', key, this.ttl.session.toString()],\r\n    ]);\r\n  }\r\n\r\n  // Document Indexing\r\n  async indexDocument(\r\n    id: string,\r\n    content: string,\r\n    metadata?: Record<string, unknown>\r\n  ): Promise<void> {\r\n    // Remove old index entries for this document first\r\n    await this.removeDocument(id);\r\n\r\n    const docKey = this.getKey('doc', id);\r\n    \r\n    // Generate search terms\r\n    const { ngrams, edgegrams } = this.generateSearchTerms(content);\r\n    \r\n    // Track all terms for this document (for efficient removal)\r\n    const allTerms = [...ngrams, ...edgegrams];\r\n    \r\n    const docData: string[] = [\r\n      'content', content,\r\n      'indexedAt', new Date().toISOString(),\r\n      'terms', JSON.stringify(allTerms), // Store terms for efficient removal\r\n    ];\r\n    \r\n    if (metadata) {\r\n      docData.push('metadata', JSON.stringify(metadata));\r\n    }\r\n\r\n    // Prepare pipeline commands\r\n    const commands: Array<[string, ...string[]]> = [\r\n      ['HSET', docKey, ...docData],\r\n      ['EXPIRE', docKey, this.ttl.document.toString()],\r\n      ['SADD', this.getKey('docs', 'all'), id],\r\n    ];\r\n\r\n    // Add n-gram indexes\r\n    for (const ngram of ngrams) {\r\n      const ngramKey = this.getKey('ngram', ngram);\r\n      commands.push(['SADD', ngramKey, id]);\r\n      commands.push(['EXPIRE', ngramKey, this.ttl.index.toString()]);\r\n    }\r\n\r\n    // Add edge-gram indexes\r\n    for (const edgegram of edgegrams) {\r\n      const edgegramKey = this.getKey('edgegram', edgegram);\r\n      commands.push(['SADD', edgegramKey, id]);\r\n      commands.push(['EXPIRE', edgegramKey, this.ttl.index.toString()]);\r\n    }\r\n\r\n    await this.redis.pipeline(commands);\r\n  }\r\n\r\n  async removeDocument(id: string): Promise<void> {\r\n    // Get document to retrieve indexed terms\r\n    const docKey = this.getKey('doc', id);\r\n    const doc = await this.redis.hgetall(docKey);\r\n    \r\n    if (doc && doc.terms) {\r\n      try {\r\n        const terms = JSON.parse(doc.terms) as string[];\r\n        const commands: Array<[string, ...string[]]> = [];\r\n        \r\n        // Remove from all indexed terms efficiently\r\n        for (const term of terms) {\r\n          // Try both n-gram and edge-gram keys (one will be a no-op)\r\n          const ngramKey = this.getKey('ngram', term);\r\n          const edgegramKey = this.getKey('edgegram', term);\r\n          commands.push(['SREM', ngramKey, id]);\r\n          commands.push(['SREM', edgegramKey, id]);\r\n        }\r\n        \r\n        // Remove document and from document list\r\n        commands.push(['DEL', docKey]);\r\n        commands.push(['SREM', this.getKey('docs', 'all'), id]);\r\n        \r\n        await this.redis.pipeline(commands);\r\n      } catch (error) {\r\n        // Fallback to old method if terms parsing fails\r\n        this.logger?.warn?.('Failed to parse indexed terms, using fallback removal', { id, error });\r\n        await this.removeDocumentFromIndexesFallback(id);\r\n      }\r\n    } else {\r\n      // Document doesn't exist or has no terms - just clean up references\r\n      await this.redis.pipeline([\r\n        ['DEL', docKey],\r\n        ['SREM', this.getKey('docs', 'all'), id],\r\n      ]);\r\n    }\r\n  }\r\n\r\n  private async removeDocumentFromIndexesFallback(id: string): Promise<void> {\r\n    // This is the old inefficient method - only used as fallback for migration\r\n    // TODO: Remove this method once all documents have been reindexed with terms tracking\r\n    const [ngramKeys, edgegramKeys] = await Promise.all([\r\n      this.redis.keys(this.getKey('ngram', '*')),\r\n      this.redis.keys(this.getKey('edgegram', '*')),\r\n    ]);\r\n\r\n    const commands: Array<[string, ...string[]]> = [];\r\n    for (const key of [...ngramKeys, ...edgegramKeys]) {\r\n      commands.push(['SREM', key, id]);\r\n    }\r\n    \r\n    // Remove document and from document list\r\n    commands.push(['DEL', this.getKey('doc', id)]);\r\n    commands.push(['SREM', this.getKey('docs', 'all'), id]);\r\n\r\n    if (commands.length > 0) {\r\n      await this.redis.pipeline(commands);\r\n    }\r\n  }\r\n\r\n  // Search Implementation\r\n  async search(query: SearchQuery): Promise<SearchResponse> {\r\n    const startTime = Date.now();\r\n    \r\n    // Add search timeout\r\n    const searchTimeout = 10000; // 10 seconds timeout for Redis operations\r\n    const timeoutPromise = new Promise<never>((_, reject) => {\r\n      setTimeout(() => reject(new Error('Search timeout')), searchTimeout);\r\n    });\r\n    \r\n    try {\r\n      const searchPromise = this.performSearch(query, startTime);\r\n      return await Promise.race([searchPromise, timeoutPromise]);\r\n    } catch (error) {\r\n      if (error instanceof Error && error.message === 'Search timeout') {\r\n        this.logger?.error?.('Search timeout exceeded', { \r\n          query: query.query, \r\n          timeout: searchTimeout \r\n        });\r\n      }\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  private async performSearch(query: SearchQuery, startTime: number): Promise<SearchResponse> {\r\n    const { searchTerms, isValid } = this.normalizeSearchQuery(query);\r\n\r\n    if (!isValid) {\r\n      return this.createEmptyResponse(query, startTime);\r\n    }\r\n\r\n    // Collect matching document IDs with scores\r\n    const documentScores = new Map<string, { score: number; data: DocumentData }>();\r\n\r\n    // Process each search term\r\n    for (const term of searchTerms) {\r\n      const { ngrams, edgegrams } = this.generateSearchTerms(term);\r\n\r\n      // Collect n-gram and edge-gram matches efficiently\r\n      const ngramCommands: Array<[string, ...string[]]> = ngrams.map(ngram => \r\n        ['SMEMBERS', this.getKey('ngram', ngram)]\r\n      );\r\n      const edgegramCommands: Array<[string, ...string[]]> = edgegrams.map(edgegram => \r\n        ['SMEMBERS', this.getKey('edgegram', edgegram)]\r\n      );\r\n\r\n      // Execute all lookups in parallel using pipeline\r\n      const [ngramResults, edgegramResults] = await Promise.all([\r\n        this.redis.pipeline(ngramCommands),\r\n        this.redis.pipeline(edgegramCommands),\r\n      ]);\r\n\r\n      // Process n-gram matches\r\n      (ngramResults as string[][]).forEach(matchingDocs => {\r\n        for (const docId of matchingDocs) {\r\n          if (!documentScores.has(docId)) {\r\n            documentScores.set(docId, { score: 0, data: { content: '' } });\r\n          }\r\n          documentScores.get(docId)!.score += this.searchConfig.ngramWeight;\r\n        }\r\n      });\r\n\r\n      // Process edge-gram matches\r\n      (edgegramResults as string[][]).forEach((matchingDocs, index) => {\r\n        const edgegram = edgegrams[index];\r\n        if (!edgegram) return;\r\n        \r\n        const boost = edgegram.length / this.searchConfig.maxEdgegram;\r\n        \r\n        for (const docId of matchingDocs) {\r\n          if (!documentScores.has(docId)) {\r\n            documentScores.set(docId, { score: 0, data: { content: '' } });\r\n          }\r\n          documentScores.get(docId)!.score += boost * this.searchConfig.edgegramWeight;\r\n        }\r\n      });\r\n    }\r\n\r\n    // Fetch document data for scoring candidates\r\n    const docCommands: Array<[string, ...string[]]> = Array.from(documentScores.keys()).map(docId => \r\n      ['HGETALL', this.getKey('doc', docId)]\r\n    );\r\n\r\n    if (docCommands.length === 0) {\r\n      return this.createEmptyResponse(query, startTime);\r\n    }\r\n\r\n    const docResults = await this.redis.pipeline(docCommands);\r\n\r\n    // Process documents and calculate final scores\r\n    let index = 0;\r\n    for (const [docId, scoreData] of documentScores.entries()) {\r\n      const docData = docResults[index] as Record<string, string>;\r\n      index++;\r\n\r\n      if (docData && docData.content) {\r\n        const documentData: DocumentData = {\r\n          content: docData.content,\r\n          ...(docData.metadata && { metadata: JSON.parse(docData.metadata) }),\r\n        };\r\n\r\n        // Calculate final relevance score including exact matches\r\n        const finalScore = this.calculateRelevanceScore(\r\n          docData.content,\r\n          searchTerms,\r\n          0, // n-gram matches already counted\r\n          0  // edge-gram matches already counted\r\n        ) + scoreData.score;\r\n\r\n        documentScores.set(docId, { score: finalScore, data: documentData });\r\n      } else {\r\n        // Remove documents that no longer exist\r\n        documentScores.delete(docId);\r\n      }\r\n    }\r\n\r\n    return this.processSearchResults(query, searchTerms, documentScores, startTime);\r\n  }\r\n\r\n  // Cleanup and Maintenance\r\n  async cleanup(): Promise<void> {\r\n    const sessionIds = await this.redis.smembers(this.getKey('sessions', 'active'));\r\n    \r\n    for (const sessionId of sessionIds) {\r\n      const key = this.getKey('session', sessionId);\r\n      const exists = await this.redis.exists(key);\r\n      \r\n      if (!exists) {\r\n        await this.redis.srem(this.getKey('sessions', 'active'), sessionId);\r\n      }\r\n    }\r\n  }\r\n\r\n  async disconnect(): Promise<void> {\r\n    await this.redis.disconnect();\r\n  }\r\n}\r\n","import type { Redis } from 'ioredis';\r\n\r\n// Unified Redis interface for both ioredis and Upstash Redis\r\n\r\nexport interface UnifiedRedisInterface {\r\n  // Basic operations\r\n  hset(key: string, field: string, value: string): Promise<void>;\r\n  hset(key: string, ...args: string[]): Promise<void>;\r\n  hgetall(key: string): Promise<Record<string, string>>;\r\n  del(key: string): Promise<void>;\r\n  exists(key: string): Promise<number>;\r\n  expire(key: string, seconds: number): Promise<void>;\r\n\r\n  // Set operations\r\n  sadd(key: string, member: string): Promise<void>;\r\n  smembers(key: string): Promise<string[]>;\r\n  srem(key: string, member: string): Promise<void>;\r\n\r\n  // Pipeline operations\r\n  pipeline(commands: Array<[string, ...string[]]>): Promise<unknown[]>;\r\n\r\n  // Key operations\r\n  keys(pattern: string): Promise<string[]>;\r\n\r\n  // Connection\r\n  disconnect(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Adapter for ioredis (regular Redis)\r\n */\r\nexport class IoRedisAdapter implements UnifiedRedisInterface {\r\n  constructor(private redis: Redis) {}\r\n\r\n  async hset(key: string, ...args: string[]): Promise<void> {\r\n    if (args.length === 2 && args[0] && args[1]) {\r\n      await this.redis.hset(key, args[0], args[1]);\r\n    } else {\r\n      await this.redis.hset(key, ...args);\r\n    }\r\n  }\r\n\r\n  async hgetall(key: string): Promise<Record<string, string>> {\r\n    return this.redis.hgetall(key);\r\n  }\r\n\r\n  async del(key: string): Promise<void> {\r\n    await this.redis.del(key);\r\n  }\r\n\r\n  async exists(key: string): Promise<number> {\r\n    return this.redis.exists(key);\r\n  }\r\n\r\n  async expire(key: string, seconds: number): Promise<void> {\r\n    await this.redis.expire(key, seconds);\r\n  }\r\n\r\n  async sadd(key: string, member: string): Promise<void> {\r\n    await this.redis.sadd(key, member);\r\n  }\r\n\r\n  async smembers(key: string): Promise<string[]> {\r\n    return this.redis.smembers(key);\r\n  }\r\n\r\n  async srem(key: string, member: string): Promise<void> {\r\n    await this.redis.srem(key, member);\r\n  }\r\n\r\n  async pipeline(commands: Array<[string, ...string[]]>): Promise<unknown[]> {\r\n    const pipeline = this.redis.pipeline();\r\n    for (const [command, ...args] of commands) {\r\n      // Use bracket notation to call pipeline methods dynamically\r\n      // TypeScript doesn't know about dynamic method calls, so we use index access\r\n      const pipelineMethod = (pipeline as unknown as Record<string, (...args: string[]) => typeof pipeline>)[command.toLowerCase()];\r\n      if (pipelineMethod) {\r\n        pipelineMethod.call(pipeline, ...args);\r\n      }\r\n    }\r\n    const results = await pipeline.exec();\r\n    \r\n    // Check for errors in pipeline results\r\n    const processedResults: unknown[] = [];\r\n    if (results) {\r\n      for (let i = 0; i < results.length; i++) {\r\n        const result = results[i];\r\n        if (!result) continue;\r\n        \r\n        const [error, value] = result;\r\n        if (error) {\r\n          let commandInfo = 'unknown';\r\n          if (i < commands.length) {\r\n            const cmd = commands[i];\r\n            if (cmd && cmd.length > 0) {\r\n              commandInfo = cmd[0];\r\n            }\r\n          }\r\n          throw new Error(`Pipeline command ${i} (${commandInfo}) failed: ${error.message}`);\r\n        }\r\n        processedResults.push(value);\r\n      }\r\n    }\r\n    \r\n    return processedResults;\r\n  }\r\n\r\n  async keys(pattern: string): Promise<string[]> {\r\n    return this.redis.keys(pattern);\r\n  }\r\n\r\n  async disconnect(): Promise<void> {\r\n    await this.redis.quit();\r\n  }\r\n}\r\n\r\n/**\r\n * Adapter for Upstash Redis (HTTP-based)\r\n */\r\nexport class UpstashRedisAdapter implements UnifiedRedisInterface {\r\n  private baseUrl: string;\r\n  private token: string;\r\n\r\n  constructor(config: { url: string; token: string }) {\r\n    this.baseUrl = config.url.endsWith('/') ? config.url.slice(0, -1) : config.url;\r\n    this.token = config.token;\r\n  }\r\n\r\n  private async request(command: string[]): Promise<unknown> {\r\n    const response = await fetch(`${this.baseUrl}`, {\r\n      method: 'POST',\r\n      headers: {\r\n        'Authorization': `Bearer ${this.token}`,\r\n        'Content-Type': 'application/json',\r\n      },\r\n      body: JSON.stringify(command),\r\n    });\r\n\r\n    if (!response.ok) {\r\n      throw new Error(`Upstash Redis request failed: ${response.statusText}`);\r\n    }\r\n\r\n    const data = await response.json() as { result: unknown; error?: string };\r\n    \r\n    if (data.error) {\r\n      throw new Error(`Upstash Redis error: ${data.error}`);\r\n    }\r\n\r\n    return data.result;\r\n  }\r\n\r\n  async hset(key: string, ...args: string[]): Promise<void> {\r\n    await this.request(['HSET', key, ...args]);\r\n  }\r\n\r\n  async hgetall(key: string): Promise<Record<string, string>> {\r\n    return await this.request(['HGETALL', key]) as Record<string, string>;\r\n  }\r\n\r\n  async del(key: string): Promise<void> {\r\n    await this.request(['DEL', key]);\r\n  }\r\n\r\n  async exists(key: string): Promise<number> {\r\n    return await this.request(['EXISTS', key]) as number;\r\n  }\r\n\r\n  async expire(key: string, seconds: number): Promise<void> {\r\n    await this.request(['EXPIRE', key, seconds.toString()]);\r\n  }\r\n\r\n  async sadd(key: string, member: string): Promise<void> {\r\n    await this.request(['SADD', key, member]);\r\n  }\r\n\r\n  async smembers(key: string): Promise<string[]> {\r\n    return await this.request(['SMEMBERS', key]) as string[];\r\n  }\r\n\r\n  async srem(key: string, member: string): Promise<void> {\r\n    await this.request(['SREM', key, member]);\r\n  }\r\n\r\n  async pipeline(commands: Array<[string, ...string[]]>): Promise<unknown[]> {\r\n    const response = await fetch(`${this.baseUrl}/pipeline`, {\r\n      method: 'POST',\r\n      headers: {\r\n        'Authorization': `Bearer ${this.token}`,\r\n        'Content-Type': 'application/json',\r\n      },\r\n      body: JSON.stringify(commands),\r\n    });\r\n\r\n    if (!response.ok) {\r\n      throw new Error(`Upstash Redis pipeline request failed: ${response.statusText}`);\r\n    }\r\n\r\n    const data = await response.json() as Array<{ result: unknown; error?: string }>;\r\n    \r\n    return data.map(item => {\r\n      if (item.error) {\r\n        throw new Error(`Upstash Redis error: ${item.error}`);\r\n      }\r\n      return item.result;\r\n    });\r\n  }\r\n\r\n  async keys(pattern: string): Promise<string[]> {\r\n    return await this.request(['KEYS', pattern]) as string[];\r\n  }\r\n\r\n  async disconnect(): Promise<void> {\r\n    // No persistent connection to close for HTTP-based client\r\n  }\r\n}\r\n","// Factory functions for creating Redis adapters with the unified interface\r\n\r\nimport Redis, { RedisOptions } from 'ioredis';\r\nimport { UnifiedRedisAdapter, UnifiedRedisAdapterConfig } from './redis';\r\nimport { IoRedisAdapter, UpstashRedisAdapter as UpstashRedisClient } from './redis-clients';\r\nimport type { SearchConfig } from './base-search';\r\n\r\nexport interface RedisAdapterConfig {\r\n  host?: string;\r\n  port?: number;\r\n  password?: string;\r\n  db?: number;\r\n  url?: string;\r\n  keyPrefix?: string;\r\n  searchConfig?: Partial<SearchConfig>;\r\n  ttl?: {\r\n    session?: number;\r\n    document?: number;\r\n    index?: number;\r\n  };\r\n}\r\n\r\nexport interface UpstashRedisConfig {\r\n  url: string;\r\n  token: string;\r\n  keyPrefix?: string;\r\n  searchConfig?: Partial<SearchConfig>;\r\n  ttl?: {\r\n    session?: number;\r\n    document?: number;\r\n    index?: number;\r\n  };\r\n}\r\n\r\n/**\r\n * Helper function to build adapter configuration\r\n * Eliminates repetitive pattern across factory functions\r\n */\r\nfunction buildAdapterConfig(\r\n  redis: IoRedisAdapter | UpstashRedisClient,\r\n  config: {\r\n    keyPrefix?: string;\r\n    searchConfig?: Partial<SearchConfig>;\r\n    ttl?: {\r\n      session?: number;\r\n      document?: number;\r\n      index?: number;\r\n    };\r\n  }\r\n): UnifiedRedisAdapterConfig {\r\n  const adapterConfig: Partial<UnifiedRedisAdapterConfig> = { redis };\r\n\r\n  if (config.keyPrefix) {\r\n    adapterConfig.keyPrefix = config.keyPrefix;\r\n  }\r\n\r\n  if (config.searchConfig) {\r\n    adapterConfig.searchConfig = config.searchConfig;\r\n  }\r\n\r\n  if (config.ttl) {\r\n    // Convert optional TTL to required format with defaults\r\n    adapterConfig.ttl = {\r\n      session: config.ttl.session ?? 24 * 60 * 60,\r\n      document: config.ttl.document ?? 7 * 24 * 60 * 60,\r\n      index: config.ttl.index ?? 7 * 24 * 60 * 60,\r\n    };\r\n  }\r\n\r\n  return adapterConfig as UnifiedRedisAdapterConfig;\r\n}\r\n\r\n/**\r\n * Create a Redis adapter using ioredis (standard Redis)\r\n */\r\nexport function createRedisAdapter(config: RedisAdapterConfig): UnifiedRedisAdapter {\r\n  let redis: Redis;\r\n\r\n  if (config.url) {\r\n    redis = new Redis(config.url);\r\n  } else {\r\n    const redisConfig: RedisOptions = {\r\n      host: config.host || 'localhost',\r\n      port: config.port || 6379,\r\n      db: config.db || 0,\r\n      maxRetriesPerRequest: 3,\r\n      lazyConnect: true,\r\n    };\r\n\r\n    if (config.password) {\r\n      redisConfig.password = config.password;\r\n    }\r\n\r\n    redis = new Redis(redisConfig);\r\n  }\r\n\r\n  const unifiedRedis = new IoRedisAdapter(redis);\r\n  const adapterConfig = buildAdapterConfig(unifiedRedis, config);\r\n\r\n  return new UnifiedRedisAdapter(adapterConfig);\r\n}\r\n\r\n/**\r\n * Create an Upstash Redis adapter (HTTP-based Redis for serverless)\r\n */\r\nexport function createUpstashRedisAdapter(config: UpstashRedisConfig): UnifiedRedisAdapter {\r\n  const upstashRedis = new UpstashRedisClient({\r\n    url: config.url,\r\n    token: config.token,\r\n  });\r\n\r\n  const adapterConfig = buildAdapterConfig(upstashRedis, config);\r\n\r\n  return new UnifiedRedisAdapter(adapterConfig);\r\n}\r\n\r\n/**\r\n * Helper to create adapter from environment variables\r\n * Supports both Redis and Upstash Redis based on available env vars\r\n */\r\nexport function createRedisAdapterFromEnv(\r\n  keyPrefix?: string,\r\n  searchConfig?: Partial<SearchConfig>\r\n): UnifiedRedisAdapter {\r\n  // Check for Upstash Redis first\r\n  const upstashUrl = process.env.UPSTASH_REDIS_REST_URL;\r\n  const upstashToken = process.env.UPSTASH_REDIS_REST_TOKEN;\r\n\r\n  if (upstashUrl && upstashToken) {\r\n    const config: UpstashRedisConfig = {\r\n      url: upstashUrl,\r\n      token: upstashToken,\r\n      ...(keyPrefix && { keyPrefix }),\r\n      ...(searchConfig && { searchConfig }),\r\n    };\r\n    return createUpstashRedisAdapter(config);\r\n  }\r\n\r\n  // Fall back to regular Redis\r\n  const redisUrl = process.env.REDIS_URL;\r\n  if (redisUrl) {\r\n    const config: RedisAdapterConfig = {\r\n      url: redisUrl,\r\n      ...(keyPrefix && { keyPrefix }),\r\n      ...(searchConfig && { searchConfig }),\r\n    };\r\n    return createRedisAdapter(config);\r\n  }\r\n\r\n  // Use default Redis connection\r\n  const config: RedisAdapterConfig = {\r\n    host: process.env.REDIS_HOST || 'localhost',\r\n    port: parseInt(process.env.REDIS_PORT || '6379'),\r\n    db: parseInt(process.env.REDIS_DB || '0'),\r\n    ...(process.env.REDIS_PASSWORD && { password: process.env.REDIS_PASSWORD }),\r\n    ...(keyPrefix && { keyPrefix }),\r\n    ...(searchConfig && { searchConfig }),\r\n  };\r\n\r\n  return createRedisAdapter(config);\r\n}\r\n"]}