plug-n-play-ws
Version:
A plug-and-play WebSocket layer on top of Socket.IO with full TypeScript support, zero manual wiring, and production-ready features
1 lines • 71.4 kB
Source Map (JSON)
{"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"]}