word-sensor
Version:
A powerful and flexible word filtering library for JavaScript/TypeScript with advanced features like regex patterns, statistics, and batch processing
1 lines • 25.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface WordSensorConfig {\n words?: string[];\n maskChar?: string;\n caseInsensitive?: boolean;\n logDetections?: boolean;\n enableRegex?: boolean;\n wordBoundary?: boolean;\n customReplacer?: (word: string, context: string) => string;\n}\n\nexport interface DetectionStats {\n totalDetections: number;\n uniqueWords: string[];\n detectionCounts: Record<string, number>;\n lastDetectionTime?: Date;\n}\n\nexport class WordSensor {\n private forbiddenWords: Map<string, string | null>;\n private regexPatterns: Map<string, RegExp>;\n private maskChar: string;\n private caseInsensitive: boolean;\n private logDetections: boolean;\n private enableRegex: boolean;\n private wordBoundary: boolean;\n private customReplacer?: (word: string, context: string) => string;\n private detectionLogs: string[];\n private detectionStats: DetectionStats;\n\n constructor(config: WordSensorConfig = {}) {\n const {\n words = [],\n maskChar = \"*\",\n caseInsensitive = true,\n logDetections = false,\n enableRegex = false,\n wordBoundary = true,\n customReplacer\n } = config;\n\n this.forbiddenWords = new Map();\n this.regexPatterns = new Map();\n this.maskChar = maskChar;\n this.caseInsensitive = caseInsensitive;\n this.logDetections = logDetections;\n this.enableRegex = enableRegex;\n this.wordBoundary = wordBoundary;\n this.customReplacer = customReplacer;\n this.detectionLogs = [];\n this.detectionStats = {\n totalDetections: 0,\n uniqueWords: [],\n detectionCounts: {}\n };\n\n words.forEach((word) => this.addWord(word));\n }\n\n addWord(word: string, mask?: string) {\n const key = this.caseInsensitive ? word.toLowerCase() : word;\n this.forbiddenWords.set(key, mask ?? null);\n \n if (this.enableRegex) {\n try {\n const pattern = this.wordBoundary ? `\\\\b${word.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\")}\\\\b` : word;\n const flags = this.caseInsensitive ? \"gi\" : \"g\";\n this.regexPatterns.set(key, new RegExp(pattern, flags));\n } catch (error) {\n console.warn(`Invalid regex pattern for word \"${word}\":`, error);\n }\n }\n }\n\n addWords(words: string[]) {\n words.forEach((word) => this.addWord(word));\n }\n\n addRegexPattern(pattern: string, mask?: string) {\n if (!this.enableRegex) {\n throw new Error(\"Regex patterns are not enabled. Set enableRegex: true in config.\");\n }\n \n try {\n const key = this.caseInsensitive ? pattern.toLowerCase() : pattern;\n this.forbiddenWords.set(key, mask ?? null);\n const flags = this.caseInsensitive ? \"gi\" : \"g\";\n this.regexPatterns.set(key, new RegExp(pattern, flags));\n } catch (error) {\n throw new Error(`Invalid regex pattern: ${error}`);\n }\n }\n\n removeWord(word: string) {\n const key = this.caseInsensitive ? word.toLowerCase() : word;\n this.forbiddenWords.delete(key);\n this.regexPatterns.delete(key);\n }\n\n removeWords(words: string[]) {\n words.forEach((word) => this.removeWord(word));\n }\n\n clearWords() {\n this.forbiddenWords.clear();\n this.regexPatterns.clear();\n this.resetStats();\n }\n\n getWords(): string[] {\n return Array.from(this.forbiddenWords.keys());\n }\n\n hasWord(word: string): boolean {\n const key = this.caseInsensitive ? word.toLowerCase() : word;\n return this.forbiddenWords.has(key);\n }\n\n private applyMask(word: string, maskType: \"full\" | \"partial\" | \"smart\"): string {\n if (maskType === \"partial\" && word.length > 2) {\n return word[0] + this.maskChar.repeat(word.length - 2) + word[word.length - 1];\n } else if (maskType === \"smart\") {\n // Smart masking: keep first and last character, mask middle\n if (word.length <= 2) return this.maskChar.repeat(word.length);\n return word[0] + this.maskChar.repeat(Math.max(1, Math.floor(word.length * 0.6))) + word[word.length - 1];\n }\n return this.maskChar.repeat(word.length);\n }\n\n private updateStats(word: string) {\n this.detectionStats.totalDetections++;\n this.detectionStats.lastDetectionTime = new Date();\n \n const key = this.caseInsensitive ? word.toLowerCase() : word;\n if (!this.detectionStats.uniqueWords.includes(key)) {\n this.detectionStats.uniqueWords.push(key);\n }\n \n this.detectionStats.detectionCounts[key] = (this.detectionStats.detectionCounts[key] || 0) + 1;\n }\n\n filter(\n text: string,\n mode: \"replace\" | \"remove\" | \"highlight\" = \"replace\",\n maskType: \"full\" | \"partial\" | \"smart\" = \"full\"\n ): string {\n if (this.forbiddenWords.size === 0) return text;\n\n if (this.enableRegex) {\n return this.filterWithRegex(text, mode, maskType);\n } else {\n return this.filterWithWords(text, mode, maskType);\n }\n }\n\n private filterWithWords(\n text: string,\n mode: \"replace\" | \"remove\" | \"highlight\",\n maskType: \"full\" | \"partial\" | \"smart\"\n ): string {\n const pattern = this.wordBoundary \n ? `\\\\b(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})\\\\b`\n : `(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})`;\n\n const regex = new RegExp(pattern, this.caseInsensitive ? \"gi\" : \"g\");\n\n return text.replace(regex, (match) => {\n const key = this.caseInsensitive ? match.toLowerCase() : match;\n\n if (this.logDetections) {\n this.detectionLogs.push(match);\n }\n\n this.updateStats(match);\n\n if (mode === \"remove\") return \"\";\n if (mode === \"highlight\") return `[FILTERED: ${match}]`;\n\n if (this.customReplacer) {\n return this.customReplacer(match, text);\n }\n\n return this.forbiddenWords.get(key) ?? this.applyMask(match, maskType);\n });\n }\n\n private filterWithRegex(\n text: string,\n mode: \"replace\" | \"remove\" | \"highlight\",\n maskType: \"full\" | \"partial\" | \"smart\"\n ): string {\n let result = text;\n \n for (const [key, regex] of this.regexPatterns) {\n result = result.replace(regex, (match) => {\n if (this.logDetections) {\n this.detectionLogs.push(match);\n }\n\n this.updateStats(match);\n\n if (mode === \"remove\") return \"\";\n if (mode === \"highlight\") return `[FILTERED: ${match}]`;\n\n if (this.customReplacer) {\n return this.customReplacer(match, text);\n }\n\n return this.forbiddenWords.get(key) ?? this.applyMask(match, maskType);\n });\n }\n\n return result;\n }\n\n detect(text: string): string[] {\n if (this.forbiddenWords.size === 0) return [];\n\n if (this.enableRegex) {\n return this.detectWithRegex(text);\n } else {\n return this.detectWithWords(text);\n }\n }\n\n private detectWithWords(text: string): string[] {\n const pattern = this.wordBoundary \n ? `\\\\b(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})\\\\b`\n : `(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})`;\n\n const regex = new RegExp(pattern, this.caseInsensitive ? \"gi\" : \"g\");\n const matches: string[] = [];\n let match;\n \n while ((match = regex.exec(text)) !== null) {\n matches.push(match[0]);\n }\n\n return matches;\n }\n\n private detectWithRegex(text: string): string[] {\n const matches: string[] = [];\n \n for (const regex of this.regexPatterns.values()) {\n let match;\n while ((match = regex.exec(text)) !== null) {\n matches.push(match[0]);\n }\n }\n\n return matches;\n }\n\n detectWithPositions(text: string): Array<{ word: string; start: number; end: number }> {\n if (this.forbiddenWords.size === 0) return [];\n\n const positions: Array<{ word: string; start: number; end: number }> = [];\n \n if (this.enableRegex) {\n for (const [key, regex] of this.regexPatterns) {\n let match;\n while ((match = regex.exec(text)) !== null) {\n positions.push({\n word: match[0],\n start: match.index,\n end: match.index + match[0].length\n });\n }\n }\n } else {\n const pattern = this.wordBoundary \n ? `\\\\b(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})\\\\b`\n : `(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})`;\n\n const regex = new RegExp(pattern, this.caseInsensitive ? \"gi\" : \"g\");\n let match;\n \n while ((match = regex.exec(text)) !== null) {\n positions.push({\n word: match[0],\n start: match.index,\n end: match.index + match[0].length\n });\n }\n }\n\n return positions.sort((a, b) => a.start - b.start);\n }\n\n getDetectionLogs(): string[] {\n return [...this.detectionLogs];\n }\n\n getStats(): DetectionStats {\n return { ...this.detectionStats };\n }\n\n resetStats() {\n this.detectionStats = {\n totalDetections: 0,\n uniqueWords: [],\n detectionCounts: {}\n };\n this.detectionLogs = [];\n }\n\n setMaskChar(char: string) {\n this.maskChar = char;\n }\n\n setCaseInsensitive(value: boolean) {\n this.caseInsensitive = value;\n }\n\n setLogDetections(value: boolean) {\n this.logDetections = value;\n }\n\n setCustomReplacer(replacer: (word: string, context: string) => string) {\n this.customReplacer = replacer;\n }\n\n // Utility methods\n sanitizeText(text: string): string {\n return this.filter(text, \"replace\", \"full\");\n }\n\n isClean(text: string): boolean {\n return this.detect(text).length === 0;\n }\n\n getCleanPercentage(text: string): number {\n const detected = this.detect(text);\n const totalWords = text.split(/\\s+/).length;\n return totalWords > 0 ? ((totalWords - detected.length) / totalWords) * 100 : 100;\n }\n}\n\n// Preset word lists\nexport const PRESET_WORDS = {\n profanity: [\n \"badword\", \"offensive\", \"rude\", \"vulgar\", \"inappropriate\",\n \"curse\", \"swear\", \"expletive\", \"obscene\", \"lewd\"\n ],\n spam: [\n \"buy now\", \"click here\", \"free money\", \"make money fast\",\n \"weight loss\", \"viagra\", \"casino\", \"lottery\", \"winner\"\n ],\n phishing: [\n \"verify account\", \"update password\", \"security alert\",\n \"suspended account\", \"unusual activity\", \"login attempt\"\n ]\n};\n\n// Utility functions\nexport function createWordSensor(config: WordSensorConfig = {}): WordSensor {\n return new WordSensor(config);\n}\n\nexport function createProfanityFilter(maskChar = \"*\"): WordSensor {\n return new WordSensor({\n words: PRESET_WORDS.profanity,\n maskChar,\n caseInsensitive: true,\n logDetections: true\n });\n}\n\nexport function createSpamFilter(maskChar = \"#\"): WordSensor {\n return new WordSensor({\n words: PRESET_WORDS.spam,\n maskChar,\n caseInsensitive: true,\n logDetections: true,\n wordBoundary: false\n });\n}\n\nexport function createPhishingFilter(maskChar = \"!\"): WordSensor {\n return new WordSensor({\n words: PRESET_WORDS.phishing,\n maskChar,\n caseInsensitive: true,\n logDetections: true,\n wordBoundary: false\n });\n}\n\nexport function getNestedValue(obj: any, path: string): any {\n return path\n .split(\".\")\n .reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj);\n}\n\nexport async function loadForbiddenWordsFromAPI(\n url: string,\n path: string | null,\n sensor: WordSensor\n): Promise<boolean> {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch: ${response.statusText}`);\n }\n\n const data = await response.json();\n let words: string[] = [];\n\n if (Array.isArray(data)) {\n words = data;\n } else if (path) {\n words = getNestedValue(data, path) ?? [];\n }\n\n if (!Array.isArray(words)) {\n throw new Error(\"Invalid words format from API\");\n }\n\n sensor.addWords(words);\n console.log(\"Forbidden words added from API:\", words);\n return true;\n } catch (error) {\n console.error(\"Error loading forbidden words:\", error);\n return false;\n }\n}\n\nexport async function loadWordsFromFile(file: File): Promise<string[]> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = (e) => {\n try {\n const content = e.target?.result as string;\n const words = content\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0 && !line.startsWith('#'));\n resolve(words);\n } catch (error) {\n reject(error);\n }\n };\n reader.onerror = () => reject(new Error('Failed to read file'));\n reader.readAsText(file);\n });\n}\n\nexport function validateRegexPattern(pattern: string): boolean {\n try {\n new RegExp(pattern);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function escapeRegexSpecialChars(str: string): string {\n return str.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\n}\n\nexport function createCustomReplacer(\n replacementMap: Record<string, string>\n): (word: string, context: string) => string {\n return (word: string) => {\n const key = word.toLowerCase();\n return replacementMap[key] || word;\n };\n}\n\nexport function createEmojiReplacer(): (word: string, context: string) => string {\n const emojiMap: Record<string, string> = {\n 'badword': '🤬',\n 'offensive': '😤',\n 'rude': '😒',\n 'vulgar': '🤢',\n 'inappropriate': '😳',\n 'curse': '💢',\n 'swear': '😠',\n 'expletive': '🤯',\n 'obscene': '😱',\n 'lewd': '😵'\n };\n \n return (word: string) => {\n const key = word.toLowerCase();\n return emojiMap[key] || '🤐';\n };\n}\n\n// Batch processing utilities\nexport function batchFilter(\n texts: string[],\n sensor: WordSensor,\n mode: \"replace\" | \"remove\" | \"highlight\" = \"replace\",\n maskType: \"full\" | \"partial\" | \"smart\" = \"full\"\n): string[] {\n return texts.map(text => sensor.filter(text, mode, maskType));\n}\n\nexport function batchDetect(\n texts: string[],\n sensor: WordSensor\n): Array<{ text: string; detected: string[] }> {\n return texts.map(text => ({\n text,\n detected: sensor.detect(text)\n }));\n}\n\nexport function getBatchStats(\n texts: string[],\n sensor: WordSensor\n): {\n totalTexts: number;\n cleanTexts: number;\n dirtyTexts: number;\n totalDetections: number;\n averageCleanPercentage: number;\n} {\n const results = texts.map(text => ({\n text,\n detected: sensor.detect(text),\n cleanPercentage: sensor.getCleanPercentage(text)\n }));\n\n const totalTexts = results.length;\n const cleanTexts = results.filter(r => r.detected.length === 0).length;\n const dirtyTexts = totalTexts - cleanTexts;\n const totalDetections = results.reduce((sum, r) => sum + r.detected.length, 0);\n const averageCleanPercentage = results.reduce((sum, r) => sum + r.cleanPercentage, 0) / totalTexts;\n\n return {\n totalTexts,\n cleanTexts,\n dirtyTexts,\n totalDetections,\n averageCleanPercentage\n };\n}"],"mappings":"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","names":[]}