UNPKG

claude-playwright

Version:

Seamless integration between Claude Code and Playwright MCP for efficient browser automation and testing

1 lines 14 kB
{"version":3,"sources":["../../src/core/tiered-cache.ts"],"sourcesContent":["import { LRUCache } from 'lru-cache';\nimport { BidirectionalCache } from './bidirectional-cache.js';\n\ninterface CacheEntry {\n selector: string;\n confidence: number;\n source: 'exact' | 'normalized' | 'reverse' | 'fuzzy';\n cached: boolean;\n timestamp: number;\n}\n\ninterface TieredCacheOptions {\n memorySize?: number;\n memoryTTL?: number;\n preloadCommonSelectors?: boolean;\n}\n\nexport class TieredCache {\n private memoryCache: LRUCache<string, CacheEntry>;\n private bidirectionalCache: BidirectionalCache;\n private stats = {\n memoryHits: 0,\n sqliteHits: 0,\n misses: 0,\n totalRequests: 0\n };\n\n constructor(\n bidirectionalCache: BidirectionalCache, \n options: TieredCacheOptions = {}\n ) {\n this.bidirectionalCache = bidirectionalCache;\n \n // Initialize LRU memory cache\n this.memoryCache = new LRUCache<string, CacheEntry>({\n max: options.memorySize ?? 100,\n ttl: options.memoryTTL ?? 300000, // 5 minutes\n updateAgeOnGet: true,\n updateAgeOnHas: true\n });\n\n // Preload common selectors if requested\n if (options.preloadCommonSelectors) {\n this.preloadCommonSelectors();\n }\n }\n\n async get(input: string, url: string): Promise<CacheEntry | null> {\n this.stats.totalRequests++;\n const memoryKey = this.createMemoryKey(input, url);\n\n // L1: Memory Cache (0.1ms)\n if (this.memoryCache.has(memoryKey)) {\n const cached = this.memoryCache.get(memoryKey);\n if (cached) {\n this.stats.memoryHits++;\n console.error(`[TieredCache] Memory HIT for \"${input}\": ${cached.selector} (${cached.source})`);\n return cached;\n }\n }\n\n // L2: SQLite Cache (1-5ms) \n const result = await this.bidirectionalCache.get(input, url);\n \n if (result) {\n this.stats.sqliteHits++;\n const cacheEntry: CacheEntry = {\n ...result,\n timestamp: Date.now()\n };\n \n // Store in memory for next time\n this.memoryCache.set(memoryKey, cacheEntry);\n \n // Also cache variations for better hit rate\n await this.cacheVariations(input, url, cacheEntry);\n \n console.error(`[TieredCache] SQLite HIT for \"${input}\": ${result.selector} (${result.source})`);\n return cacheEntry;\n }\n\n this.stats.misses++;\n console.error(`[TieredCache] MISS for \"${input}\"`);\n return null;\n }\n\n async set(input: string, url: string, selector: string): Promise<void> {\n // Store in SQLite (persistent)\n await this.bidirectionalCache.set(input, url, selector);\n \n // Store in memory (fast access)\n const memoryKey = this.createMemoryKey(input, url);\n const cacheEntry: CacheEntry = {\n selector,\n confidence: 0.8, // New entries start with good confidence\n source: 'exact',\n cached: true,\n timestamp: Date.now()\n };\n \n this.memoryCache.set(memoryKey, cacheEntry);\n \n // Preemptively cache common variations\n await this.cacheVariations(input, url, cacheEntry);\n \n console.error(`[TieredCache] STORED \"${input}\" → ${selector}`);\n }\n\n private createMemoryKey(input: string, url: string): string {\n return `${input.toLowerCase().trim()}|${url}`;\n }\n\n private async cacheVariations(input: string, url: string, cacheEntry: CacheEntry): Promise<void> {\n // Generate and cache common variations\n const variations = this.generateInputVariations(input);\n \n for (const variation of variations) {\n const varKey = this.createMemoryKey(variation, url);\n if (!this.memoryCache.has(varKey)) {\n // Store with slightly lower confidence\n const varEntry: CacheEntry = {\n ...cacheEntry,\n confidence: cacheEntry.confidence * 0.95,\n source: 'normalized'\n };\n this.memoryCache.set(varKey, varEntry);\n }\n }\n }\n\n private generateInputVariations(input: string): string[] {\n const variations = new Set<string>();\n \n // Basic variations\n variations.add(input.toLowerCase());\n variations.add(input.toLowerCase().trim());\n variations.add(input.replace(/\\s+/g, ' ').trim());\n \n // Remove articles\n variations.add(input.replace(/\\b(the|a|an)\\s+/gi, '').trim());\n \n // Action variations\n const actionMappings = [\n ['click', 'press', 'tap', 'hit'],\n ['type', 'enter', 'input', 'fill'],\n ['select', 'choose', 'pick']\n ];\n \n for (const synonyms of actionMappings) {\n for (let i = 0; i < synonyms.length; i++) {\n for (let j = 0; j < synonyms.length; j++) {\n if (i !== j) {\n const regex = new RegExp(`\\\\b${synonyms[i]}\\\\b`, 'gi');\n if (regex.test(input)) {\n variations.add(input.replace(regex, synonyms[j]));\n }\n }\n }\n }\n }\n \n // Remove \"button\" suffix\n variations.add(input.replace(/\\s+button\\s*$/i, '').trim());\n \n // Limit variations to prevent memory bloat\n return Array.from(variations).slice(0, 8);\n }\n\n private async preloadCommonSelectors(): Promise<void> {\n // This would preload frequently used selectors\n // Implementation depends on having historical data\n try {\n const stats = await this.bidirectionalCache.getStats();\n console.error(`[TieredCache] Preloading enabled - ${stats.storage?.unique_selectors || 0} selectors available`);\n } catch (error) {\n console.error('[TieredCache] Preload failed:', error);\n }\n }\n\n async invalidateForUrl(url: string): Promise<void> {\n // Clear memory cache entries for this URL\n const keysToDelete: string[] = [];\n \n for (const [key] of this.memoryCache.entries()) {\n if (key.endsWith(`|${url}`)) {\n keysToDelete.push(key);\n }\n }\n \n for (const key of keysToDelete) {\n this.memoryCache.delete(key);\n }\n \n console.error(`[TieredCache] Invalidated ${keysToDelete.length} memory entries for ${url}`);\n }\n\n async clear(): Promise<void> {\n this.memoryCache.clear();\n await this.bidirectionalCache.clear();\n \n // Reset stats\n this.stats = {\n memoryHits: 0,\n sqliteHits: 0,\n misses: 0,\n totalRequests: 0\n };\n \n console.error('[TieredCache] All caches cleared');\n }\n\n getStats(): any {\n const memoryHitRate = this.stats.totalRequests > 0 \n ? (this.stats.memoryHits / this.stats.totalRequests) * 100\n : 0;\n \n const sqliteHitRate = this.stats.totalRequests > 0\n ? (this.stats.sqliteHits / this.stats.totalRequests) * 100 \n : 0;\n \n const overallHitRate = this.stats.totalRequests > 0\n ? ((this.stats.memoryHits + this.stats.sqliteHits) / this.stats.totalRequests) * 100\n : 0;\n\n return {\n tiered: {\n memoryHitRate: Math.round(memoryHitRate * 10) / 10,\n sqliteHitRate: Math.round(sqliteHitRate * 10) / 10,\n overallHitRate: Math.round(overallHitRate * 10) / 10,\n totalRequests: this.stats.totalRequests,\n memorySize: this.memoryCache.size,\n memoryMax: this.memoryCache.max\n },\n breakdown: {\n memoryHits: this.stats.memoryHits,\n sqliteHits: this.stats.sqliteHits,\n misses: this.stats.misses\n }\n };\n }\n\n // Enhanced wrapper for MCP server integration\n async wrapSelectorOperation<T>(\n description: string,\n url: string,\n operation: (selector: string) => Promise<T>,\n fallbackSelector?: string\n ): Promise<{ result: T; cached: boolean; selector: string }> {\n \n // Try cache first\n const cached = await this.get(description, url);\n if (cached) {\n try {\n const result = await operation(cached.selector);\n console.error(`[TieredCache] Operation SUCCESS with cached selector: ${cached.selector}`);\n return { \n result, \n cached: true, \n selector: cached.selector \n };\n } catch (error) {\n console.error(`[TieredCache] Cached selector FAILED, invalidating: ${cached.selector}`);\n // Cached selector failed - remove it\n const memKey = this.createMemoryKey(description, url);\n this.memoryCache.delete(memKey);\n }\n }\n\n // Use fallback selector\n if (!fallbackSelector) {\n throw new Error(`No cached selector found for \"${description}\" and no fallback provided`);\n }\n\n try {\n const result = await operation(fallbackSelector);\n \n // Cache successful operation\n await this.set(description, url, fallbackSelector);\n console.error(`[TieredCache] Operation SUCCESS with fallback, now cached: ${fallbackSelector}`);\n \n return { \n result, \n cached: false, \n selector: fallbackSelector \n };\n } catch (error) {\n console.error(`[TieredCache] Fallback selector also FAILED: ${fallbackSelector}`);\n throw error;\n }\n }\n\n close(): void {\n this.memoryCache.clear();\n this.bidirectionalCache.close();\n }\n}"],"mappings":"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","names":[]}