| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.isDuplicateIssue = isDuplicateIssue; |
| exports.registerIssue = registerIssue; |
| exports.clearDedupCache = clearDedupCache; |
| exports.getDedupStats = getDedupStats; |
| exports.cleanupDedupCache = cleanupDedupCache; |
| function simpleHash(str) { |
| let hash = 0x811c9dc5; |
| for (let i = 0; i < str.length; i++) { |
| hash ^= str.charCodeAt(i); |
| hash = Math.imul(hash, 0x01000193); |
| } |
| return (hash >>> 0).toString(16).padStart(8, '0'); |
| } |
| function normalizePath(path) { |
| return path |
| .toLowerCase() |
| .replace(/\\/g, '/') |
| .replace(/^\.\//, '') |
| .replace(/\/index\.(ts|tsx|js|jsx)$/, ''); |
| } |
| function normalizeType(type) { |
| const typeMap = { |
| mutation: 'mutation', |
| mutating: 'mutation', |
| mutate: 'mutation', |
| async: 'async', |
| race: 'async', |
| promise: 'async', |
| 'null-safety': 'null-safety', |
| null: 'null-safety', |
| undefined: 'null-safety', |
| security: 'security', |
| vulnerability: 'security', |
| performance: 'performance', |
| slow: 'performance', |
| syntax: 'syntax', |
| error: 'syntax', |
| bug: 'bug', |
| logic: 'bug' |
| }; |
| return typeMap[type.toLowerCase()] || type.toLowerCase(); |
| } |
| function normalizeLineRange(start, end) { |
| return `${Math.floor(start / 5) * 5}-${Math.ceil(end / 5) * 5}`; |
| } |
| function normalizeForDedup(description) { |
| return description |
| .toLowerCase() |
| .replace(/[^\w\s-]/g, ' ') |
| .replace(/\s+/g, ' ') |
| .trim(); |
| } |
| function extractSemanticKey(description, type) { |
| const normalized = description |
| .toLowerCase() |
| .replace(/\b(may|might|could|possible|possibly|perhaps|maybe|seems|appears|probably|usually|generally|mostly|sometimes|often)\b/g, '') |
| .replace(/\b(the|a|an|is|are|was|were|be|been|being|there|this|that|these|those|in|on|at|for|with|by|from|to|of|it|its|their|them|they|not|will|would|should|can|could|have|has|had|what|which|who|when|where|how|also|very|just|some|any|each|every|both|such)\b/g, '') |
| .replace(/[^\w\s-]/g, ' ') |
| .replace(/\s+/g, ' ') |
| .trim(); |
| const words = normalized.split(' ').filter(w => w.length > 3); |
| const keyTerms = words.slice(0, 5).join(' '); |
| return `${normalizeType(type)}:${keyTerms}`; |
| } |
| function generateSemanticHash(path, type, startLine, endLine, description) { |
| const sig = JSON.stringify({ |
| path: normalizePath(path), |
| type: normalizeType(type), |
| range: normalizeLineRange(startLine, endLine), |
| key: normalizeForDedup(description) |
| }); |
| return simpleHash(sig); |
| } |
| class DedupCache { |
| store = new Map(); |
| async isDuplicate(key) { |
| return this.store.has(`dedup:${key}`); |
| } |
| async add(key) { |
| this.store.set(`dedup:${key}`, '1'); |
| } |
| async clear() { |
| this.store.clear(); |
| } |
| async size() { |
| return this.store.size; |
| } |
| async cleanup() { |
| this.store.clear(); |
| } |
| } |
| const dedupCache = new DedupCache(); |
| async function isDuplicateIssue(path, type, startLine, endLine, description) { |
| const hash = generateSemanticHash(path, type, startLine, endLine, description); |
| if (await dedupCache.isDuplicate(hash)) |
| return true; |
| const semKey = simpleHash(extractSemanticKey(description, type)); |
| if (await dedupCache.isDuplicate(`semantic:${semKey}`)) |
| return true; |
| return false; |
| } |
| async function registerIssue(path, type, startLine, endLine, description) { |
| const hash = generateSemanticHash(path, type, startLine, endLine, description); |
| await dedupCache.add(hash); |
| await dedupCache.add(`semantic:${simpleHash(extractSemanticKey(description, type))}`); |
| } |
| async function clearDedupCache() { |
| await dedupCache.clear(); |
| } |
| async function getDedupStats() { |
| return { size: await dedupCache.size() }; |
| } |
| async function cleanupDedupCache() { |
| await dedupCache.cleanup(); |
| } |
| |