1const normalize = value => String(value ?? '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); 2const stopWords = new Set(['de','del','la','el','los','las','y','en','un','una','al','por','para']); 3function singular(word) { 4 if (['jeans','tenis','lentes'].includes(word)) return word; 5 if (word.length > 4 && word.endsWith('ces')) return word.slice(0,-3)+'z'; 6 if (word.length > 4 && /[aeiou]s$/.test(word)) { 7 if (/(?:n|r|l|d|j)es$/.test(word)) return word.slice(0,-2); 8 return word.slice(0,-1); 9 } 10 if (word.length > 4 && word.endsWith('s')) return word.slice(0,-1); 11 return word; 12} 13const tokens = value => (normalize(value).match(/[a-z0-9]+/g) || []).filter(word => !stopWords.has(word)).map(singular); 14// Each meaningful query word must match. Prefixes allow natural partial searches. 15export function matchesSearch(text, query) { 16 const terms = tokens(query); 17 if (!terms.length) return true; 18 const words = tokens(text); 19 return terms.every(term => words.some(word => word === term || (term.length >= 3 && word.startsWith(term)))); 20}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.