opengrammar-server 2.0.615350

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.
Files changed (53) hide show
  1. package/README.npm.md +95 -0
  2. package/bin/opengrammar-server.js +111 -0
  3. package/dist/server.js +48639 -0
  4. package/package.json +80 -0
  5. package/server-node.ts +159 -0
  6. package/server.ts +15 -0
  7. package/src/analyzer.ts +542 -0
  8. package/src/dictionary.ts +1973 -0
  9. package/src/index.ts +978 -0
  10. package/src/nlp/nlp-engine.ts +17 -0
  11. package/src/nlp/tone-analyzer.ts +269 -0
  12. package/src/rephraser.ts +146 -0
  13. package/src/rules/categories/academic-writing.ts +182 -0
  14. package/src/rules/categories/adjectives-adverbs.ts +152 -0
  15. package/src/rules/categories/articles.ts +160 -0
  16. package/src/rules/categories/business-writing.ts +250 -0
  17. package/src/rules/categories/capitalization.ts +79 -0
  18. package/src/rules/categories/clarity.ts +117 -0
  19. package/src/rules/categories/common-errors.ts +601 -0
  20. package/src/rules/categories/confused-words.ts +219 -0
  21. package/src/rules/categories/conjunctions.ts +176 -0
  22. package/src/rules/categories/dangling-modifiers.ts +123 -0
  23. package/src/rules/categories/formality.ts +274 -0
  24. package/src/rules/categories/formatting-idioms.ts +323 -0
  25. package/src/rules/categories/gerund-infinitive.ts +274 -0
  26. package/src/rules/categories/grammar-advanced.ts +294 -0
  27. package/src/rules/categories/grammar.ts +286 -0
  28. package/src/rules/categories/inclusive-language.ts +280 -0
  29. package/src/rules/categories/nouns-pronouns.ts +233 -0
  30. package/src/rules/categories/prepositions-extended.ts +217 -0
  31. package/src/rules/categories/prepositions.ts +159 -0
  32. package/src/rules/categories/punctuation.ts +347 -0
  33. package/src/rules/categories/quantity-agreement.ts +200 -0
  34. package/src/rules/categories/readability.ts +293 -0
  35. package/src/rules/categories/sentence-structure.ts +100 -0
  36. package/src/rules/categories/spelling-advanced.ts +164 -0
  37. package/src/rules/categories/spelling.ts +119 -0
  38. package/src/rules/categories/style-tone.ts +511 -0
  39. package/src/rules/categories/style.ts +78 -0
  40. package/src/rules/categories/subject-verb-agreement.ts +201 -0
  41. package/src/rules/categories/tone-rules.ts +206 -0
  42. package/src/rules/categories/verb-tense.ts +582 -0
  43. package/src/rules/context-filter.ts +446 -0
  44. package/src/rules/index.ts +96 -0
  45. package/src/rules/ruleset-part1-cj-pu-sp.json +657 -0
  46. package/src/rules/ruleset-part1-np-ad-aa-pr.json +831 -0
  47. package/src/rules/ruleset-part1-ss-vt.json +907 -0
  48. package/src/rules/ruleset-part2-cw-st-nf.json +318 -0
  49. package/src/rules/ruleset-part3-aw-bw-il-rd.json +161 -0
  50. package/src/rules/types.ts +79 -0
  51. package/src/shared-types.ts +152 -0
  52. package/src/spellchecker.ts +418 -0
  53. package/tsconfig.json +25 -0
