iraqi-dialect-bad-words 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -4
- package/dist/index.d.mts +77 -3
- package/dist/index.d.ts +77 -3
- package/dist/index.js +148 -21
- package/dist/index.mjs +148 -21
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -57,9 +57,23 @@ export function CommentInput() {
|
|
|
57
57
|
|
|
58
58
|
## API
|
|
59
59
|
|
|
60
|
-
### `new IraqiBadWordsFilter(
|
|
60
|
+
### `new IraqiBadWordsFilter(options?)`
|
|
61
61
|
|
|
62
|
-
Creates a filter with the built-in word list
|
|
62
|
+
Creates a filter with the built-in word list.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
new IraqiBadWordsFilter(); // profanity only
|
|
66
|
+
new IraqiBadWordsFilter({ includeInsults: true }); // also milder insults
|
|
67
|
+
new IraqiBadWordsFilter({ stripSuffixes: true }); // also unlisted suffix forms
|
|
68
|
+
new IraqiBadWordsFilter({ customWords: ['word1', 'word2'] });
|
|
69
|
+
new IraqiBadWordsFilter(['word1', 'word2']); // same as customWords
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| Option | Default | Description |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `customWords` | `[]` | Extra words to filter. |
|
|
75
|
+
| `includeInsults` | `false` | Also filter milder insults like كلب, حمار, قندرة, نعال. Off by default because many of them are everyday words, so turning it on flags sentences like `عندي كلب` ("I have a dog"). |
|
|
76
|
+
| `stripSuffixes` | `false` | Also match words with a pronoun suffix (ي ك ج ه ها نا كم هم) that the list doesn't spell out, e.g. `طيزهم` → `طيز`. Off by default because it widens matching: a clean word that ends like a bad word plus a suffix gets flagged too. |
|
|
63
77
|
|
|
64
78
|
### `isProfane(text: string): boolean`
|
|
65
79
|
|
|
@@ -69,6 +83,36 @@ Returns `true` if any word in the text is on the list.
|
|
|
69
83
|
|
|
70
84
|
Replaces each bad word with the placeholder, repeated to the word's length. Spaces and punctuation are kept.
|
|
71
85
|
|
|
86
|
+
### `inspect(text: string, options?): ProfanityMatch[]`
|
|
87
|
+
|
|
88
|
+
Lists each bad word in the text and explains why it matched. Use it to highlight words, to treat lists differently (e.g. block `words` but only warn on `insults`), or to let someone review a match before your app blocks it.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
filter.inspect('روح للكواد!');
|
|
92
|
+
// [{ token: 'للكواد', start: 4, end: 10, normalized: 'لكواد',
|
|
93
|
+
// entry: 'كواد', source: 'words', prefix: 'ل' }]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Field | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `token` | The word exactly as written. |
|
|
99
|
+
| `start`, `end` | Offsets in the **original** text, so `text.slice(start, end) === token`. Counted in JavaScript string units (UTF-16); convert them if another platform counts characters differently. |
|
|
100
|
+
| `normalized` | The word after normalization (Arabizi is folded too, so `Qa7ba` shows as `gahba`). |
|
|
101
|
+
| `entry` | The list entry it matched, spelled as in the list. |
|
|
102
|
+
| `source` | `'words'`, `'insults'` or `'custom'`. A word on more than one list keeps the first: built-in, then insults, then custom. |
|
|
103
|
+
| `prefix` | The attached prefix stripped before matching (after normalization, so `لل` shows as `ل`), or `''` for a direct match. |
|
|
104
|
+
| `suffix` | The suffix stripped before matching (only with `stripSuffixes`), or `''`. |
|
|
105
|
+
| `suppressedBy` | Only with `includeSuppressed`: the safe stem that cleared this match. |
|
|
106
|
+
|
|
107
|
+
Pass `{ includeSuppressed: true }` to also see words that matched but were cleared by a safe stem. They are not profane (`isProfane` and `clean` ignore them), but they show why a word *wasn't* flagged:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
filter.inspect('وبعيرك', { includeSuppressed: true });
|
|
111
|
+
// [{ token: 'وبعيرك', …, entry: 'عيرك', prefix: 'وب', suppressedBy: 'بعير' }]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Including the output of `inspect` in a [“Clean word flagged” issue](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=false-positive.yml) shows exactly which entry and prefix rule fired.
|
|
115
|
+
|
|
72
116
|
### `addWords(words: string[]): void`
|
|
73
117
|
|
|
74
118
|
Adds words to this filter.
|
|
@@ -88,8 +132,9 @@ Matching is done per whole word, after normalizing both the text and the word li
|
|
|
88
132
|
| Tatweel | `عـــارة` |
|
|
89
133
|
| ة / ه, ى / ي, أ / إ / آ / ا | `عاره` |
|
|
90
134
|
| Attached prefixes و ف ب ل ك ال | `والعارة`, `للعارة` |
|
|
135
|
+
| Pronoun suffixes (with `stripSuffixes`) | `طيزهم`, `وطيزكم` |
|
|
91
136
|
| Punctuation next to the word | `عارة!` `عارة،` |
|
|
92
|
-
| Arabizi (Latin letters and numbers) | `ga7ba`, `3ayri` |
|
|
137
|
+
| Arabizi (Latin letters and numbers), including spelling variants | `ga7ba` = `qahba` = `gahba`, `kosomak` = `kusumak`, `3ayri` = `ayri` |
|
|
93
138
|
|
|
94
139
|
Because matching is whole-word, a clean word that merely contains a bad word is not flagged.
|
|
95
140
|
|
|
@@ -107,7 +152,9 @@ The word list is the heart of this package, and native speakers are the best jud
|
|
|
107
152
|
- **A clean word is flagged:** open a [“Clean word flagged” issue](https://github.com/karam-fikre/iraqi-dialect-bad-words/issues/new?template=false-positive.yml).
|
|
108
153
|
If it's flagged only because of a prefix (e.g. `بعير` "camel" = `ب` + a bad word), the fix is adding it to [`src/safe-stems.json`](src/safe-stems.json).
|
|
109
154
|
|
|
110
|
-
|
|
155
|
+
- **A sentence is judged wrong:** add it to [`test/fixtures/sentences.json`](test/fixtures/sentences.json) with the verdict a native speaker expects (`"profane": true` or `false`, plus `"options"` if it needs `includeInsults` or `stripSuffixes`). Every sentence there runs as a test, so later word-list changes can't silently flip it.
|
|
156
|
+
|
|
157
|
+
Words that also have a common, normal meaning (like كلب "dog") belong in [`src/insults.json`](src/insults.json), which is opt-in, not in `words.json`.
|
|
111
158
|
|
|
112
159
|
### Development
|
|
113
160
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,22 +1,96 @@
|
|
|
1
|
+
interface IraqiBadWordsFilterOptions {
|
|
2
|
+
/** Extra words to filter, on top of the built-in list. */
|
|
3
|
+
customWords?: string[];
|
|
4
|
+
/**
|
|
5
|
+
* Also filter milder insults (كلب, حمار, قندرة, …). Off by default because
|
|
6
|
+
* many of them are everyday words ("عندي كلب", "جلب لي هدية").
|
|
7
|
+
*/
|
|
8
|
+
includeInsults?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Also match words with a pronoun suffix the list doesn't spell out
|
|
11
|
+
* (طيزهم -> طيز). Off by default: it widens matching, so it can flag
|
|
12
|
+
* clean words that happen to end like a bad word plus a suffix.
|
|
13
|
+
*/
|
|
14
|
+
stripSuffixes?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Which list a dictionary entry came from. */
|
|
17
|
+
type WordSource = "words" | "insults" | "custom";
|
|
18
|
+
interface InspectOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Also return words that matched but were cleared by a safe stem
|
|
21
|
+
* (e.g. بعير "camel" = ب + عير). They carry `suppressedBy`.
|
|
22
|
+
*/
|
|
23
|
+
includeSuppressed?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** A bad word found by inspect(). */
|
|
26
|
+
interface ProfanityMatch {
|
|
27
|
+
/** The word exactly as written in the input. */
|
|
28
|
+
token: string;
|
|
29
|
+
/**
|
|
30
|
+
* Offset of the word in the input; text.slice(start, end) === token.
|
|
31
|
+
* Counted in JavaScript string units (UTF-16), like String.prototype.slice.
|
|
32
|
+
*/
|
|
33
|
+
start: number;
|
|
34
|
+
/** Offset just past the end of the word in the input. */
|
|
35
|
+
end: number;
|
|
36
|
+
/** The word after normalization (diacritics, stretched letters, … removed). */
|
|
37
|
+
normalized: string;
|
|
38
|
+
/** The dictionary entry it matched, spelled as in its list. */
|
|
39
|
+
entry: string;
|
|
40
|
+
/** The list the entry came from. */
|
|
41
|
+
source: WordSource;
|
|
42
|
+
/** The prefix stripped before matching (e.g. "وال"), or "" for a direct match. */
|
|
43
|
+
prefix: string;
|
|
44
|
+
/** The suffix stripped before matching (only with stripSuffixes), or "". */
|
|
45
|
+
suffix: string;
|
|
46
|
+
/**
|
|
47
|
+
* The safe stem that cleared this match, spelled as in safe-stems.json.
|
|
48
|
+
* Only set on matches returned with `includeSuppressed`; these are not profane.
|
|
49
|
+
*/
|
|
50
|
+
suppressedBy?: string;
|
|
51
|
+
}
|
|
1
52
|
declare class IraqiBadWordsFilter {
|
|
2
53
|
private words;
|
|
3
54
|
private safeStems;
|
|
4
|
-
|
|
55
|
+
private stripSuffixes;
|
|
56
|
+
constructor(options?: string[] | IraqiBadWordsFilterOptions);
|
|
5
57
|
/**
|
|
6
58
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
7
59
|
* removes diacritics (Tashkeel) and tatweel, unifies Alef/Teh Marbuta/Alef Maksura
|
|
8
60
|
* forms, and collapses stretched letters (عاااارة -> عارة).
|
|
9
61
|
*/
|
|
10
62
|
private normalizeArabic;
|
|
63
|
+
/**
|
|
64
|
+
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
65
|
+
* variants compare equal: ga7ba / qahba / gahba, kosomak / kusomak,
|
|
66
|
+
* teezak / tizak. Only words made purely of Latin letters and digits are
|
|
67
|
+
* folded; expects text already passed through normalizeArabic.
|
|
68
|
+
*/
|
|
69
|
+
private foldArabizi;
|
|
70
|
+
/** Normalizes a word or token for comparison. */
|
|
71
|
+
private normalize;
|
|
72
|
+
/**
|
|
73
|
+
* Adds words from one list. A word already present keeps its first source.
|
|
74
|
+
*/
|
|
75
|
+
private addEntries;
|
|
11
76
|
/**
|
|
12
77
|
* Checks a single word, also trying it with attached prefixes removed
|
|
13
|
-
* (والكحبة -> كحبة, بطيزك -> طيزك).
|
|
78
|
+
* (والكحبة -> كحبة, بطيزك -> طيزك), and suffixes removed when stripSuffixes is on.
|
|
79
|
+
* Returns the matched entry, or null.
|
|
80
|
+
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
14
81
|
*/
|
|
82
|
+
private matchWord;
|
|
83
|
+
/** True when the word is profane, i.e. matched and not cleared by a safe stem. */
|
|
15
84
|
private isProfaneWord;
|
|
16
85
|
/**
|
|
17
86
|
* Checks if a given text contains profane words.
|
|
18
87
|
*/
|
|
19
88
|
isProfane(text: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Lists every bad word in the text with its position in the original input
|
|
91
|
+
* and why it matched, e.g. to highlight words or review a match before blocking.
|
|
92
|
+
*/
|
|
93
|
+
inspect(text: string, options?: InspectOptions): ProfanityMatch[];
|
|
20
94
|
/**
|
|
21
95
|
* Replaces profane words with a placeholder string.
|
|
22
96
|
*/
|
|
@@ -31,4 +105,4 @@ declare class IraqiBadWordsFilter {
|
|
|
31
105
|
removeWords(wordsToRemove: string[]): void;
|
|
32
106
|
}
|
|
33
107
|
|
|
34
|
-
export { IraqiBadWordsFilter, IraqiBadWordsFilter as default };
|
|
108
|
+
export { type InspectOptions, IraqiBadWordsFilter, type IraqiBadWordsFilterOptions, type ProfanityMatch, type WordSource, IraqiBadWordsFilter as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,96 @@
|
|
|
1
|
+
interface IraqiBadWordsFilterOptions {
|
|
2
|
+
/** Extra words to filter, on top of the built-in list. */
|
|
3
|
+
customWords?: string[];
|
|
4
|
+
/**
|
|
5
|
+
* Also filter milder insults (كلب, حمار, قندرة, …). Off by default because
|
|
6
|
+
* many of them are everyday words ("عندي كلب", "جلب لي هدية").
|
|
7
|
+
*/
|
|
8
|
+
includeInsults?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Also match words with a pronoun suffix the list doesn't spell out
|
|
11
|
+
* (طيزهم -> طيز). Off by default: it widens matching, so it can flag
|
|
12
|
+
* clean words that happen to end like a bad word plus a suffix.
|
|
13
|
+
*/
|
|
14
|
+
stripSuffixes?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Which list a dictionary entry came from. */
|
|
17
|
+
type WordSource = "words" | "insults" | "custom";
|
|
18
|
+
interface InspectOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Also return words that matched but were cleared by a safe stem
|
|
21
|
+
* (e.g. بعير "camel" = ب + عير). They carry `suppressedBy`.
|
|
22
|
+
*/
|
|
23
|
+
includeSuppressed?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** A bad word found by inspect(). */
|
|
26
|
+
interface ProfanityMatch {
|
|
27
|
+
/** The word exactly as written in the input. */
|
|
28
|
+
token: string;
|
|
29
|
+
/**
|
|
30
|
+
* Offset of the word in the input; text.slice(start, end) === token.
|
|
31
|
+
* Counted in JavaScript string units (UTF-16), like String.prototype.slice.
|
|
32
|
+
*/
|
|
33
|
+
start: number;
|
|
34
|
+
/** Offset just past the end of the word in the input. */
|
|
35
|
+
end: number;
|
|
36
|
+
/** The word after normalization (diacritics, stretched letters, … removed). */
|
|
37
|
+
normalized: string;
|
|
38
|
+
/** The dictionary entry it matched, spelled as in its list. */
|
|
39
|
+
entry: string;
|
|
40
|
+
/** The list the entry came from. */
|
|
41
|
+
source: WordSource;
|
|
42
|
+
/** The prefix stripped before matching (e.g. "وال"), or "" for a direct match. */
|
|
43
|
+
prefix: string;
|
|
44
|
+
/** The suffix stripped before matching (only with stripSuffixes), or "". */
|
|
45
|
+
suffix: string;
|
|
46
|
+
/**
|
|
47
|
+
* The safe stem that cleared this match, spelled as in safe-stems.json.
|
|
48
|
+
* Only set on matches returned with `includeSuppressed`; these are not profane.
|
|
49
|
+
*/
|
|
50
|
+
suppressedBy?: string;
|
|
51
|
+
}
|
|
1
52
|
declare class IraqiBadWordsFilter {
|
|
2
53
|
private words;
|
|
3
54
|
private safeStems;
|
|
4
|
-
|
|
55
|
+
private stripSuffixes;
|
|
56
|
+
constructor(options?: string[] | IraqiBadWordsFilterOptions);
|
|
5
57
|
/**
|
|
6
58
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
7
59
|
* removes diacritics (Tashkeel) and tatweel, unifies Alef/Teh Marbuta/Alef Maksura
|
|
8
60
|
* forms, and collapses stretched letters (عاااارة -> عارة).
|
|
9
61
|
*/
|
|
10
62
|
private normalizeArabic;
|
|
63
|
+
/**
|
|
64
|
+
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
65
|
+
* variants compare equal: ga7ba / qahba / gahba, kosomak / kusomak,
|
|
66
|
+
* teezak / tizak. Only words made purely of Latin letters and digits are
|
|
67
|
+
* folded; expects text already passed through normalizeArabic.
|
|
68
|
+
*/
|
|
69
|
+
private foldArabizi;
|
|
70
|
+
/** Normalizes a word or token for comparison. */
|
|
71
|
+
private normalize;
|
|
72
|
+
/**
|
|
73
|
+
* Adds words from one list. A word already present keeps its first source.
|
|
74
|
+
*/
|
|
75
|
+
private addEntries;
|
|
11
76
|
/**
|
|
12
77
|
* Checks a single word, also trying it with attached prefixes removed
|
|
13
|
-
* (والكحبة -> كحبة, بطيزك -> طيزك).
|
|
78
|
+
* (والكحبة -> كحبة, بطيزك -> طيزك), and suffixes removed when stripSuffixes is on.
|
|
79
|
+
* Returns the matched entry, or null.
|
|
80
|
+
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
14
81
|
*/
|
|
82
|
+
private matchWord;
|
|
83
|
+
/** True when the word is profane, i.e. matched and not cleared by a safe stem. */
|
|
15
84
|
private isProfaneWord;
|
|
16
85
|
/**
|
|
17
86
|
* Checks if a given text contains profane words.
|
|
18
87
|
*/
|
|
19
88
|
isProfane(text: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Lists every bad word in the text with its position in the original input
|
|
91
|
+
* and why it matched, e.g. to highlight words or review a match before blocking.
|
|
92
|
+
*/
|
|
93
|
+
inspect(text: string, options?: InspectOptions): ProfanityMatch[];
|
|
20
94
|
/**
|
|
21
95
|
* Replaces profane words with a placeholder string.
|
|
22
96
|
*/
|
|
@@ -31,4 +105,4 @@ declare class IraqiBadWordsFilter {
|
|
|
31
105
|
removeWords(wordsToRemove: string[]): void;
|
|
32
106
|
}
|
|
33
107
|
|
|
34
|
-
export { IraqiBadWordsFilter, IraqiBadWordsFilter as default };
|
|
108
|
+
export { type InspectOptions, IraqiBadWordsFilter, type IraqiBadWordsFilterOptions, type ProfanityMatch, type WordSource, IraqiBadWordsFilter as default };
|
package/dist/index.js
CHANGED
|
@@ -28,7 +28,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
// src/words.json
|
|
29
29
|
var words_default = [
|
|
30
30
|
"\u0639\u0627\u0631\u0629",
|
|
31
|
-
"\u0641\u0627\u0633\u062F",
|
|
32
31
|
"\u0643\u0633",
|
|
33
32
|
"\u0643\u0633\u0643",
|
|
34
33
|
"\u0643\u0633\u0647\u0627",
|
|
@@ -42,6 +41,12 @@ var words_default = [
|
|
|
42
41
|
"\u0639\u064A\u0631\u0647",
|
|
43
42
|
"\u0639\u064A\u0631\u0647\u0627",
|
|
44
43
|
"\u0639\u064A\u0631\u064A\u0646",
|
|
44
|
+
"\u0639\u064A\u063A",
|
|
45
|
+
"\u0639\u064A\u063A\u064A",
|
|
46
|
+
"\u0639\u064A\u063A\u0643",
|
|
47
|
+
"\u0639\u064A\u063A\u0647",
|
|
48
|
+
"\u0639\u064A\u063A\u0647\u0627",
|
|
49
|
+
"\u0639\u064A\u063A\u064A\u0646",
|
|
45
50
|
"\u0632\u0628",
|
|
46
51
|
"\u0632\u0628\u064A",
|
|
47
52
|
"\u0632\u0628\u0643",
|
|
@@ -182,9 +187,52 @@ var words_default = [
|
|
|
182
187
|
"zebak"
|
|
183
188
|
];
|
|
184
189
|
|
|
190
|
+
// src/insults.json
|
|
191
|
+
var insults_default = [
|
|
192
|
+
"\u0643\u0644\u0628",
|
|
193
|
+
"\u0643\u0644\u0627\u0628",
|
|
194
|
+
"\u0627\u0644\u0643\u0644\u0628",
|
|
195
|
+
"\u062C\u0644\u0628",
|
|
196
|
+
"\u062C\u0644\u0627\u0628",
|
|
197
|
+
"\u062D\u064A\u0648\u0627\u0646",
|
|
198
|
+
"\u062D\u064A\u0648\u0627\u0646\u0627\u062A",
|
|
199
|
+
"\u062D\u0645\u0627\u0631",
|
|
200
|
+
"\u062D\u0645\u0627\u0631\u0629",
|
|
201
|
+
"\u062D\u064E\u0645\u064A\u0631",
|
|
202
|
+
"\u062B\u0648\u0631",
|
|
203
|
+
"\u0642\u0646\u062F\u0631\u0629",
|
|
204
|
+
"\u0627\u0644\u0642\u0646\u062F\u0631\u0629",
|
|
205
|
+
"\u0642\u0646\u062F\u0631\u062A\u064A",
|
|
206
|
+
"\u0646\u0639\u0627\u0644",
|
|
207
|
+
"\u0627\u0644\u0646\u0639\u0627\u0644",
|
|
208
|
+
"\u0646\u0639\u0627\u0644\u064A",
|
|
209
|
+
"\u0645\u0633\u062D\u0627\u0629",
|
|
210
|
+
"\u0637\u0627\u0628\u0648\u0642\u0629",
|
|
211
|
+
"\u062E\u0627\u064A\u0633",
|
|
212
|
+
"\u062E\u0627\u064A\u0633\u0629",
|
|
213
|
+
"\u062E\u064A\u0633\u0629",
|
|
214
|
+
"\u0639\u0641\u0646",
|
|
215
|
+
"\u0639\u0641\u0646\u0629",
|
|
216
|
+
"\u0645\u0639\u0641\u0646",
|
|
217
|
+
"\u062A\u0641\u0644",
|
|
218
|
+
"\u062A\u0641\u0627\u0644\u0629",
|
|
219
|
+
"\u0639\u0642\u0631\u0648\u0643",
|
|
220
|
+
"\u0635\u0639\u0644\u0648\u0643",
|
|
221
|
+
"\u0637\u0643\u0639\u0629",
|
|
222
|
+
"\u0645\u0644\u0639\u0648\u0646",
|
|
223
|
+
"kalb",
|
|
224
|
+
"gandora",
|
|
225
|
+
"naal",
|
|
226
|
+
"khayis",
|
|
227
|
+
"7ayawan",
|
|
228
|
+
"7mar",
|
|
229
|
+
"ma3fan"
|
|
230
|
+
];
|
|
231
|
+
|
|
185
232
|
// src/safe-stems.json
|
|
186
233
|
var safe_stems_default = [
|
|
187
234
|
"\u0628\u0639\u064A\u0631",
|
|
235
|
+
"\u0628\u0639\u064A\u063A",
|
|
188
236
|
"\u0628\u0643\u0633",
|
|
189
237
|
"\u0648\u0643\u0633",
|
|
190
238
|
"\u0641\u0643\u0633"
|
|
@@ -195,11 +243,16 @@ var WORD_PATTERN = /[A-Za-z0-9_À-ɏ-؋؍-ؚ-؞ؠ-٩ٮ-ۓە-ۿݐ-ݿࢠ-ࣿ
|
|
|
195
243
|
var CONJUNCTIONS = ["", "\u0648", "\u0641"];
|
|
196
244
|
var PREPOSITIONS = ["", "\u0628", "\u0644", "\u0643"];
|
|
197
245
|
var ARTICLES = ["", "\u0627\u0644"];
|
|
246
|
+
var SUFFIXES = ["\u064A", "\u0643", "\u062C", "\u0647", "\u0647\u0627", "\u0646\u0627", "\u0643\u0645", "\u0647\u0645"];
|
|
198
247
|
var IraqiBadWordsFilter = class {
|
|
199
|
-
constructor(
|
|
200
|
-
|
|
201
|
-
this.
|
|
202
|
-
this.
|
|
248
|
+
constructor(options = {}) {
|
|
249
|
+
const { customWords = [], includeInsults = false, stripSuffixes = false } = Array.isArray(options) ? { customWords: options } : options;
|
|
250
|
+
this.words = /* @__PURE__ */ new Map();
|
|
251
|
+
this.stripSuffixes = stripSuffixes;
|
|
252
|
+
this.safeStems = safe_stems_default.map((word) => ({ normalized: this.normalize(word), word }));
|
|
253
|
+
this.addEntries(words_default, "words");
|
|
254
|
+
if (includeInsults) this.addEntries(insults_default, "insults");
|
|
255
|
+
this.addEntries(customWords, "custom");
|
|
203
256
|
}
|
|
204
257
|
/**
|
|
205
258
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
@@ -207,30 +260,83 @@ var IraqiBadWordsFilter = class {
|
|
|
207
260
|
* forms, and collapses stretched letters (عاااارة -> عارة).
|
|
208
261
|
*/
|
|
209
262
|
normalizeArabic(text) {
|
|
210
|
-
return text.replace(/[
|
|
263
|
+
return text.replace(/[ً-ٰٟـ]/g, "").replace(/[أإآٱ]/g, "\u0627").replace(/ة/g, "\u0647").replace(/ى/g, "\u064A").replace(/(.)\1+/gu, "$1").toLowerCase();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
267
|
+
* variants compare equal: ga7ba / qahba / gahba, kosomak / kusomak,
|
|
268
|
+
* teezak / tizak. Only words made purely of Latin letters and digits are
|
|
269
|
+
* folded; expects text already passed through normalizeArabic.
|
|
270
|
+
*/
|
|
271
|
+
foldArabizi(word) {
|
|
272
|
+
if (!/^[a-z0-9]+$/.test(word) || !/[a-z]/.test(word)) return word;
|
|
273
|
+
return word.replace(/7/g, "h").replace(/5/g, "kh").replace(/[23]/g, "").replace(/q/g, "g").replace(/ou|o/g, "u").replace(/e/g, "i").replace(/(.)\1+/g, "$1");
|
|
274
|
+
}
|
|
275
|
+
/** Normalizes a word or token for comparison. */
|
|
276
|
+
normalize(text) {
|
|
277
|
+
return this.foldArabizi(this.normalizeArabic(text));
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Adds words from one list. A word already present keeps its first source.
|
|
281
|
+
*/
|
|
282
|
+
addEntries(newWords, source) {
|
|
283
|
+
for (const word of newWords) {
|
|
284
|
+
const normalized = this.normalize(word);
|
|
285
|
+
if (!this.words.has(normalized)) this.words.set(normalized, { word, source });
|
|
286
|
+
}
|
|
211
287
|
}
|
|
212
288
|
/**
|
|
213
289
|
* Checks a single word, also trying it with attached prefixes removed
|
|
214
|
-
* (والكحبة -> كحبة, بطيزك -> طيزك).
|
|
290
|
+
* (والكحبة -> كحبة, بطيزك -> طيزك), and suffixes removed when stripSuffixes is on.
|
|
291
|
+
* Returns the matched entry, or null.
|
|
292
|
+
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
215
293
|
*/
|
|
216
|
-
|
|
217
|
-
const word = this.
|
|
218
|
-
|
|
219
|
-
|
|
294
|
+
matchWord(token) {
|
|
295
|
+
const word = this.normalize(token);
|
|
296
|
+
const direct = this.words.get(word);
|
|
297
|
+
if (direct) {
|
|
298
|
+
return { normalized: word, entry: direct.word, source: direct.source, prefix: "", suffix: "" };
|
|
299
|
+
}
|
|
300
|
+
const candidates = [{ prefix: "", stem: word, suffix: "" }];
|
|
220
301
|
for (const conjunction of CONJUNCTIONS) {
|
|
221
302
|
for (const preposition of PREPOSITIONS) {
|
|
222
303
|
for (const article of ARTICLES) {
|
|
223
304
|
const prefix = conjunction + preposition + article;
|
|
224
305
|
if (prefix && word.startsWith(prefix) && word.length - prefix.length >= 2) {
|
|
225
|
-
|
|
306
|
+
candidates.push({ prefix, stem: word.slice(prefix.length), suffix: "" });
|
|
226
307
|
}
|
|
227
308
|
}
|
|
228
309
|
}
|
|
229
310
|
}
|
|
230
|
-
if (
|
|
231
|
-
|
|
311
|
+
if (this.stripSuffixes) {
|
|
312
|
+
for (const { prefix, stem } of [...candidates]) {
|
|
313
|
+
for (const suffix of SUFFIXES) {
|
|
314
|
+
if (stem.endsWith(suffix) && stem.length - suffix.length >= 2) {
|
|
315
|
+
candidates.push({ prefix, stem: stem.slice(0, -suffix.length), suffix });
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
232
319
|
}
|
|
233
|
-
|
|
320
|
+
const match = candidates.find(({ stem }) => this.words.has(stem));
|
|
321
|
+
if (!match) return null;
|
|
322
|
+
const { word: entry, source } = this.words.get(match.stem);
|
|
323
|
+
const result = {
|
|
324
|
+
normalized: word,
|
|
325
|
+
entry,
|
|
326
|
+
source,
|
|
327
|
+
prefix: match.prefix,
|
|
328
|
+
suffix: match.suffix
|
|
329
|
+
};
|
|
330
|
+
for (const { stem } of candidates) {
|
|
331
|
+
const safe = this.safeStems.find(({ normalized }) => stem.startsWith(normalized));
|
|
332
|
+
if (safe) return { ...result, suppressedBy: safe.word };
|
|
333
|
+
}
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
/** True when the word is profane, i.e. matched and not cleared by a safe stem. */
|
|
337
|
+
isProfaneWord(token) {
|
|
338
|
+
const match = this.matchWord(token);
|
|
339
|
+
return match !== null && match.suppressedBy === void 0;
|
|
234
340
|
}
|
|
235
341
|
/**
|
|
236
342
|
* Checks if a given text contains profane words.
|
|
@@ -244,27 +350,48 @@ var IraqiBadWordsFilter = class {
|
|
|
244
350
|
}
|
|
245
351
|
return false;
|
|
246
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Lists every bad word in the text with its position in the original input
|
|
355
|
+
* and why it matched, e.g. to highlight words or review a match before blocking.
|
|
356
|
+
*/
|
|
357
|
+
inspect(text, options = {}) {
|
|
358
|
+
if (!text) return [];
|
|
359
|
+
const matches = [];
|
|
360
|
+
const pattern = new RegExp(WORD_PATTERN.source, "g");
|
|
361
|
+
let found;
|
|
362
|
+
while (found = pattern.exec(text)) {
|
|
363
|
+
const token = found[0];
|
|
364
|
+
const match = this.matchWord(token);
|
|
365
|
+
if (match && (match.suppressedBy === void 0 || options.includeSuppressed)) {
|
|
366
|
+
matches.push({ token, start: found.index, end: found.index + token.length, ...match });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return matches;
|
|
370
|
+
}
|
|
247
371
|
/**
|
|
248
372
|
* Replaces profane words with a placeholder string.
|
|
249
373
|
*/
|
|
250
374
|
clean(text, placeholder = "*") {
|
|
251
375
|
if (!text) return "";
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
376
|
+
let result = "";
|
|
377
|
+
let last = 0;
|
|
378
|
+
for (const { start, end } of this.inspect(text)) {
|
|
379
|
+
result += text.slice(last, start) + placeholder.repeat(end - start);
|
|
380
|
+
last = end;
|
|
381
|
+
}
|
|
382
|
+
return result + text.slice(last);
|
|
256
383
|
}
|
|
257
384
|
/**
|
|
258
385
|
* Dynamically add words to the active dictionary.
|
|
259
386
|
*/
|
|
260
387
|
addWords(newWords) {
|
|
261
|
-
|
|
388
|
+
this.addEntries(newWords, "custom");
|
|
262
389
|
}
|
|
263
390
|
/**
|
|
264
391
|
* Remove words from the active filter dictionary.
|
|
265
392
|
*/
|
|
266
393
|
removeWords(wordsToRemove) {
|
|
267
|
-
wordsToRemove.forEach((word) => this.words.delete(this.
|
|
394
|
+
wordsToRemove.forEach((word) => this.words.delete(this.normalize(word)));
|
|
268
395
|
}
|
|
269
396
|
};
|
|
270
397
|
var index_default = IraqiBadWordsFilter;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// src/words.json
|
|
2
2
|
var words_default = [
|
|
3
3
|
"\u0639\u0627\u0631\u0629",
|
|
4
|
-
"\u0641\u0627\u0633\u062F",
|
|
5
4
|
"\u0643\u0633",
|
|
6
5
|
"\u0643\u0633\u0643",
|
|
7
6
|
"\u0643\u0633\u0647\u0627",
|
|
@@ -15,6 +14,12 @@ var words_default = [
|
|
|
15
14
|
"\u0639\u064A\u0631\u0647",
|
|
16
15
|
"\u0639\u064A\u0631\u0647\u0627",
|
|
17
16
|
"\u0639\u064A\u0631\u064A\u0646",
|
|
17
|
+
"\u0639\u064A\u063A",
|
|
18
|
+
"\u0639\u064A\u063A\u064A",
|
|
19
|
+
"\u0639\u064A\u063A\u0643",
|
|
20
|
+
"\u0639\u064A\u063A\u0647",
|
|
21
|
+
"\u0639\u064A\u063A\u0647\u0627",
|
|
22
|
+
"\u0639\u064A\u063A\u064A\u0646",
|
|
18
23
|
"\u0632\u0628",
|
|
19
24
|
"\u0632\u0628\u064A",
|
|
20
25
|
"\u0632\u0628\u0643",
|
|
@@ -155,9 +160,52 @@ var words_default = [
|
|
|
155
160
|
"zebak"
|
|
156
161
|
];
|
|
157
162
|
|
|
163
|
+
// src/insults.json
|
|
164
|
+
var insults_default = [
|
|
165
|
+
"\u0643\u0644\u0628",
|
|
166
|
+
"\u0643\u0644\u0627\u0628",
|
|
167
|
+
"\u0627\u0644\u0643\u0644\u0628",
|
|
168
|
+
"\u062C\u0644\u0628",
|
|
169
|
+
"\u062C\u0644\u0627\u0628",
|
|
170
|
+
"\u062D\u064A\u0648\u0627\u0646",
|
|
171
|
+
"\u062D\u064A\u0648\u0627\u0646\u0627\u062A",
|
|
172
|
+
"\u062D\u0645\u0627\u0631",
|
|
173
|
+
"\u062D\u0645\u0627\u0631\u0629",
|
|
174
|
+
"\u062D\u064E\u0645\u064A\u0631",
|
|
175
|
+
"\u062B\u0648\u0631",
|
|
176
|
+
"\u0642\u0646\u062F\u0631\u0629",
|
|
177
|
+
"\u0627\u0644\u0642\u0646\u062F\u0631\u0629",
|
|
178
|
+
"\u0642\u0646\u062F\u0631\u062A\u064A",
|
|
179
|
+
"\u0646\u0639\u0627\u0644",
|
|
180
|
+
"\u0627\u0644\u0646\u0639\u0627\u0644",
|
|
181
|
+
"\u0646\u0639\u0627\u0644\u064A",
|
|
182
|
+
"\u0645\u0633\u062D\u0627\u0629",
|
|
183
|
+
"\u0637\u0627\u0628\u0648\u0642\u0629",
|
|
184
|
+
"\u062E\u0627\u064A\u0633",
|
|
185
|
+
"\u062E\u0627\u064A\u0633\u0629",
|
|
186
|
+
"\u062E\u064A\u0633\u0629",
|
|
187
|
+
"\u0639\u0641\u0646",
|
|
188
|
+
"\u0639\u0641\u0646\u0629",
|
|
189
|
+
"\u0645\u0639\u0641\u0646",
|
|
190
|
+
"\u062A\u0641\u0644",
|
|
191
|
+
"\u062A\u0641\u0627\u0644\u0629",
|
|
192
|
+
"\u0639\u0642\u0631\u0648\u0643",
|
|
193
|
+
"\u0635\u0639\u0644\u0648\u0643",
|
|
194
|
+
"\u0637\u0643\u0639\u0629",
|
|
195
|
+
"\u0645\u0644\u0639\u0648\u0646",
|
|
196
|
+
"kalb",
|
|
197
|
+
"gandora",
|
|
198
|
+
"naal",
|
|
199
|
+
"khayis",
|
|
200
|
+
"7ayawan",
|
|
201
|
+
"7mar",
|
|
202
|
+
"ma3fan"
|
|
203
|
+
];
|
|
204
|
+
|
|
158
205
|
// src/safe-stems.json
|
|
159
206
|
var safe_stems_default = [
|
|
160
207
|
"\u0628\u0639\u064A\u0631",
|
|
208
|
+
"\u0628\u0639\u064A\u063A",
|
|
161
209
|
"\u0628\u0643\u0633",
|
|
162
210
|
"\u0648\u0643\u0633",
|
|
163
211
|
"\u0641\u0643\u0633"
|
|
@@ -168,11 +216,16 @@ var WORD_PATTERN = /[A-Za-z0-9_À-ɏ-؋؍-ؚ-؞ؠ-٩ٮ-ۓە-ۿݐ-ݿࢠ-ࣿ
|
|
|
168
216
|
var CONJUNCTIONS = ["", "\u0648", "\u0641"];
|
|
169
217
|
var PREPOSITIONS = ["", "\u0628", "\u0644", "\u0643"];
|
|
170
218
|
var ARTICLES = ["", "\u0627\u0644"];
|
|
219
|
+
var SUFFIXES = ["\u064A", "\u0643", "\u062C", "\u0647", "\u0647\u0627", "\u0646\u0627", "\u0643\u0645", "\u0647\u0645"];
|
|
171
220
|
var IraqiBadWordsFilter = class {
|
|
172
|
-
constructor(
|
|
173
|
-
|
|
174
|
-
this.
|
|
175
|
-
this.
|
|
221
|
+
constructor(options = {}) {
|
|
222
|
+
const { customWords = [], includeInsults = false, stripSuffixes = false } = Array.isArray(options) ? { customWords: options } : options;
|
|
223
|
+
this.words = /* @__PURE__ */ new Map();
|
|
224
|
+
this.stripSuffixes = stripSuffixes;
|
|
225
|
+
this.safeStems = safe_stems_default.map((word) => ({ normalized: this.normalize(word), word }));
|
|
226
|
+
this.addEntries(words_default, "words");
|
|
227
|
+
if (includeInsults) this.addEntries(insults_default, "insults");
|
|
228
|
+
this.addEntries(customWords, "custom");
|
|
176
229
|
}
|
|
177
230
|
/**
|
|
178
231
|
* Normalizes Arabic text so spelling variants compare equal:
|
|
@@ -180,30 +233,83 @@ var IraqiBadWordsFilter = class {
|
|
|
180
233
|
* forms, and collapses stretched letters (عاااارة -> عارة).
|
|
181
234
|
*/
|
|
182
235
|
normalizeArabic(text) {
|
|
183
|
-
return text.replace(/[
|
|
236
|
+
return text.replace(/[ً-ٰٟـ]/g, "").replace(/[أإآٱ]/g, "\u0627").replace(/ة/g, "\u0647").replace(/ى/g, "\u064A").replace(/(.)\1+/gu, "$1").toLowerCase();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Folds Arabizi (Arabic written in Latin letters and digits) so spelling
|
|
240
|
+
* variants compare equal: ga7ba / qahba / gahba, kosomak / kusomak,
|
|
241
|
+
* teezak / tizak. Only words made purely of Latin letters and digits are
|
|
242
|
+
* folded; expects text already passed through normalizeArabic.
|
|
243
|
+
*/
|
|
244
|
+
foldArabizi(word) {
|
|
245
|
+
if (!/^[a-z0-9]+$/.test(word) || !/[a-z]/.test(word)) return word;
|
|
246
|
+
return word.replace(/7/g, "h").replace(/5/g, "kh").replace(/[23]/g, "").replace(/q/g, "g").replace(/ou|o/g, "u").replace(/e/g, "i").replace(/(.)\1+/g, "$1");
|
|
247
|
+
}
|
|
248
|
+
/** Normalizes a word or token for comparison. */
|
|
249
|
+
normalize(text) {
|
|
250
|
+
return this.foldArabizi(this.normalizeArabic(text));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Adds words from one list. A word already present keeps its first source.
|
|
254
|
+
*/
|
|
255
|
+
addEntries(newWords, source) {
|
|
256
|
+
for (const word of newWords) {
|
|
257
|
+
const normalized = this.normalize(word);
|
|
258
|
+
if (!this.words.has(normalized)) this.words.set(normalized, { word, source });
|
|
259
|
+
}
|
|
184
260
|
}
|
|
185
261
|
/**
|
|
186
262
|
* Checks a single word, also trying it with attached prefixes removed
|
|
187
|
-
* (والكحبة -> كحبة, بطيزك -> طيزك).
|
|
263
|
+
* (والكحبة -> كحبة, بطيزك -> طيزك), and suffixes removed when stripSuffixes is on.
|
|
264
|
+
* Returns the matched entry, or null.
|
|
265
|
+
* A match cleared by a safe stem is returned with `suppressedBy` set.
|
|
188
266
|
*/
|
|
189
|
-
|
|
190
|
-
const word = this.
|
|
191
|
-
|
|
192
|
-
|
|
267
|
+
matchWord(token) {
|
|
268
|
+
const word = this.normalize(token);
|
|
269
|
+
const direct = this.words.get(word);
|
|
270
|
+
if (direct) {
|
|
271
|
+
return { normalized: word, entry: direct.word, source: direct.source, prefix: "", suffix: "" };
|
|
272
|
+
}
|
|
273
|
+
const candidates = [{ prefix: "", stem: word, suffix: "" }];
|
|
193
274
|
for (const conjunction of CONJUNCTIONS) {
|
|
194
275
|
for (const preposition of PREPOSITIONS) {
|
|
195
276
|
for (const article of ARTICLES) {
|
|
196
277
|
const prefix = conjunction + preposition + article;
|
|
197
278
|
if (prefix && word.startsWith(prefix) && word.length - prefix.length >= 2) {
|
|
198
|
-
|
|
279
|
+
candidates.push({ prefix, stem: word.slice(prefix.length), suffix: "" });
|
|
199
280
|
}
|
|
200
281
|
}
|
|
201
282
|
}
|
|
202
283
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
284
|
+
if (this.stripSuffixes) {
|
|
285
|
+
for (const { prefix, stem } of [...candidates]) {
|
|
286
|
+
for (const suffix of SUFFIXES) {
|
|
287
|
+
if (stem.endsWith(suffix) && stem.length - suffix.length >= 2) {
|
|
288
|
+
candidates.push({ prefix, stem: stem.slice(0, -suffix.length), suffix });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
205
292
|
}
|
|
206
|
-
|
|
293
|
+
const match = candidates.find(({ stem }) => this.words.has(stem));
|
|
294
|
+
if (!match) return null;
|
|
295
|
+
const { word: entry, source } = this.words.get(match.stem);
|
|
296
|
+
const result = {
|
|
297
|
+
normalized: word,
|
|
298
|
+
entry,
|
|
299
|
+
source,
|
|
300
|
+
prefix: match.prefix,
|
|
301
|
+
suffix: match.suffix
|
|
302
|
+
};
|
|
303
|
+
for (const { stem } of candidates) {
|
|
304
|
+
const safe = this.safeStems.find(({ normalized }) => stem.startsWith(normalized));
|
|
305
|
+
if (safe) return { ...result, suppressedBy: safe.word };
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
/** True when the word is profane, i.e. matched and not cleared by a safe stem. */
|
|
310
|
+
isProfaneWord(token) {
|
|
311
|
+
const match = this.matchWord(token);
|
|
312
|
+
return match !== null && match.suppressedBy === void 0;
|
|
207
313
|
}
|
|
208
314
|
/**
|
|
209
315
|
* Checks if a given text contains profane words.
|
|
@@ -217,27 +323,48 @@ var IraqiBadWordsFilter = class {
|
|
|
217
323
|
}
|
|
218
324
|
return false;
|
|
219
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Lists every bad word in the text with its position in the original input
|
|
328
|
+
* and why it matched, e.g. to highlight words or review a match before blocking.
|
|
329
|
+
*/
|
|
330
|
+
inspect(text, options = {}) {
|
|
331
|
+
if (!text) return [];
|
|
332
|
+
const matches = [];
|
|
333
|
+
const pattern = new RegExp(WORD_PATTERN.source, "g");
|
|
334
|
+
let found;
|
|
335
|
+
while (found = pattern.exec(text)) {
|
|
336
|
+
const token = found[0];
|
|
337
|
+
const match = this.matchWord(token);
|
|
338
|
+
if (match && (match.suppressedBy === void 0 || options.includeSuppressed)) {
|
|
339
|
+
matches.push({ token, start: found.index, end: found.index + token.length, ...match });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return matches;
|
|
343
|
+
}
|
|
220
344
|
/**
|
|
221
345
|
* Replaces profane words with a placeholder string.
|
|
222
346
|
*/
|
|
223
347
|
clean(text, placeholder = "*") {
|
|
224
348
|
if (!text) return "";
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
349
|
+
let result = "";
|
|
350
|
+
let last = 0;
|
|
351
|
+
for (const { start, end } of this.inspect(text)) {
|
|
352
|
+
result += text.slice(last, start) + placeholder.repeat(end - start);
|
|
353
|
+
last = end;
|
|
354
|
+
}
|
|
355
|
+
return result + text.slice(last);
|
|
229
356
|
}
|
|
230
357
|
/**
|
|
231
358
|
* Dynamically add words to the active dictionary.
|
|
232
359
|
*/
|
|
233
360
|
addWords(newWords) {
|
|
234
|
-
|
|
361
|
+
this.addEntries(newWords, "custom");
|
|
235
362
|
}
|
|
236
363
|
/**
|
|
237
364
|
* Remove words from the active filter dictionary.
|
|
238
365
|
*/
|
|
239
366
|
removeWords(wordsToRemove) {
|
|
240
|
-
wordsToRemove.forEach((word) => this.words.delete(this.
|
|
367
|
+
wordsToRemove.forEach((word) => this.words.delete(this.normalize(word)));
|
|
241
368
|
}
|
|
242
369
|
};
|
|
243
370
|
var index_default = IraqiBadWordsFilter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iraqi-dialect-bad-words",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "An Iraqi dialect profanity filter for React, React Native Expo, and Node.js",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"default": "./dist/index.js"
|
|
17
17
|
}
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"dist"
|