UNPKG

claude-playwright

Version:

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

227 lines (226 loc) 7.95 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/core/tiered-cache.ts var tiered_cache_exports = {}; __export(tiered_cache_exports, { TieredCache: () => TieredCache }); module.exports = __toCommonJS(tiered_cache_exports); var import_lru_cache = require("lru-cache"); var TieredCache = class { constructor(bidirectionalCache, options = {}) { this.stats = { memoryHits: 0, sqliteHits: 0, misses: 0, totalRequests: 0 }; this.bidirectionalCache = bidirectionalCache; this.memoryCache = new import_lru_cache.LRUCache({ max: options.memorySize ?? 100, ttl: options.memoryTTL ?? 3e5, // 5 minutes updateAgeOnGet: true, updateAgeOnHas: true }); if (options.preloadCommonSelectors) { this.preloadCommonSelectors(); } } async get(input, url) { this.stats.totalRequests++; const memoryKey = this.createMemoryKey(input, url); if (this.memoryCache.has(memoryKey)) { const cached = this.memoryCache.get(memoryKey); if (cached) { this.stats.memoryHits++; console.error(`[TieredCache] Memory HIT for "${input}": ${cached.selector} (${cached.source})`); return cached; } } const result = await this.bidirectionalCache.get(input, url); if (result) { this.stats.sqliteHits++; const cacheEntry = { ...result, timestamp: Date.now() }; this.memoryCache.set(memoryKey, cacheEntry); await this.cacheVariations(input, url, cacheEntry); console.error(`[TieredCache] SQLite HIT for "${input}": ${result.selector} (${result.source})`); return cacheEntry; } this.stats.misses++; console.error(`[TieredCache] MISS for "${input}"`); return null; } async set(input, url, selector) { await this.bidirectionalCache.set(input, url, selector); const memoryKey = this.createMemoryKey(input, url); const cacheEntry = { selector, confidence: 0.8, // New entries start with good confidence source: "exact", cached: true, timestamp: Date.now() }; this.memoryCache.set(memoryKey, cacheEntry); await this.cacheVariations(input, url, cacheEntry); console.error(`[TieredCache] STORED "${input}" \u2192 ${selector}`); } createMemoryKey(input, url) { return `${input.toLowerCase().trim()}|${url}`; } async cacheVariations(input, url, cacheEntry) { const variations = this.generateInputVariations(input); for (const variation of variations) { const varKey = this.createMemoryKey(variation, url); if (!this.memoryCache.has(varKey)) { const varEntry = { ...cacheEntry, confidence: cacheEntry.confidence * 0.95, source: "normalized" }; this.memoryCache.set(varKey, varEntry); } } } generateInputVariations(input) { const variations = /* @__PURE__ */ new Set(); variations.add(input.toLowerCase()); variations.add(input.toLowerCase().trim()); variations.add(input.replace(/\s+/g, " ").trim()); variations.add(input.replace(/\b(the|a|an)\s+/gi, "").trim()); const actionMappings = [ ["click", "press", "tap", "hit"], ["type", "enter", "input", "fill"], ["select", "choose", "pick"] ]; for (const synonyms of actionMappings) { for (let i = 0; i < synonyms.length; i++) { for (let j = 0; j < synonyms.length; j++) { if (i !== j) { const regex = new RegExp(`\\b${synonyms[i]}\\b`, "gi"); if (regex.test(input)) { variations.add(input.replace(regex, synonyms[j])); } } } } } variations.add(input.replace(/\s+button\s*$/i, "").trim()); return Array.from(variations).slice(0, 8); } async preloadCommonSelectors() { try { const stats = await this.bidirectionalCache.getStats(); console.error(`[TieredCache] Preloading enabled - ${stats.storage?.unique_selectors || 0} selectors available`); } catch (error) { console.error("[TieredCache] Preload failed:", error); } } async invalidateForUrl(url) { const keysToDelete = []; for (const [key] of this.memoryCache.entries()) { if (key.endsWith(`|${url}`)) { keysToDelete.push(key); } } for (const key of keysToDelete) { this.memoryCache.delete(key); } console.error(`[TieredCache] Invalidated ${keysToDelete.length} memory entries for ${url}`); } async clear() { this.memoryCache.clear(); await this.bidirectionalCache.clear(); this.stats = { memoryHits: 0, sqliteHits: 0, misses: 0, totalRequests: 0 }; console.error("[TieredCache] All caches cleared"); } getStats() { const memoryHitRate = this.stats.totalRequests > 0 ? this.stats.memoryHits / this.stats.totalRequests * 100 : 0; const sqliteHitRate = this.stats.totalRequests > 0 ? this.stats.sqliteHits / this.stats.totalRequests * 100 : 0; const overallHitRate = this.stats.totalRequests > 0 ? (this.stats.memoryHits + this.stats.sqliteHits) / this.stats.totalRequests * 100 : 0; return { tiered: { memoryHitRate: Math.round(memoryHitRate * 10) / 10, sqliteHitRate: Math.round(sqliteHitRate * 10) / 10, overallHitRate: Math.round(overallHitRate * 10) / 10, totalRequests: this.stats.totalRequests, memorySize: this.memoryCache.size, memoryMax: this.memoryCache.max }, breakdown: { memoryHits: this.stats.memoryHits, sqliteHits: this.stats.sqliteHits, misses: this.stats.misses } }; } // Enhanced wrapper for MCP server integration async wrapSelectorOperation(description, url, operation, fallbackSelector) { const cached = await this.get(description, url); if (cached) { try { const result = await operation(cached.selector); console.error(`[TieredCache] Operation SUCCESS with cached selector: ${cached.selector}`); return { result, cached: true, selector: cached.selector }; } catch (error) { console.error(`[TieredCache] Cached selector FAILED, invalidating: ${cached.selector}`); const memKey = this.createMemoryKey(description, url); this.memoryCache.delete(memKey); } } if (!fallbackSelector) { throw new Error(`No cached selector found for "${description}" and no fallback provided`); } try { const result = await operation(fallbackSelector); await this.set(description, url, fallbackSelector); console.error(`[TieredCache] Operation SUCCESS with fallback, now cached: ${fallbackSelector}`); return { result, cached: false, selector: fallbackSelector }; } catch (error) { console.error(`[TieredCache] Fallback selector also FAILED: ${fallbackSelector}`); throw error; } } close() { this.memoryCache.clear(); this.bidirectionalCache.close(); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { TieredCache }); //# sourceMappingURL=tiered-cache.cjs.map