@@ -0,0 +1,293 @@
1
+ import type { Issue } from '../../shared-types.js';
2
+ import { createRegexRule, type Rule } from '../types.js';
3
+
4
+ /**
5
+ * ═══════════════════════════════════════════════════
6
+ * Readability & Simplification (RD)
7
+ * Complex words, sentence length, paragraph length
8
+ * ═══════════════════════════════════════════════════
9
+ */
10
+ export const readabilityRules: Rule[] = [
11
+ // ═══ Complex Word Simplification (RD_003) ═══
12
+ createRegexRule({
13
+ id: 'RD_utilize',
14
+ category: 'clarity',
15
+ pattern: /\butilize\b/i,
16
+ suggestion: 'use',
17
+ reason: '"Utilize" can almost always be replaced with the simpler "use".',
18
+ }),
19
+ createRegexRule({
20
+ id: 'RD_demonstrate',
21
+ category: 'clarity',
22
+ pattern: /\bdemonstrate\b/i,
23
+ suggestion: 'show',
24
+ reason: '"Demonstrate" can often be simplified to "show".',
25
+ }),
26
+ createRegexRule({
27
+ id: 'RD_approximately',
28
+ category: 'clarity',
29
+ pattern: /\bapproximately\b/i,
30
+ suggestion: 'about',
31
+ reason: '"Approximately" can be simplified to "about" or "roughly".',
32
+ }),
33
+ createRegexRule({
34
+ id: 'RD_subsequent',
35
+ category: 'clarity',
36
+ pattern: /\bsubsequent\b/i,
37
+ suggestion: 'next',
38
+ reason: '"Subsequent" can be simplified to "next" or "following".',
39
+ }),
40
+ createRegexRule({
41
+ id: 'RD_prior_to',
42
+ category: 'clarity',
43
+ pattern: /\bprior\s+to\b/i,
44
+ suggestion: 'before',
45
+ reason: '"Prior to" can be simplified to "before".',
46
+ }),
47
+ createRegexRule({
48
+ id: 'RD_sufficient',
49
+ category: 'clarity',
50
+ pattern: /\bsufficient\b/i,
51
+ suggestion: 'enough',
52
+ reason: '"Sufficient" can be simplified to "enough".',
53
+ }),
54
+ createRegexRule({
55
+ id: 'RD_nevertheless',
56
+ category: 'clarity',
57
+ pattern: /\bnevertheless\b/i,
58
+ suggestion: 'still',
59
+ reason: '"Nevertheless" can often be simplified to "still", "yet", or "however".',
60
+ }),
61
+ createRegexRule({
62
+ id: 'RD_notwithstanding',
63
+ category: 'clarity',
64
+ pattern: /\bnotwithstanding\b/i,
65
+ suggestion: 'despite',
66
+ reason: '"Notwithstanding" can be simplified to "despite" or "in spite of".',
67
+ }),
68
+ createRegexRule({
69
+ id: 'RD_heretofore',
70
+ category: 'clarity',
71
+ pattern: /\bheretofore\b/i,
72
+ suggestion: 'previously',
73
+ reason: '"Heretofore" is archaic. Use "previously" or "until now".',
74
+ }),
75
+ createRegexRule({
76
+ id: 'RD_aforementioned',
77
+ category: 'clarity',
78
+ pattern: /\baforementioned\b/i,
79
+ suggestion: 'previous',
80
+ reason: '"Aforementioned" is overly formal. Use "previous", "earlier", or just name the thing.',
81
+ }),
82
+ createRegexRule({
83
+ id: 'RD_inasmuch',
84
+ category: 'clarity',
85
+ pattern: /\binasmuch\s+as\b/i,
86
+ suggestion: 'since',
87
+ reason: '"Inasmuch as" is archaic. Use "since" or "because".',
88
+ }),
89
+ createRegexRule({
90
+ id: 'RD_henceforth',
91
+ category: 'clarity',
92
+ pattern: /\bhenceforth\b/i,
93
+ suggestion: 'from now on',
94
+ reason: '"Henceforth" is archaic. Use "from now on".',
95
+ }),
96
+ createRegexRule({
97
+ id: 'RD_moreover',
98
+ category: 'clarity',
99
+ pattern: /\bmoreover\b/i,
100
+ suggestion: 'also',
101
+ reason: '"Moreover" can usually be simplified to "also" or "besides".',
102
+ }),
103
+ createRegexRule({
104
+ id: 'RD_furthermore',
105
+ category: 'clarity',
106
+ pattern: /\bfurthermore\b/i,
107
+ suggestion: 'also',
108
+ reason: '"Furthermore" can usually be simplified to "also" or "in addition".',
109
+ }),
110
+ createRegexRule({
111
+ id: 'RD_consequently',
112
+ category: 'clarity',
113
+ pattern: /\bconsequently\b/i,
114
+ suggestion: 'so',
115
+ reason: '"Consequently" can often be simplified to "so" or "as a result".',
116
+ }),
117
+ createRegexRule({
118
+ id: 'RD_predominantly',
119
+ category: 'clarity',
120
+ pattern: /\bpredominantly\b/i,
121
+ suggestion: 'mainly',
122
+ reason: '"Predominantly" can be simplified to "mainly" or "mostly".',
123
+ }),
124
+ createRegexRule({
125
+ id: 'RD_necessitate',
126
+ category: 'clarity',
127
+ pattern: /\bnecessitate\b/i,
128
+ suggestion: 'require',
129
+ reason: '"Necessitate" can be simplified to "require" or "need".',
130
+ }),
131
+ createRegexRule({
132
+ id: 'RD_endeavour',
133
+ category: 'clarity',
134
+ pattern: /\bendeavou?r\b/i,
135
+ suggestion: 'try',
136
+ reason: '"Endeavor" is unnecessarily formal. Use "try" or "attempt".',
137
+ }),
138
+ createRegexRule({
139
+ id: 'RD_commensurate',
140
+ category: 'clarity',
141
+ pattern: /\bcommensurate\b/i,
142
+ suggestion: 'proportional',
143
+ reason: '"Commensurate" can be simplified to "proportional" or "matching".',
144
+ }),
145
+ createRegexRule({
146
+ id: 'RD_multifaceted',
147
+ category: 'clarity',
148
+ pattern: /\bmultifaceted\b/i,
149
+ suggestion: 'complex',
150
+ reason: '"Multifaceted" can be simplified to "complex" or "varied".',
151
+ }),
152
+ createRegexRule({
153
+ id: 'RD_dichotomy',
154
+ category: 'clarity',
155
+ pattern: /\bdichotomy\b/i,
156
+ suggestion: 'divide',
157
+ reason: '"Dichotomy" can be simplified to "divide", "split", or "contrast".',
158
+ }),
159
+ createRegexRule({
160
+ id: 'RD_ubiquitous',
161
+ category: 'clarity',
162
+ pattern: /\bubiquitous\b/i,
163
+ suggestion: 'widespread',
164
+ reason: '"Ubiquitous" can be simplified to "widespread" or "everywhere".',
165
+ }),
166
+ createRegexRule({
167
+ id: 'RD_juxtapose',
168
+ category: 'clarity',
169
+ pattern: /\bjuxtapose\b/i,
170
+ suggestion: 'compare',
171
+ reason: '"Juxtapose" can be simplified to "compare" or "place side by side".',
172
+ }),
173
+ createRegexRule({
174
+ id: 'RD_ameliorate',
175
+ category: 'clarity',
176
+ pattern: /\bameliorate\b/i,
177
+ suggestion: 'improve',
178
+ reason: '"Ameliorate" can be simplified to "improve" or "make better".',
179
+ }),
180
+ createRegexRule({
181
+ id: 'RD_exacerbate',
182
+ category: 'clarity',
183
+ pattern: /\bexacerbate\b/i,
184
+ suggestion: 'worsen',
185
+ reason: '"Exacerbate" can be simplified to "worsen" or "make worse".',
186
+ }),
187
+ createRegexRule({
188
+ id: 'RD_proliferate',
189
+ category: 'clarity',
190
+ pattern: /\bproliferate\b/i,
191
+ suggestion: 'spread',
192
+ reason: '"Proliferate" can be simplified to "spread" or "increase rapidly".',
193
+ }),
194
+ createRegexRule({
195
+ id: 'RD_myriad',
196
+ category: 'clarity',
197
+ pattern: /\ba\s+myriad\s+of\b/i,
198
+ suggestion: 'many',
199
+ reason: '"A myriad of" is wordy. Use "many" or "countless". (Or: "myriad" without "a...of").',
200
+ }),
201
+ createRegexRule({
202
+ id: 'RD_plethora',
203
+ category: 'clarity',
204
+ pattern: /\ba\s+plethora\s+of\b/i,
205
+ suggestion: 'many',
206
+ reason: '"A plethora of" is wordy. Use "many", "plenty of", or "an abundance of".',
207
+ }),
208
+ createRegexRule({
209
+ id: 'RD_quintessential',
210
+ category: 'clarity',
211
+ pattern: /\bquintessential\b/i,
212
+ suggestion: 'classic',
213
+ reason: '"Quintessential" can be simplified to "classic", "ideal", or "perfect example of".',
214
+ }),
215
+ createRegexRule({
216
+ id: 'RD_paradigm',
217
+ category: 'clarity',
218
+ pattern: /\bparadigm\b/i,
219
+ suggestion: 'model',
220
+ reason: '"Paradigm" is overused jargon. Use "model", "framework", or "pattern".',
221
+ }),
222
+
223
+ // ═══ Very Long Sentences (RD_001) ═══
224
+ {
225
+ id: 'RD_long_sentence',
226
+ type: 'regex',
227
+ category: 'clarity',
228
+ pattern: /[^.!?]+[.!?]+/g,
229
+ reason: 'Very long sentence detected.',
230
+ suggestion: 'Consider breaking this into shorter sentences.',
231
+ check: (text: string): Issue[] => {
232
+ const issues: Issue[] = [];
233
+ const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
234
+ let offset = 0;
235
+ for (const sentence of sentences) {
236
+ const wordCount = sentence.trim().split(/\s+/).length;
237
+ if (wordCount > 40) {
238
+ issues.push({
239
+ id: `RD_long_sentence-${offset}`,
240
+ type: 'clarity',
241
+ original: sentence.trim().substring(0, 60) + '...',
242
+ suggestion: 'Consider breaking this into shorter sentences.',
243
+ reason: `This sentence has ${wordCount} words. Sentences over 40 words are difficult to read.`,
244
+ offset,
245
+ length: sentence.length,
246
+ });
247
+ }
248
+ offset += sentence.length;
249
+ }
250
+ return issues;
251
+ },
252
+ },
253
+
254
+ // ═══ Repeated Sentence Beginnings (RD_005) ═══
255
+ {
256
+ id: 'RD_repeated_starts',
257
+ type: 'regex',
258
+ category: 'style',
259
+ pattern: /[^.!?]+[.!?]+/g,
260
+ reason: 'Repeated sentence beginnings reduce readability.',
261
+ suggestion: 'Vary your sentence openings.',
262
+ check: (text: string): Issue[] => {
263
+ const issues: Issue[] = [];
264
+ const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
265
+ if (sentences.length < 3) return issues;
266
+
267
+ let offset = 0;
268
+ for (let i = 2; i < sentences.length; i++) {
269
+ const getStart = (s: string) => (s.trim().split(/\s+/)[0] || '').toLowerCase();
270
+ const w1 = getStart(sentences[i - 2] || '');
271
+ const w2 = getStart(sentences[i - 1] || '');
272
+ const w3 = getStart(sentences[i] || '');
273
+
274
+ if (w1 && w1 === w2 && w2 === w3 && !['the', 'a', 'an', 'i'].includes(w1)) {
275
+ const sentenceOffset = text.indexOf((sentences[i] || '').trim(), offset);
276
+ if (sentenceOffset >= 0) {
277
+ issues.push({
278
+ id: `RD_repeated_starts-${sentenceOffset}`,
279
+ type: 'style',
280
+ original: (sentences[i] || '').trim().substring(0, 40) + '...',
281
+ suggestion: 'Vary your sentence openings for better flow.',
282
+ reason: `Three consecutive sentences start with "${w1}". Vary your sentence beginnings.`,
283
+ offset: sentenceOffset,
284
+ length: (sentences[i] || '').trim().length,
285
+ });
286
+ }
287
+ }
288
+ offset += (sentences[i - 2] || '').length;
289
+ }
290
+ return issues;
291
+ },
292
+ },
293
+ ];
@@ -0,0 +1,100 @@
1
+ import type { Issue } from '../../shared-types.js';
2
+ import { createRegexRule, type Rule } from '../types.js';
3
+
4
+ export const sentenceStructureRules: Rule[] = [
5
+ // ═══ Subject-Verb Agreement (SS_SVA) ═══
6
+
7
+ // SS_SVA_001 — Singular subjects with plural verbs
8
+ createRegexRule({
9
+ id: 'SS_SVA_001a',
10
+ category: 'grammar',
11
+ pattern:
12
+ /\b(he|she|it)\s+(are|were|have|do|go|need|want|make|take|come|give|say|seem|know|think)\b/i,
13
+ suggestion: (m) =>
14
+ `${m[1]} ${({ are: 'is', were: 'was', have: 'has', do: 'does', go: 'goes', need: 'needs', want: 'wants', make: 'makes', take: 'takes', come: 'comes', give: 'gives', say: 'says', seem: 'seems', know: 'knows', think: 'thinks' } as Record<string, string>)[(m[2] || '').toLowerCase()] || m[2]}`,
15
+ reason: 'Singular subject requires a singular verb.',
16
+ }),
17
+
18
+ // SS_SVA_003 — Indefinite pronouns + plural verbs
19
+ createRegexRule({
20
+ id: 'SS_SVA_003a',
21
+ category: 'grammar',
22
+ pattern:
23
+ /\b(everyone|someone|anybody|somebody|nobody|no\s+one|each|either|neither)\s+(are|were|have|do|go|seem|need|want)\b/i,
24
+ suggestion: (m) =>
25
+ `${m[1]} ${({ are: 'is', were: 'was', have: 'has', do: 'does', go: 'goes', seem: 'seems', need: 'needs', want: 'wants' } as Record<string, string>)[(m[2] || '').toLowerCase()] || m[2]}`,
26
+ reason: 'Indefinite pronouns (everyone, someone, each, neither, etc.) take singular verbs.',
27
+ }),
28
+
29
+ // SS_SVA_004 — Collective nouns (AmE singular)
30
+ createRegexRule({
31
+ id: 'SS_SVA_004a',
32
+ category: 'grammar',
33
+ pattern:
34
+ /\b(the\s+)?(team|committee|family|group|staff|jury|audience|crowd|class|government|company|board|council|panel)\s+(are|were|have|do)\b/i,
35
+ suggestion: (m) =>
36
+ `${m[1] || ''}${m[2]} ${({ are: 'is', were: 'was', have: 'has', do: 'does' } as Record<string, string>)[(m[3] || '').toLowerCase()] || m[3]}`,
37
+ reason: 'Collective nouns take singular verbs in American English.',
38
+ }),
39
+
40
+ // SS_SVA_007 — There is/are agreement
41
+ createRegexRule({
42
+ id: 'SS_SVA_007a',
43
+ category: 'grammar',
44
+ pattern:
45
+ /\bthere\s+is\s+(many|several|numerous|various|multiple|two|three|four|five|six|seven|eight|nine|ten)\b/i,
46
+ suggestion: (m) => `there are ${m[1]}`,
47
+ reason: 'Use "are" with plural subjects after "there".',
48
+ }),
49
+ createRegexRule({
50
+ id: 'SS_SVA_007b',
51
+ category: 'grammar',
52
+ pattern:
53
+ /\bthere\s+was\s+(many|several|numerous|various|multiple|two|three|four|five|six|seven|eight|nine|ten)\b/i,
54
+ suggestion: (m) => `there were ${m[1]}`,
55
+ reason: 'Use "were" with plural subjects after "there".',
56
+ }),
57
+
58
+ // ═══ Double Subjects (SS_DBS) ═══
59
+ createRegexRule({
60
+ id: 'SS_DBS_001a',
61
+ category: 'grammar',
62
+ pattern:
63
+ /\b(my\s+\w+|the\s+\w+)\s+(he|she|it|they)\s+(is|are|was|were|has|have|does|do|will|would|can|could|should|shall|might|must)\b/i,
64
+ suggestion: (m) => `${m[1]} ${m[3]}`,
65
+ reason: 'Redundant pronoun after noun subject. Remove the extra pronoun.',
66
+ }),
67
+
68
+ // ═══ Faulty Predication (SS_FPR) ═══
69
+ createRegexRule({
70
+ id: 'SS_FPR_001',
71
+ category: 'grammar',
72
+ pattern: /\bthe\s+reason\s+(is|was)\s+because\b/i,
73
+ suggestion: (m) => `the reason ${m[1]} that`,
74
+ reason: '"The reason is because" is redundant. Use "the reason is that".',
75
+ }),
76
+ createRegexRule({
77
+ id: 'SS_FPR_002',
78
+ category: 'grammar',
79
+ pattern:
80
+ /\b(the\s+problem|the\s+issue|the\s+question|the\s+difficulty)\s+(is|was)\s+(when|where|if)\b/i,
81
+ suggestion: (m) => `${m[1]} ${m[2]} that`,
82
+ reason: 'A noun subject should not be equated with an adverb clause. Use "that" instead.',
83
+ }),
84
+
85
+ // ═══ Parallel Structure with Correlatives (SS_PAR) ═══
86
+ createRegexRule({
87
+ id: 'SS_PAR_not_only',
88
+ category: 'grammar',
89
+ pattern: /\bnot\s+only\s+(\w+)\s+but\s+also\s+is\s+(\w+ing)\b/i,
90
+ suggestion: (m) => `not only ${m[1]} but also ${m[2]}`,
91
+ reason: 'Correlative conjunctions require parallel structure.',
92
+ }),
93
+ createRegexRule({
94
+ id: 'SS_PAR_both_and',
95
+ category: 'grammar',
96
+ pattern: /\bboth\s+likes?\s+to\s+(\w+)\s+and\s+(\w+ing)\b/i,
97
+ suggestion: (m) => `both ${m[1]}ing and ${m[2]}`,
98
+ reason: 'Items in "both...and" must be parallel — use the same grammatical form.',
99
+ }),
100
+ ];
@@ -0,0 +1,164 @@
1
+ import { createRegexRule, type Rule } from '../types.js';
2
+
3
+ export const spellingAdvancedRules: Rule[] = [
4
+ // Confused Words
5
+ createRegexRule({
6
+ pattern: /\bthe\s+affect\b/i,
7
+ suggestion: 'the effect',
8
+ reason: 'Use "effect" (noun). "Affect" is usually a verb.',
9
+ id: 'confuse-the-affect',
10
+ category: 'spelling',
11
+ }),
12
+ createRegexRule({
13
+ pattern: /\ban\s+affect\b/i,
14
+ suggestion: 'an effect',
15
+ reason: 'Use "effect" (noun). "Affect" is usually a verb.',
16
+ id: 'confuse-an-affect',
17
+ category: 'spelling',
18
+ }),
19
+ createRegexRule({
20
+ pattern: /\bhas\s+no\s+affect\b/i,
21
+ suggestion: 'has no effect',
22
+ reason: 'Use "effect" (noun) here.',
23
+ id: 'confuse-no-affect',
24
+ category: 'spelling',
25
+ }),
26
+ createRegexRule({
27
+ pattern: /\beffect\s+(the|a|his|her|their|our|my|your|its)\b/i,
28
+ suggestion: (m) => `affect ${m[1]}`,
29
+ reason: 'Use "affect" (verb) when meaning "to influence".',
30
+ id: 'effect-determiner',
31
+ category: 'spelling',
32
+ }),
33
+
34
+ createRegexRule({
35
+ pattern:
36
+ /\b(bigger|smaller|better|worse|more|less|faster|slower|higher|lower|greater|fewer|older|younger|earlier|later|longer|shorter|taller|stronger|weaker|easier|harder|smarter|richer|poorer|cheaper|nicer|closer)\s+then\b/i,
37
+ suggestion: (m) => `${m[1]} than`,
38
+ reason: 'Use "than" for comparisons, not "then".',
39
+ id: 'then-than',
40
+ category: 'spelling',
41
+ }),
42
+
43
+ createRegexRule({
44
+ pattern: /\b(will|might|could|would|going to|gonna|dont want to|don't want to)\s+loose\b/i,
45
+ suggestion: (m) => `${m[1]} lose`,
46
+ reason: 'Use "lose" (verb, opposite of win/find). "Loose" means not tight.',
47
+ id: 'loose-lose',
48
+ category: 'spelling',
49
+ }),
50
+
51
+ createRegexRule({
52
+ pattern:
53
+ /\bexcept\s+(the|his|her|their|our|my|your|this|that|an?)\s+(offer|invitation|terms|conditions|award|gift|prize|challenge|proposal|request|apology|responsibility)\b/i,
54
+ suggestion: (m) => `accept ${m[1]} ${m[2]}`,
55
+ reason: 'Use "accept" (to receive). "Except" means excluding.',
56
+ id: 'except-accept',
57
+ category: 'spelling',
58
+ }),
59
+
60
+ createRegexRule({
61
+ pattern: /\bto\s+who\b/i,
62
+ suggestion: 'to whom',
63
+ reason: 'Use "whom" after a preposition (to, for, with, by).',
64
+ id: 'to-whom',
65
+ category: 'spelling',
66
+ }),
67
+ createRegexRule({
68
+ pattern: /\bfor\s+who\b/i,
69
+ suggestion: 'for whom',
70
+ reason: 'Use "whom" after a preposition.',
71
+ id: 'for-whom',
72
+ category: 'spelling',
73
+ }),
74
+ createRegexRule({
75
+ pattern: /\bwith\s+who\b/i,
76
+ suggestion: 'with whom',
77
+ reason: 'Use "whom" after a preposition.',
78
+ id: 'with-whom',
79
+ category: 'spelling',
80
+ }),
81
+ createRegexRule({
82
+ pattern: /\bby\s+who\b/i,
83
+ suggestion: 'by whom',
84
+ reason: 'Use "whom" after a preposition.',
85
+ id: 'by-whom',
86
+ category: 'spelling',
87
+ }),
88
+
89
+ createRegexRule({
90
+ pattern: /\bcomplement\s+(him|her|them|you|me|us)\b/i,
91
+ suggestion: (m) => `compliment ${m[1]}`,
92
+ reason: 'Use "compliment" (praise). "Complement" means to complete.',
93
+ id: 'complement-compliment',
94
+ category: 'spelling',
95
+ }),
96
+
97
+ createRegexRule({
98
+ pattern: /\bthe\s+principle\s+(of\s+the\s+school|said|announced|decided)\b/i,
99
+ suggestion: (m) => `the principal ${m[1]}`,
100
+ reason: 'Use "principal" for a school leader. "Principle" is a rule or belief.',
101
+ id: 'principal-principle',
102
+ category: 'spelling',
103
+ }),
104
+
105
+ createRegexRule({
106
+ pattern: /\bweather\s+(or\s+not|we|you|they|he|she|it|I|to)\b/i,
107
+ suggestion: (m) => `whether ${m[1]}`,
108
+ reason: 'Use "whether" for conditions/choices. "Weather" refers to climate.',
109
+ id: 'weather-whether',
110
+ category: 'spelling',
111
+ }),
112
+
113
+ createRegexRule({
114
+ pattern: /\bcan't\s+bare\b/i,
115
+ suggestion: "can't bear",
116
+ reason: 'Use "bear" (to tolerate). "Bare" means naked or uncovered.',
117
+ id: 'bare-bear',
118
+ category: 'spelling',
119
+ }),
120
+
121
+ createRegexRule({
122
+ pattern: /\bpeek\s+(interest|curiosity)\b/i,
123
+ suggestion: (m) => `pique ${m[1]}`,
124
+ reason: 'Use "pique" (to stimulate). "Peek" means to look quickly.',
125
+ id: 'peek-pique',
126
+ category: 'spelling',
127
+ }),
128
+ createRegexRule({
129
+ pattern: /\bpeak\s+(interest|curiosity)\b/i,
130
+ suggestion: (m) => `pique ${m[1]}`,
131
+ reason: 'Use "pique" (to stimulate). "Peak" means the top.',
132
+ id: 'peak-pique',
133
+ category: 'spelling',
134
+ }),
135
+
136
+ // Its vs It
137
+ createRegexRule({
138
+ id: 'its-wrong',
139
+ category: 'spelling',
140
+ pattern:
141
+ /\bits\s+(been|become|becoming|seemed|seems|appeared|appears|gotten|made|done|said|written|created)\b/i,
142
+ suggestion: (m) => `it's ${m[1]}`,
143
+ reason: "Use 'it's' (contraction) when you mean 'it is' or 'it has'.",
144
+ }),
145
+
146
+ // Their Location
147
+ createRegexRule({
148
+ id: 'there-location',
149
+ category: 'spelling',
150
+ pattern: /\bover\s+their\b/i,
151
+ suggestion: 'over there',
152
+ reason: "Use 'there' for locations, not 'their' (possessive).",
153
+ }),
154
+
155
+ // Your vs You're
156
+ createRegexRule({
157
+ id: 'your-should-be-youre',
158
+ category: 'spelling',
159
+ pattern:
160
+ /\byour\s+(welcome|absolutely|right|wrong|amazing|awesome|incredible|fantastic|wonderful|great|excellent|perfect|beautiful|stunning|gorgeous|brilliant|smart|intelligent|talented|skilled|experienced|qualified|prepared|ready|finished|done|complete|correct|incorrect|mistaken|confused|lost|found|gone|here|there|early|late|busy|free|available|unavailable|important|necessary|essential|critical|vital|crucial|key|main|primary|principal|chief|major|minor|significant|relevant|appropriate|suitable|fitting|proper|correct|right|wrong|bad|good|better|best|worse|worst)\b/i,
161
+ suggestion: (m) => `you're ${m[1]}`,
162
+ reason: "Use 'you're' (contraction of 'you are') here.",
163
+ }),
164
+ ];
@@ -0,0 +1,119 @@
1
+ import { createRegexRule, type Rule } from '../types.js';
2
+
3
+ const commonMisspellings: Record<string, string> = {
4
+ teh: 'the',
5
+ taht: 'that',
6
+ waht: 'what',
7
+ whta: 'what',
8
+ hte: 'the',
9
+ iwth: 'with',
10
+ witht: 'with',
11
+ adn: 'and',
12
+ nad: 'and',
13
+ abd: 'bad',
14
+ becuase: 'because',
15
+ becasue: 'because',
16
+ beacuse: 'because',
17
+ becomeing: 'becoming',
18
+ begining: 'beginning',
19
+ believeable: 'believable',
20
+ buisness: 'business',
21
+ calender: 'calendar',
22
+ cant: "can't",
23
+ collegue: 'colleague',
24
+ comming: 'coming',
25
+ completly: 'completely',
26
+ definately: 'definitely',
27
+ definitly: 'definitely',
28
+ dissapear: 'disappear',
29
+ dissapoint: 'disappoint',
30
+ embarass: 'embarrass',
31
+ enviroment: 'environment',
32
+ existance: 'existence',
33
+ experiance: 'experience',
34
+ familar: 'familiar',
35
+ finaly: 'finally',
36
+ freind: 'friend',
37
+ goverment: 'government',
38
+ governer: 'governor',
39
+ grammer: 'grammar',
40
+ happend: 'happened',
41
+ happenned: 'happened',
42
+ harrass: 'harass',
43
+ heighth: 'height',
44
+ helpfull: 'helpful',
45
+ immediatly: 'immediately',
46
+ independant: 'independent',
47
+ indispensible: 'indispensable',
48
+ irresistable: 'irresistible',
49
+ knowlege: 'knowledge',
50
+ libary: 'library',
51
+ lisence: 'license',
52
+ maintainance: 'maintenance',
53
+ millenium: 'millennium',
54
+ minature: 'miniature',
55
+ mischievious: 'mischievous',
56
+ misspell: 'misspell',
57
+ neccessary: 'necessary',
58
+ necessery: 'necessary',
59
+ noticable: 'noticeable',
60
+ occassion: 'occasion',
61
+ occured: 'occurred',
62
+ occuring: 'occurring',
63
+ occurence: 'occurrence',
64
+ parliment: 'parliament',
65
+ peice: 'piece',
66
+ persistance: 'persistence',
67
+ persue: 'pursue',
68
+ posession: 'possession',
69
+ potatos: 'potatoes',
70
+ preceed: 'precede',
71
+ presance: 'presence',
72
+ privelege: 'privilege',
73
+ publically: 'publicly',
74
+ questionaire: 'questionnaire',
75
+ realy: 'really',
76
+ recieve: 'receive',
77
+ recomend: 'recommend',
78
+ refered: 'referred',
79
+ refering: 'referring',
80
+ relevent: 'relevant',
81
+ reminisce: 'reminisce',
82
+ repitition: 'repetition',
83
+ resistence: 'resistance',
84
+ seperate: 'separate',
85
+ similer: 'similar',
86
+ sincerly: 'sincerely',
87
+ speach: 'speech',
88
+ strenght: 'strength',
89
+ succesful: 'successful',
90
+ suprise: 'surprise',
91
+ tendancy: 'tendency',
92
+ therefor: 'therefore',
93
+ tommorrow: 'tomorrow',
94
+ tongue: 'tongue',
95
+ truely: 'truly',
96
+ unfortunatly: 'unfortunately',
97
+ untill: 'until',
98
+ unusuall: 'unusual',
99
+ usefull: 'useful',
100
+ vaccum: 'vacuum',
101
+ vegatable: 'vegetable',
102
+ visious: 'vicious',
103
+ wether: 'whether',
104
+ wich: 'which',
105
+ writting: 'writing',
106
+ yache: 'yacht',
107
+ yeild: 'yield',
108
+ yourselfs: 'yourselves',
109
+ };
110
+
111
+ export const spellingRules: Rule[] = Object.entries(commonMisspellings).map(([wrong, correct]) =>
112
+ createRegexRule({
113
+ id: `spell-${wrong}`,
114
+ category: 'spelling',
115
+ pattern: new RegExp(`\\b${wrong}\\b`, 'i'),
116
+ suggestion: correct,
117
+ reason: `Misspelled word. The correct spelling is "${correct}".`,
118
+ }),
119
+ );