UNPKG

word-sensor

Version:

A simple word filtering library for JavaScript/TypeScript

1 lines 6.32 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export class WordSensor {\n private forbiddenWords: Map<string, string | null>;\n private maskChar: string;\n private caseInsensitive: boolean;\n private logDetections: boolean;\n private detectionLogs: string[];\n\n constructor(\n words: string[] = [],\n maskChar = \"*\",\n caseInsensitive = true,\n logDetections = false\n ) {\n this.forbiddenWords = new Map();\n this.maskChar = maskChar;\n this.caseInsensitive = caseInsensitive;\n this.logDetections = logDetections;\n this.detectionLogs = [];\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\n addWords(words: string[]) {\n words.forEach((word) => this.addWord(word));\n }\n\n removeWord(word: string) {\n const key = this.caseInsensitive ? word.toLowerCase() : word;\n this.forbiddenWords.delete(key);\n }\n\n removeWords(words: string[]) {\n words.forEach((word) => this.removeWord(word));\n }\n\n private applyMask(word: string, maskType: \"full\" | \"partial\"): string {\n if (maskType === \"partial\" && word.length > 2) {\n return (\n word[0] + this.maskChar.repeat(word.length - 2) + word[word.length - 1]\n );\n }\n return this.maskChar.repeat(word.length);\n }\n\n filter(\n text: string,\n mode: \"replace\" | \"remove\" = \"replace\",\n maskType: \"full\" | \"partial\" = \"full\"\n ): string {\n if (this.forbiddenWords.size === 0) return text;\n\n const regex = new RegExp(\n `\\\\b(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})\\\\b`,\n this.caseInsensitive ? \"gi\" : \"g\"\n );\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 if (mode === \"remove\") return \"\";\n\n // Jika ada custom mask, gunakan itu, jika tidak, gunakan `applyMask()`\n return this.forbiddenWords.get(key) ?? this.applyMask(match, maskType);\n });\n }\n\n detect(text: string): string[] {\n if (this.forbiddenWords.size === 0) return [];\n\n const regex = new RegExp(\n `\\\\b(${[...this.forbiddenWords.keys()]\n .map((w) => w.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\"))\n .join(\"|\")})\\\\b`,\n this.caseInsensitive ? \"gi\" : \"g\"\n );\n\n const matches: string[] = [];\n let match;\n while ((match = regex.exec(text)) !== null) {\n matches.push(match[0]);\n }\n\n return matches;\n }\n\n getDetectionLogs(): string[] {\n return this.detectionLogs;\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) {\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\n let words: string[] = [];\n\n if (Array.isArray(data)) {\n words = data; // Jika API langsung berupa array\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 } catch (error) {\n console.error(\"Error loading forbidden words:\", error);\n }\n}"],"mappings":"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","names":[]}