@codemerge/hunspell 2.4.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/dist/index.mjs ADDED
@@ -0,0 +1,434 @@
1
+ function escapeRegExp(text) {
2
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3
+ }
4
+ //#endregion
5
+ //#region src/check.ts
6
+ /** Typographic apostrophe → ASCII (dictionary-en stems use U+0027). */
7
+ function normalizeWord(word) {
8
+ return word.replaceAll("’", "'");
9
+ }
10
+ function flattenFlags(ruleCodes) {
11
+ if (ruleCodes === null || ruleCodes === void 0) return [];
12
+ return ruleCodes.flat();
13
+ }
14
+ /**
15
+ * Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).
16
+ */
17
+ function hasFlag(state, word, flag, wordFlags) {
18
+ const flagValue = state.flags[flag];
19
+ if (flagValue === void 0) return false;
20
+ return (wordFlags ?? flattenFlags(state.dictionaryTable.get(word) ?? void 0)).includes(flagValue);
21
+ }
22
+ /**
23
+ * Exact dictionary lookup (no capitalization folding). Also matches compound rules.
24
+ */
25
+ function checkExact(state, word) {
26
+ if (word.length > 256) return false;
27
+ const ruleCodes = state.dictionaryTable.get(word);
28
+ if (ruleCodes === void 0) {
29
+ const compoundMin = state.flags.COMPOUNDMIN;
30
+ if (compoundMin !== void 0 && word.length >= Number(compoundMin) && word.length <= 256) {
31
+ for (const rule of state.compoundRules) if (rule.test(word)) return true;
32
+ }
33
+ return false;
34
+ }
35
+ if (ruleCodes === null) return true;
36
+ for (const codes of ruleCodes) if (!hasFlag(state, word, "ONLYINCOMPOUND", codes)) return true;
37
+ return false;
38
+ }
39
+ /**
40
+ * Spell-check with capitalization variants (all-caps / Capitalized).
41
+ */
42
+ function check(state, aWord) {
43
+ if (aWord.length === 0) return false;
44
+ const trimmedWord = normalizeWord(aWord).replace(/^\s+/, "").replace(/\s+$/, "");
45
+ if (checkExact(state, trimmedWord)) return true;
46
+ const first = trimmedWord.charAt(0);
47
+ if (trimmedWord.toUpperCase() === trimmedWord) {
48
+ const capitalizedWord = first + trimmedWord.slice(1).toLowerCase();
49
+ if (hasFlag(state, capitalizedWord, "KEEPCASE")) return false;
50
+ if (checkExact(state, capitalizedWord)) return true;
51
+ if (checkExact(state, trimmedWord.toLowerCase())) return true;
52
+ }
53
+ const uncapitalizedWord = first.toLowerCase() + trimmedWord.slice(1);
54
+ if (uncapitalizedWord !== trimmedWord) {
55
+ if (hasFlag(state, uncapitalizedWord, "KEEPCASE")) return false;
56
+ if (checkExact(state, uncapitalizedWord)) return true;
57
+ }
58
+ return false;
59
+ }
60
+ //#endregion
61
+ //#region src/parseRuleCodes.ts
62
+ /**
63
+ * Split a Hunspell flag string according to the `.aff` `FLAG` mode.
64
+ * Default: one character per flag.
65
+ */
66
+ function parseRuleCodes(textCodes, flags) {
67
+ if (!textCodes) return [];
68
+ if (!("FLAG" in flags)) return textCodes.split("");
69
+ if (flags.FLAG === "long") {
70
+ const out = [];
71
+ for (let i = 0; i < textCodes.length; i += 2) out.push(textCodes.slice(i, i + 2));
72
+ return out;
73
+ }
74
+ if (flags.FLAG === "num") return textCodes.split(",");
75
+ if (flags.FLAG === "UTF-8") return Array.from(textCodes);
76
+ return textCodes.split("");
77
+ }
78
+ //#endregion
79
+ //#region src/parseAff.ts
80
+ function removeAffixComments(line) {
81
+ if (/^\s*#/.test(line)) return "";
82
+ return line;
83
+ }
84
+ function tryCompileAffixRegex(source) {
85
+ if (source.length > 80) return;
86
+ try {
87
+ return new RegExp(source);
88
+ } catch {
89
+ return;
90
+ }
91
+ }
92
+ /**
93
+ * Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.
94
+ */
95
+ function parseAff(data) {
96
+ const rules = {};
97
+ const flags = {};
98
+ const compoundRulePatterns = [];
99
+ const replacementTable = [];
100
+ const lines = data.split(/\r?\n/);
101
+ for (let i = 0; i < lines.length; i++) {
102
+ let line = removeAffixComments(lines[i] ?? "").trim();
103
+ if (!line) continue;
104
+ const definitionParts = line.split(/\s+/);
105
+ const ruleType = definitionParts[0];
106
+ if (!ruleType) continue;
107
+ if (ruleType === "PFX" || ruleType === "SFX") {
108
+ const ruleCode = definitionParts[1];
109
+ const combineable = definitionParts[2];
110
+ const numEntries = Math.trunc(Number(definitionParts[3] ?? "0"));
111
+ if (!ruleCode || Number.isNaN(numEntries) || numEntries < 0 || numEntries > 1e4) {
112
+ if (!Number.isNaN(numEntries) && numEntries > 0) i += Math.min(numEntries, lines.length);
113
+ continue;
114
+ }
115
+ const entries = [];
116
+ for (let j = i + 1; j < i + 1 + numEntries; j++) {
117
+ const lineParts = (lines[j] ?? "").split(/\s+/);
118
+ const charactersToRemove = lineParts[2] ?? "0";
119
+ const additionParts = (lineParts[3] ?? "0").split("/");
120
+ let charactersToAdd = additionParts[0] ?? "";
121
+ if (charactersToAdd === "0") charactersToAdd = "";
122
+ const continuationClasses = parseRuleCodes(additionParts[1], flags);
123
+ const regexToMatch = lineParts[4] ?? ".";
124
+ const entry = { add: charactersToAdd };
125
+ if (continuationClasses.length > 0) entry.continuationClasses = continuationClasses;
126
+ if (regexToMatch !== ".") {
127
+ const match = ruleType === "SFX" ? tryCompileAffixRegex(`${regexToMatch}$`) : tryCompileAffixRegex(`^${regexToMatch}`);
128
+ if (match === void 0) continue;
129
+ entry.match = match;
130
+ }
131
+ if (charactersToRemove !== "0") {
132
+ if (ruleType === "SFX") {
133
+ const remove = tryCompileAffixRegex(`${charactersToRemove}$`);
134
+ if (remove === void 0) continue;
135
+ entry.remove = remove;
136
+ } else if (charactersToRemove.length <= 80) entry.remove = charactersToRemove;
137
+ else continue;
138
+ }
139
+ entries.push(entry);
140
+ }
141
+ rules[ruleCode] = {
142
+ type: ruleType,
143
+ combineable: combineable === "Y",
144
+ entries
145
+ };
146
+ i += numEntries;
147
+ } else if (ruleType === "COMPOUNDRULE") {
148
+ const numEntries = Math.trunc(Number(definitionParts[1] ?? "0"));
149
+ if (Number.isNaN(numEntries) || numEntries < 0 || numEntries > 1e4) {
150
+ if (!Number.isNaN(numEntries) && numEntries > 0) i += Math.min(numEntries, lines.length);
151
+ continue;
152
+ }
153
+ for (let j = i + 1; j < i + 1 + numEntries; j++) {
154
+ const pattern = (lines[j] ?? "").split(/\s+/)[1];
155
+ if (pattern) compoundRulePatterns.push(pattern);
156
+ }
157
+ i += numEntries;
158
+ } else if (ruleType === "REP") {
159
+ const from = definitionParts[1];
160
+ const to = definitionParts[2];
161
+ if (from !== void 0 && to !== void 0 && definitionParts.length === 3) replacementTable.push([from, to]);
162
+ } else {
163
+ const value = definitionParts[1];
164
+ if (value !== void 0) flags[ruleType] = value;
165
+ }
166
+ }
167
+ return {
168
+ rules,
169
+ flags,
170
+ compoundRulePatterns,
171
+ replacementTable
172
+ };
173
+ }
174
+ //#endregion
175
+ //#region src/applyRule.ts
176
+ /**
177
+ * Apply one affix rule (and any continuation classes) to a stem.
178
+ */
179
+ function applyRule(word, rule, rules, depth = 0) {
180
+ const newWords = [];
181
+ if (depth > 12) return newWords;
182
+ for (const entry of rule.entries) {
183
+ if (entry.match !== void 0 && !entry.match.test(word)) continue;
184
+ let newWord = word;
185
+ if (entry.remove !== void 0) newWord = newWord.replace(entry.remove, "");
186
+ newWord = rule.type === "SFX" ? newWord + entry.add : entry.add + newWord;
187
+ newWords.push(newWord);
188
+ const cont = entry.continuationClasses;
189
+ if (cont === void 0) continue;
190
+ for (const code of cont) {
191
+ const continuationRule = rules[code];
192
+ if (continuationRule !== void 0) newWords.push(...applyRule(newWord, continuationRule, rules, depth + 1));
193
+ }
194
+ }
195
+ return newWords;
196
+ }
197
+ //#endregion
198
+ //#region src/parseDic.ts
199
+ function removeDicComments(data) {
200
+ return data.replace(/^\t.*$/gm, "");
201
+ }
202
+ /**
203
+ * Parse Hunspell `.dic` content into a word→flags lookup table.
204
+ * Also expands affix forms into the table (Typo.js approach).
205
+ */
206
+ function parseDic(data, options) {
207
+ const { rules, flags, compoundRuleCodes } = options;
208
+ const lines = removeDicComments(data).split(/\r?\n/);
209
+ const dictionaryTable = /* @__PURE__ */ new Map();
210
+ function addWord(word, ruleCodes) {
211
+ if (!dictionaryTable.has(word)) dictionaryTable.set(word, null);
212
+ if (ruleCodes.length === 0) return;
213
+ if (dictionaryTable.get(word) === null) dictionaryTable.set(word, []);
214
+ const existing = dictionaryTable.get(word);
215
+ if (Array.isArray(existing)) existing.push(ruleCodes);
216
+ }
217
+ for (let i = 1; i < lines.length; i++) {
218
+ const line = lines[i];
219
+ if (line === void 0 || line.length === 0) continue;
220
+ const parts = line.replace(/\s.*$/, "").split("/", 2);
221
+ const word = parts[0] ?? "";
222
+ if (word.length === 0) continue;
223
+ if (parts.length > 1) {
224
+ const ruleCodesArray = parseRuleCodes(parts[1], flags);
225
+ const needAffix = flags.NEEDAFFIX;
226
+ if (needAffix === void 0 || !ruleCodesArray.includes(needAffix)) addWord(word, ruleCodesArray);
227
+ for (let j = 0; j < ruleCodesArray.length; j++) {
228
+ const code = ruleCodesArray[j];
229
+ if (code === void 0) continue;
230
+ const rule = rules[code];
231
+ if (rule !== void 0) {
232
+ const newWords = applyRule(word, rule, rules);
233
+ for (const newWord of newWords) {
234
+ addWord(newWord, []);
235
+ if (rule.combineable) for (let k = j + 1; k < ruleCodesArray.length; k++) {
236
+ const combineCode = ruleCodesArray[k];
237
+ if (combineCode === void 0) continue;
238
+ const combineRule = rules[combineCode];
239
+ if (combineRule !== void 0 && combineRule.combineable && rule.type !== combineRule.type) for (const other of applyRule(newWord, combineRule, rules)) addWord(other, []);
240
+ }
241
+ }
242
+ }
243
+ const compoundStems = compoundRuleCodes[code];
244
+ if (compoundStems !== void 0) compoundStems.push(word);
245
+ }
246
+ } else addWord(word.trim(), []);
247
+ }
248
+ return dictionaryTable;
249
+ }
250
+ //#endregion
251
+ //#region src/suggest.ts
252
+ function ensureAlphabet(state) {
253
+ if (state.alphabet.length > 0) return state.alphabet;
254
+ const LATIN_FALLBACK = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
255
+ let alphabet = "";
256
+ if (state.flags.TRY !== void 0 && state.flags.TRY.length > 0) alphabet = state.flags.TRY;
257
+ else alphabet = LATIN_FALLBACK;
258
+ if (state.flags.WORDCHARS !== void 0) alphabet += state.flags.WORDCHARS;
259
+ const seen = /* @__PURE__ */ new Set();
260
+ let unique = "";
261
+ for (const ch of alphabet) if (!seen.has(ch)) {
262
+ seen.add(ch);
263
+ unique += ch;
264
+ }
265
+ state.alphabet = unique;
266
+ return unique;
267
+ }
268
+ function edits1(state, words, knownOnly) {
269
+ const alphabet = ensureAlphabet(state);
270
+ const rv = {};
271
+ const bump = (edit) => {
272
+ if (knownOnly && !check(state, edit)) return;
273
+ rv[edit] = (rv[edit] ?? 0) + 1;
274
+ };
275
+ for (const word of Object.keys(words)) for (let i = 0; i <= word.length; i++) {
276
+ const left = word.slice(0, i);
277
+ const right = word.slice(i);
278
+ if (right.length > 0) bump(left + right.slice(1));
279
+ const right0 = right[0];
280
+ const right1 = right[1];
281
+ if (right.length > 1 && right1 !== void 0 && right0 !== void 0 && right1 !== right0) bump(left + right1 + right0 + right.slice(2));
282
+ if (right.length > 0) {
283
+ const lettercase = right.slice(0, 1).toUpperCase() === right.slice(0, 1) ? "uppercase" : "lowercase";
284
+ for (const ch of alphabet) {
285
+ let replacementLetter = ch;
286
+ if (lettercase === "uppercase") replacementLetter = replacementLetter.toUpperCase();
287
+ if (replacementLetter !== right.slice(0, 1)) bump(left + replacementLetter + right.slice(1));
288
+ }
289
+ }
290
+ if (right.length > 0) {
291
+ const leftTail = left.slice(-1);
292
+ const rightHead = right.slice(0, 1);
293
+ const lettercase = leftTail.toUpperCase() === leftTail && rightHead.toUpperCase() === rightHead ? "uppercase" : "lowercase";
294
+ for (const ch of alphabet) {
295
+ let replacementLetter = ch;
296
+ if (lettercase === "uppercase") replacementLetter = replacementLetter.toUpperCase();
297
+ bump(left + replacementLetter + right);
298
+ }
299
+ }
300
+ }
301
+ return rv;
302
+ }
303
+ function rankCorrections(state, word, limit) {
304
+ const tier1 = edits1(state, { [word]: true }, true);
305
+ let weighted = tier1;
306
+ if (Object.keys(tier1).length === 0) {
307
+ const ed1 = edits1(state, { [word]: true }, false);
308
+ const unknownSeeds = {};
309
+ let seedCount = 0;
310
+ const MAX_ED2_SEEDS = 64;
311
+ for (const [candidate, weight] of Object.entries(ed1)) {
312
+ if (check(state, candidate)) {
313
+ weighted[candidate] = (weighted[candidate] ?? 0) + weight;
314
+ continue;
315
+ }
316
+ if (seedCount >= MAX_ED2_SEEDS) continue;
317
+ unknownSeeds[candidate] = weight;
318
+ seedCount += 1;
319
+ }
320
+ if (Object.keys(unknownSeeds).length > 0) {
321
+ const ed2 = edits1(state, unknownSeeds, true);
322
+ for (const [candidate, weight] of Object.entries(ed2)) weighted[candidate] = (weighted[candidate] ?? 0) + weight;
323
+ }
324
+ }
325
+ const sorted = [];
326
+ for (const [candidate, weight] of Object.entries(weighted)) {
327
+ let w = weight;
328
+ if (hasFlag(state, candidate, "PRIORITYSUGGEST")) w += 1e3;
329
+ sorted.push([candidate, w]);
330
+ }
331
+ sorted.sort((a, b) => {
332
+ if (a[1] !== b[1]) return b[1] - a[1];
333
+ return a[0].localeCompare(b[0]);
334
+ });
335
+ let capitalizationScheme = "lowercase";
336
+ if (word.toUpperCase() === word) capitalizationScheme = "uppercase";
337
+ else if (word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() === word) capitalizationScheme = "capitalized";
338
+ const rv = [];
339
+ let workingLimit = limit;
340
+ for (let i = 0; i < Math.min(workingLimit, sorted.length); i++) {
341
+ const entry = sorted[i];
342
+ if (entry === void 0) break;
343
+ let suggestion = entry[0];
344
+ if (capitalizationScheme === "uppercase") suggestion = suggestion.toUpperCase();
345
+ else if (capitalizationScheme === "capitalized") suggestion = suggestion.slice(0, 1).toUpperCase() + suggestion.slice(1);
346
+ if (!hasFlag(state, suggestion, "NOSUGGEST") && !rv.includes(suggestion)) rv.push(suggestion);
347
+ else workingLimit++;
348
+ }
349
+ return rv;
350
+ }
351
+ /**
352
+ * Suggest corrections for a misspelling (REP table + edit-distance ranking).
353
+ */
354
+ function suggest(state, word, limit = 5) {
355
+ if (word.length === 0 || word.length > 64) return [];
356
+ const normalized = word.replaceAll("’", "'");
357
+ const memo = state.memoized[normalized];
358
+ if (memo !== void 0 && (limit <= memo.limit || memo.suggestions.length < memo.limit)) return memo.suggestions.slice(0, limit);
359
+ if (check(state, normalized)) return [];
360
+ for (const [from, to] of state.replacementTable) if (normalized.includes(from)) {
361
+ const corrected = normalized.replace(from, to);
362
+ if (check(state, corrected)) {
363
+ const suggestions = [corrected];
364
+ remember(state, normalized, suggestions, limit);
365
+ return suggestions;
366
+ }
367
+ }
368
+ const suggestions = rankCorrections(state, normalized, limit);
369
+ remember(state, normalized, suggestions, limit);
370
+ return suggestions;
371
+ }
372
+ function remember(state, word, suggestions, limit) {
373
+ const keys = Object.keys(state.memoized);
374
+ if (keys.length >= 512) {
375
+ const drop = keys[0];
376
+ if (drop !== void 0) delete state.memoized[drop];
377
+ }
378
+ state.memoized[word] = {
379
+ suggestions,
380
+ limit
381
+ };
382
+ }
383
+ //#endregion
384
+ //#region src/dictionary.ts
385
+ function buildCompoundRegexes(patterns, compoundRuleCodes) {
386
+ const out = [];
387
+ for (const ruleText of patterns) {
388
+ let expressionText = "";
389
+ for (const character of ruleText) if (Object.hasOwn(compoundRuleCodes, character)) {
390
+ const stems = compoundRuleCodes[character] ?? [];
391
+ expressionText += `(${stems.map(escapeRegExp).join("|")})`;
392
+ } else expressionText += character;
393
+ if (expressionText.length > 4096) continue;
394
+ try {
395
+ out.push(new RegExp(`^${expressionText}$`, "i"));
396
+ } catch {}
397
+ }
398
+ return out;
399
+ }
400
+ /**
401
+ * Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.
402
+ * Dictionaries are not bundled — callers fetch/load the files.
403
+ */
404
+ function createDictionary(affData, dicData) {
405
+ if (affData.length > 8388608 || dicData.length > 8388608) throw new Error("dictionary too large");
406
+ const { rules, flags, compoundRulePatterns, replacementTable } = parseAff(affData);
407
+ const compoundRuleCodes = {};
408
+ for (const pattern of compoundRulePatterns) for (const ch of pattern) compoundRuleCodes[ch] = [];
409
+ if (flags.ONLYINCOMPOUND !== void 0) compoundRuleCodes[flags.ONLYINCOMPOUND] = [];
410
+ const dictionaryTable = parseDic(dicData, {
411
+ rules,
412
+ flags,
413
+ compoundRuleCodes
414
+ });
415
+ for (const code of Object.keys(compoundRuleCodes)) if ((compoundRuleCodes[code] ?? []).length === 0) delete compoundRuleCodes[code];
416
+ const state = {
417
+ dictionaryTable,
418
+ compoundRules: buildCompoundRegexes(compoundRulePatterns, compoundRuleCodes),
419
+ replacementTable,
420
+ flags,
421
+ memoized: {},
422
+ alphabet: ""
423
+ };
424
+ return {
425
+ loaded: true,
426
+ check: (word) => check(state, word),
427
+ checkExact: (word) => checkExact(state, word),
428
+ suggest: (word, limit) => suggest(state, word, limit)
429
+ };
430
+ }
431
+ //#endregion
432
+ export { createDictionary };
433
+
434
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/limits.ts","../src/check.ts","../src/parseRuleCodes.ts","../src/parseAff.ts","../src/applyRule.ts","../src/parseDic.ts","../src/suggest.ts","../src/dictionary.ts"],"sourcesContent":["/** Resource bounds for untrusted / oversized Hunspell inputs. */\n\nexport const MAX_AFF_BYTES = 8_388_608;\nexport const MAX_DIC_BYTES = 8_388_608;\nexport const MAX_AFFIX_ENTRIES = 10_000;\nexport const MAX_APPLY_RULE_DEPTH = 12;\nexport const MAX_COMPOUND_REGEX_SOURCE = 4096;\nexport const MAX_AFFIX_PATTERN_LEN = 80;\nexport const MAX_CHECK_WORD_LEN = 256;\nexport const MAX_SUGGEST_WORD_LEN = 64;\nexport const MAX_SUGGEST_MEMO = 512;\n\nexport function escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","import { MAX_CHECK_WORD_LEN } from './limits';\nimport type { HunspellState } from './types';\n\n/** Typographic apostrophe → ASCII (dictionary-en stems use U+0027). */\nfunction normalizeWord(word: string): string {\n return word.replaceAll('\\u2019', \"'\");\n}\n\nfunction flattenFlags(ruleCodes: string[][] | null | undefined): string[] {\n if (ruleCodes === null || ruleCodes === undefined) {\n return [];\n }\n return ruleCodes.flat();\n}\n\n/**\n * Whether `word` carries the named affix flag (e.g. `KEEPCASE`, `NOSUGGEST`).\n */\nexport function hasFlag(\n state: HunspellState,\n word: string,\n flag: string,\n wordFlags?: string[]\n): boolean {\n const flagValue = state.flags[flag];\n if (flagValue === undefined) {\n return false;\n }\n const codes = wordFlags ?? flattenFlags(state.dictionaryTable.get(word) ?? undefined);\n return codes.includes(flagValue);\n}\n\n/**\n * Exact dictionary lookup (no capitalization folding). Also matches compound rules.\n */\nexport function checkExact(state: HunspellState, word: string): boolean {\n if (word.length > MAX_CHECK_WORD_LEN) {\n return false;\n }\n\n const ruleCodes = state.dictionaryTable.get(word);\n\n if (ruleCodes === undefined) {\n const compoundMin = state.flags.COMPOUNDMIN;\n if (\n compoundMin !== undefined &&\n word.length >= Number(compoundMin) &&\n word.length <= MAX_CHECK_WORD_LEN\n ) {\n for (const rule of state.compoundRules) {\n if (rule.test(word)) {\n return true;\n }\n }\n }\n return false;\n }\n\n if (ruleCodes === null) {\n return true;\n }\n\n for (const codes of ruleCodes) {\n if (!hasFlag(state, word, 'ONLYINCOMPOUND', codes)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Spell-check with capitalization variants (all-caps / Capitalized).\n */\nexport function check(state: HunspellState, aWord: string): boolean {\n if (aWord.length === 0) {\n return false;\n }\n\n const trimmedWord = normalizeWord(aWord).replace(/^\\s+/, '').replace(/\\s+$/, '');\n if (checkExact(state, trimmedWord)) {\n return true;\n }\n\n const first = trimmedWord.charAt(0);\n if (trimmedWord.toUpperCase() === trimmedWord) {\n const capitalizedWord = first + trimmedWord.slice(1).toLowerCase();\n if (hasFlag(state, capitalizedWord, 'KEEPCASE')) {\n return false;\n }\n if (checkExact(state, capitalizedWord)) {\n return true;\n }\n if (checkExact(state, trimmedWord.toLowerCase())) {\n return true;\n }\n }\n\n const uncapitalizedWord = first.toLowerCase() + trimmedWord.slice(1);\n if (uncapitalizedWord !== trimmedWord) {\n if (hasFlag(state, uncapitalizedWord, 'KEEPCASE')) {\n return false;\n }\n if (checkExact(state, uncapitalizedWord)) {\n return true;\n }\n }\n\n return false;\n}\n","import type { AffFlags } from './types';\n\n/**\n * Split a Hunspell flag string according to the `.aff` `FLAG` mode.\n * Default: one character per flag.\n */\nexport function parseRuleCodes(textCodes: string | undefined, flags: AffFlags): string[] {\n if (!textCodes) {\n return [];\n }\n if (!('FLAG' in flags)) {\n return textCodes.split('');\n }\n if (flags.FLAG === 'long') {\n const out: string[] = [];\n for (let i = 0; i < textCodes.length; i += 2) {\n out.push(textCodes.slice(i, i + 2));\n }\n return out;\n }\n if (flags.FLAG === 'num') {\n return textCodes.split(',');\n }\n if (flags.FLAG === 'UTF-8') {\n return Array.from(textCodes);\n }\n return textCodes.split('');\n}\n","import type { AffFlags, AffixEntry, AffixRules, ReplacementPair } from './types';\nimport { MAX_AFFIX_ENTRIES, MAX_AFFIX_PATTERN_LEN } from './limits';\nimport { parseRuleCodes } from './parseRuleCodes';\n\nexport type AffParseResult = {\n rules: AffixRules;\n flags: AffFlags;\n /** Raw COMPOUNDRULE pattern strings (not yet RegExp). */\n compoundRulePatterns: string[];\n replacementTable: ReplacementPair[];\n};\n\nfunction removeAffixComments(line: string): string {\n // Only strip full-line comments — COMPOUNDRULE may use `#` as a flag char.\n if (/^\\s*#/.test(line)) {\n return '';\n }\n return line;\n}\n\nfunction tryCompileAffixRegex(source: string): RegExp | undefined {\n if (source.length > MAX_AFFIX_PATTERN_LEN) {\n return undefined;\n }\n try {\n return new RegExp(source);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.\n */\nexport function parseAff(data: string): AffParseResult {\n const rules: AffixRules = {};\n const flags: AffFlags = {};\n const compoundRulePatterns: string[] = [];\n const replacementTable: ReplacementPair[] = [];\n\n const lines = data.split(/\\r?\\n/);\n\n for (let i = 0; i < lines.length; i++) {\n let line = removeAffixComments(lines[i] ?? '').trim();\n if (!line) {\n continue;\n }\n\n const definitionParts = line.split(/\\s+/);\n const ruleType = definitionParts[0];\n if (!ruleType) {\n continue;\n }\n\n if (ruleType === 'PFX' || ruleType === 'SFX') {\n const ruleCode = definitionParts[1];\n const combineable = definitionParts[2];\n const numEntries = Math.trunc(Number(definitionParts[3] ?? '0'));\n if (\n !ruleCode ||\n Number.isNaN(numEntries) ||\n numEntries < 0 ||\n numEntries > MAX_AFFIX_ENTRIES\n ) {\n if (!Number.isNaN(numEntries) && numEntries > 0) {\n i += Math.min(numEntries, lines.length);\n }\n continue;\n }\n\n const entries: AffixEntry[] = [];\n for (let j = i + 1; j < i + 1 + numEntries; j++) {\n const subline = lines[j] ?? '';\n const lineParts = subline.split(/\\s+/);\n const charactersToRemove = lineParts[2] ?? '0';\n const additionParts = (lineParts[3] ?? '0').split('/');\n let charactersToAdd = additionParts[0] ?? '';\n if (charactersToAdd === '0') {\n charactersToAdd = '';\n }\n const continuationClasses = parseRuleCodes(additionParts[1], flags);\n const regexToMatch = lineParts[4] ?? '.';\n\n const entry: AffixEntry = { add: charactersToAdd };\n if (continuationClasses.length > 0) {\n entry.continuationClasses = continuationClasses;\n }\n if (regexToMatch !== '.') {\n const match =\n ruleType === 'SFX'\n ? tryCompileAffixRegex(`${regexToMatch}$`)\n : tryCompileAffixRegex(`^${regexToMatch}`);\n if (match === undefined) {\n continue;\n }\n entry.match = match;\n }\n if (charactersToRemove !== '0') {\n if (ruleType === 'SFX') {\n const remove = tryCompileAffixRegex(`${charactersToRemove}$`);\n if (remove === undefined) {\n continue;\n }\n entry.remove = remove;\n } else if (charactersToRemove.length <= MAX_AFFIX_PATTERN_LEN) {\n entry.remove = charactersToRemove;\n } else {\n continue;\n }\n }\n entries.push(entry);\n }\n\n rules[ruleCode] = {\n type: ruleType,\n combineable: combineable === 'Y',\n entries,\n };\n i += numEntries;\n } else if (ruleType === 'COMPOUNDRULE') {\n const numEntries = Math.trunc(Number(definitionParts[1] ?? '0'));\n if (Number.isNaN(numEntries) || numEntries < 0 || numEntries > MAX_AFFIX_ENTRIES) {\n if (!Number.isNaN(numEntries) && numEntries > 0) {\n i += Math.min(numEntries, lines.length);\n }\n continue;\n }\n for (let j = i + 1; j < i + 1 + numEntries; j++) {\n const parts = (lines[j] ?? '').split(/\\s+/);\n const pattern = parts[1];\n if (pattern) {\n compoundRulePatterns.push(pattern);\n }\n }\n i += numEntries;\n } else if (ruleType === 'REP') {\n const from = definitionParts[1];\n const to = definitionParts[2];\n if (from !== undefined && to !== undefined && definitionParts.length === 3) {\n replacementTable.push([from, to]);\n }\n } else {\n // ONLYINCOMPOUND, COMPOUNDMIN, FLAG, KEEPCASE, NEEDAFFIX, TRY, WORDCHARS, NOSUGGEST, …\n const value = definitionParts[1];\n if (value !== undefined) {\n flags[ruleType] = value;\n }\n }\n }\n\n return { rules, flags, compoundRulePatterns, replacementTable };\n}\n","import { MAX_APPLY_RULE_DEPTH } from './limits';\nimport type { AffixRule, AffixRules } from './types';\n\n/**\n * Apply one affix rule (and any continuation classes) to a stem.\n */\nexport function applyRule(word: string, rule: AffixRule, rules: AffixRules, depth = 0): string[] {\n const newWords: string[] = [];\n if (depth > MAX_APPLY_RULE_DEPTH) {\n return newWords;\n }\n\n for (const entry of rule.entries) {\n if (entry.match !== undefined && !entry.match.test(word)) {\n continue;\n }\n\n let newWord = word;\n if (entry.remove !== undefined) {\n newWord = newWord.replace(entry.remove, '');\n }\n newWord = rule.type === 'SFX' ? newWord + entry.add : entry.add + newWord;\n newWords.push(newWord);\n\n const cont = entry.continuationClasses;\n if (cont === undefined) {\n continue;\n }\n for (const code of cont) {\n const continuationRule = rules[code];\n if (continuationRule !== undefined) {\n newWords.push(...applyRule(newWord, continuationRule, rules, depth + 1));\n }\n }\n }\n\n return newWords;\n}\n","import { applyRule } from './applyRule';\nimport { parseRuleCodes } from './parseRuleCodes';\nimport type { AffFlags, AffixRules, DictionaryTable } from './types';\n\nfunction removeDicComments(data: string): string {\n // de_DE and some others use tab-indented comment lines.\n return data.replace(/^\\t.*$/gm, '');\n}\n\nexport type ParseDicOptions = {\n rules: AffixRules;\n flags: AffFlags;\n /** Mutated: flag code → stems that carry that compound flag. */\n compoundRuleCodes: Record<string, string[]>;\n};\n\n/**\n * Parse Hunspell `.dic` content into a word→flags lookup table.\n * Also expands affix forms into the table (Typo.js approach).\n */\nexport function parseDic(data: string, options: ParseDicOptions): DictionaryTable {\n const { rules, flags, compoundRuleCodes } = options;\n const cleaned = removeDicComments(data);\n const lines = cleaned.split(/\\r?\\n/);\n const dictionaryTable: DictionaryTable = new Map();\n\n function addWord(word: string, ruleCodes: string[]): void {\n if (!dictionaryTable.has(word)) {\n dictionaryTable.set(word, null);\n }\n if (ruleCodes.length === 0) {\n return;\n }\n if (dictionaryTable.get(word) === null) {\n dictionaryTable.set(word, []);\n }\n const existing = dictionaryTable.get(word);\n if (Array.isArray(existing)) {\n existing.push(ruleCodes);\n }\n }\n\n // First line is the word count — skip it.\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined || line.length === 0) {\n continue;\n }\n\n // Drop morphological fields (`xx:abc`); keep `word` or `word/flags`.\n const justWordAndFlags = line.replace(/\\s.*$/, '');\n const parts = justWordAndFlags.split('/', 2);\n const word = parts[0] ?? '';\n if (word.length === 0) {\n continue;\n }\n\n if (parts.length > 1) {\n const ruleCodesArray = parseRuleCodes(parts[1], flags);\n const needAffix = flags.NEEDAFFIX;\n if (needAffix === undefined || !ruleCodesArray.includes(needAffix)) {\n addWord(word, ruleCodesArray);\n }\n\n for (let j = 0; j < ruleCodesArray.length; j++) {\n const code = ruleCodesArray[j];\n if (code === undefined) {\n continue;\n }\n const rule = rules[code];\n if (rule !== undefined) {\n const newWords = applyRule(word, rule, rules);\n for (const newWord of newWords) {\n addWord(newWord, []);\n if (rule.combineable) {\n for (let k = j + 1; k < ruleCodesArray.length; k++) {\n const combineCode = ruleCodesArray[k];\n if (combineCode === undefined) {\n continue;\n }\n const combineRule = rules[combineCode];\n if (\n combineRule !== undefined &&\n combineRule.combineable &&\n rule.type !== combineRule.type\n ) {\n for (const other of applyRule(newWord, combineRule, rules)) {\n addWord(other, []);\n }\n }\n }\n }\n }\n }\n const compoundStems = compoundRuleCodes[code];\n if (compoundStems !== undefined) {\n compoundStems.push(word);\n }\n }\n } else {\n addWord(word.trim(), []);\n }\n }\n\n return dictionaryTable;\n}\n","import { check, hasFlag } from './check';\nimport { MAX_SUGGEST_MEMO, MAX_SUGGEST_WORD_LEN } from './limits';\nimport type { HunspellState } from './types';\n\nfunction ensureAlphabet(state: HunspellState): string {\n if (state.alphabet.length > 0) {\n return state.alphabet;\n }\n\n // Hunspell: suggestion edits come from TRY (+ WORDCHARS). Latin fallback is only\n // for incomplete .aff files — do not force English letters onto every locale.\n const LATIN_FALLBACK = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n let alphabet = '';\n if (state.flags.TRY !== undefined && state.flags.TRY.length > 0) {\n alphabet = state.flags.TRY;\n } else {\n alphabet = LATIN_FALLBACK;\n }\n if (state.flags.WORDCHARS !== undefined) {\n alphabet += state.flags.WORDCHARS;\n }\n\n const seen = new Set<string>();\n let unique = '';\n for (const ch of alphabet) {\n if (!seen.has(ch)) {\n seen.add(ch);\n unique += ch;\n }\n }\n state.alphabet = unique;\n return unique;\n}\n\ntype EditCounts = Record<string, number>;\n\nfunction edits1(\n state: HunspellState,\n words: Record<string, true | number>,\n knownOnly: boolean\n): EditCounts {\n const alphabet = ensureAlphabet(state);\n const rv: EditCounts = {};\n\n const bump = (edit: string): void => {\n if (knownOnly && !check(state, edit)) {\n return;\n }\n rv[edit] = (rv[edit] ?? 0) + 1;\n };\n\n for (const word of Object.keys(words)) {\n for (let i = 0; i <= word.length; i++) {\n const left = word.slice(0, i);\n const right = word.slice(i);\n\n if (right.length > 0) {\n bump(left + right.slice(1));\n }\n\n const right0 = right[0];\n const right1 = right[1];\n if (right.length > 1 && right1 !== undefined && right0 !== undefined && right1 !== right0) {\n bump(left + right1 + right0 + right.slice(2));\n }\n\n if (right.length > 0) {\n const lettercase =\n right.slice(0, 1).toUpperCase() === right.slice(0, 1) ? 'uppercase' : 'lowercase';\n for (const ch of alphabet) {\n let replacementLetter = ch;\n if (lettercase === 'uppercase') {\n replacementLetter = replacementLetter.toUpperCase();\n }\n if (replacementLetter !== right.slice(0, 1)) {\n bump(left + replacementLetter + right.slice(1));\n }\n }\n }\n\n if (right.length > 0) {\n // Capitalize insert when both neighbors are uppercase.\n const leftTail = left.slice(-1);\n const rightHead = right.slice(0, 1);\n const lettercase =\n leftTail.toUpperCase() === leftTail && rightHead.toUpperCase() === rightHead\n ? 'uppercase'\n : 'lowercase';\n for (const ch of alphabet) {\n let replacementLetter = ch;\n if (lettercase === 'uppercase') {\n replacementLetter = replacementLetter.toUpperCase();\n }\n bump(left + replacementLetter + right);\n }\n }\n }\n }\n\n return rv;\n}\n\nfunction rankCorrections(state: HunspellState, word: string, limit: number): string[] {\n // Norvig tier-1: known edit-distance-1 first (fast path for near-misses).\n const tier1 = edits1(state, { [word]: true }, true);\n let weighted: EditCounts = tier1;\n\n if (Object.keys(tier1).length === 0) {\n // Tier-2: expand unknown ed1 seeds, then keep only known ed2 forms.\n const ed1 = edits1(state, { [word]: true }, false);\n const unknownSeeds: Record<string, true | number> = {};\n let seedCount = 0;\n const MAX_ED2_SEEDS = 64;\n for (const [candidate, weight] of Object.entries(ed1)) {\n if (check(state, candidate)) {\n weighted[candidate] = (weighted[candidate] ?? 0) + weight;\n continue;\n }\n if (seedCount >= MAX_ED2_SEEDS) {\n continue;\n }\n unknownSeeds[candidate] = weight;\n seedCount += 1;\n }\n if (Object.keys(unknownSeeds).length > 0) {\n const ed2 = edits1(state, unknownSeeds, true);\n for (const [candidate, weight] of Object.entries(ed2)) {\n weighted[candidate] = (weighted[candidate] ?? 0) + weight;\n }\n }\n }\n\n const sorted: Array<[string, number]> = [];\n for (const [candidate, weight] of Object.entries(weighted)) {\n let w = weight;\n if (hasFlag(state, candidate, 'PRIORITYSUGGEST')) {\n w += 1000;\n }\n sorted.push([candidate, w]);\n }\n\n sorted.sort((a, b) => {\n if (a[1] !== b[1]) {\n return b[1] - a[1];\n }\n return a[0].localeCompare(b[0]);\n });\n\n let capitalizationScheme: 'lowercase' | 'uppercase' | 'capitalized' = 'lowercase';\n if (word.toUpperCase() === word) {\n capitalizationScheme = 'uppercase';\n } else if (word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase() === word) {\n capitalizationScheme = 'capitalized';\n }\n\n const rv: string[] = [];\n let workingLimit = limit;\n for (let i = 0; i < Math.min(workingLimit, sorted.length); i++) {\n const entry = sorted[i];\n if (entry === undefined) {\n break;\n }\n let suggestion = entry[0];\n if (capitalizationScheme === 'uppercase') {\n suggestion = suggestion.toUpperCase();\n } else if (capitalizationScheme === 'capitalized') {\n suggestion = suggestion.slice(0, 1).toUpperCase() + suggestion.slice(1);\n }\n if (!hasFlag(state, suggestion, 'NOSUGGEST') && !rv.includes(suggestion)) {\n rv.push(suggestion);\n } else {\n workingLimit++;\n }\n }\n return rv;\n}\n\n/**\n * Suggest corrections for a misspelling (REP table + edit-distance ranking).\n */\nexport function suggest(state: HunspellState, word: string, limit = 5): string[] {\n if (word.length === 0 || word.length > MAX_SUGGEST_WORD_LEN) {\n return [];\n }\n\n const normalized = word.replaceAll('\\u2019', \"'\");\n const memo = state.memoized[normalized];\n if (memo !== undefined && (limit <= memo.limit || memo.suggestions.length < memo.limit)) {\n return memo.suggestions.slice(0, limit);\n }\n\n if (check(state, normalized)) {\n return [];\n }\n\n for (const [from, to] of state.replacementTable) {\n if (normalized.includes(from)) {\n const corrected = normalized.replace(from, to);\n if (check(state, corrected)) {\n const suggestions = [corrected];\n remember(state, normalized, suggestions, limit);\n return suggestions;\n }\n }\n }\n\n const suggestions = rankCorrections(state, normalized, limit);\n remember(state, normalized, suggestions, limit);\n return suggestions;\n}\n\nfunction remember(state: HunspellState, word: string, suggestions: string[], limit: number): void {\n const keys = Object.keys(state.memoized);\n if (keys.length >= MAX_SUGGEST_MEMO) {\n const drop = keys[0];\n if (drop !== undefined) {\n delete state.memoized[drop];\n }\n }\n state.memoized[word] = { suggestions, limit };\n}\n","import { check, checkExact } from './check';\nimport { escapeRegExp, MAX_AFF_BYTES, MAX_COMPOUND_REGEX_SOURCE, MAX_DIC_BYTES } from './limits';\nimport { parseAff } from './parseAff';\nimport { parseDic } from './parseDic';\nimport { suggest } from './suggest';\nimport type { HunspellDictionary, HunspellState } from './types';\n\nfunction buildCompoundRegexes(\n patterns: string[],\n compoundRuleCodes: Record<string, string[]>\n): RegExp[] {\n const out: RegExp[] = [];\n for (const ruleText of patterns) {\n let expressionText = '';\n for (const character of ruleText) {\n if (Object.hasOwn(compoundRuleCodes, character)) {\n const stems = compoundRuleCodes[character] ?? [];\n expressionText += `(${stems.map(escapeRegExp).join('|')})`;\n } else {\n // Hunspell COMPOUNDRULE operators (e.g. `*` in `n*1t`) must stay unescaped.\n expressionText += character;\n }\n }\n if (expressionText.length > MAX_COMPOUND_REGEX_SOURCE) {\n continue;\n }\n try {\n out.push(new RegExp(`^${expressionText}$`, 'i'));\n } catch {\n // Malformed pattern after escaping — skip this compound rule.\n }\n }\n return out;\n}\n\n/**\n * Build a Hunspell dictionary from raw `.aff` and `.dic` file contents.\n * Dictionaries are not bundled — callers fetch/load the files.\n */\nexport function createDictionary(affData: string, dicData: string): HunspellDictionary {\n if (affData.length > MAX_AFF_BYTES || dicData.length > MAX_DIC_BYTES) {\n throw new Error('dictionary too large');\n }\n\n const { rules, flags, compoundRulePatterns, replacementTable } = parseAff(affData);\n\n const compoundRuleCodes: Record<string, string[]> = {};\n for (const pattern of compoundRulePatterns) {\n for (const ch of pattern) {\n compoundRuleCodes[ch] = [];\n }\n }\n if (flags.ONLYINCOMPOUND !== undefined) {\n compoundRuleCodes[flags.ONLYINCOMPOUND] = [];\n }\n\n const dictionaryTable = parseDic(dicData, { rules, flags, compoundRuleCodes });\n\n for (const code of Object.keys(compoundRuleCodes)) {\n if ((compoundRuleCodes[code] ?? []).length === 0) {\n delete compoundRuleCodes[code];\n }\n }\n\n const compoundRules = buildCompoundRegexes(compoundRulePatterns, compoundRuleCodes);\n\n const state: HunspellState = {\n dictionaryTable,\n compoundRules,\n replacementTable,\n flags,\n memoized: {},\n alphabet: '',\n };\n\n return {\n loaded: true,\n check: (word: string) => check(state, word),\n checkExact: (word: string) => checkExact(state, word),\n suggest: (word: string, limit?: number) => suggest(state, word, limit),\n };\n}\n"],"mappings":"AAYA,SAAgB,aAAa,MAAsB;CACjD,OAAO,KAAK,QAAQ,uBAAuB,MAAM;AACnD;;;;ACVA,SAAS,cAAc,MAAsB;CAC3C,OAAO,KAAK,WAAW,KAAU,GAAG;AACtC;AAEA,SAAS,aAAa,WAAoD;CACxE,IAAI,cAAc,QAAQ,cAAc,KAAA,GACtC,OAAO,CAAC;CAEV,OAAO,UAAU,KAAK;AACxB;;;;AAKA,SAAgB,QACd,OACA,MACA,MACA,WACS;CACT,MAAM,YAAY,MAAM,MAAM;CAC9B,IAAI,cAAc,KAAA,GAChB,OAAO;CAGT,QADc,aAAa,aAAa,MAAM,gBAAgB,IAAI,IAAI,KAAK,KAAA,CAAS,EAAA,CACvE,SAAS,SAAS;AACjC;;;;AAKA,SAAgB,WAAW,OAAsB,MAAuB;CACtE,IAAI,KAAK,SAAA,KACP,OAAO;CAGT,MAAM,YAAY,MAAM,gBAAgB,IAAI,IAAI;CAEhD,IAAI,cAAc,KAAA,GAAW;EAC3B,MAAM,cAAc,MAAM,MAAM;EAChC,IACE,gBAAgB,KAAA,KAChB,KAAK,UAAU,OAAO,WAAW,KACjC,KAAK,UAAA,KAEA;QAAA,MAAM,QAAQ,MAAM,eACvB,IAAI,KAAK,KAAK,IAAI,GAChB,OAAO;EAAA;EAIb,OAAO;CACT;CAEA,IAAI,cAAc,MAChB,OAAO;CAGT,KAAK,MAAM,SAAS,WAClB,IAAI,CAAC,QAAQ,OAAO,MAAM,kBAAkB,KAAK,GAC/C,OAAO;CAGX,OAAO;AACT;;;;AAKA,SAAgB,MAAM,OAAsB,OAAwB;CAClE,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,MAAM,cAAc,cAAc,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;CAC/E,IAAI,WAAW,OAAO,WAAW,GAC/B,OAAO;CAGT,MAAM,QAAQ,YAAY,OAAO,CAAC;CAClC,IAAI,YAAY,YAAY,MAAM,aAAa;EAC7C,MAAM,kBAAkB,QAAQ,YAAY,MAAM,CAAC,CAAC,CAAC,YAAY;EACjE,IAAI,QAAQ,OAAO,iBAAiB,UAAU,GAC5C,OAAO;EAET,IAAI,WAAW,OAAO,eAAe,GACnC,OAAO;EAET,IAAI,WAAW,OAAO,YAAY,YAAY,CAAC,GAC7C,OAAO;CAEX;CAEA,MAAM,oBAAoB,MAAM,YAAY,IAAI,YAAY,MAAM,CAAC;CACnE,IAAI,sBAAsB,aAAa;EACrC,IAAI,QAAQ,OAAO,mBAAmB,UAAU,GAC9C,OAAO;EAET,IAAI,WAAW,OAAO,iBAAiB,GACrC,OAAO;CAEX;CAEA,OAAO;AACT;;;;;;;ACtGA,SAAgB,eAAe,WAA+B,OAA2B;CACvF,IAAI,CAAC,WACH,OAAO,CAAC;CAEV,IAAI,EAAE,UAAU,QACd,OAAO,UAAU,MAAM,EAAE;CAE3B,IAAI,MAAM,SAAS,QAAQ;EACzB,MAAM,MAAgB,CAAC;EACvB,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,GACzC,IAAI,KAAK,UAAU,MAAM,GAAG,IAAI,CAAC,CAAC;EAEpC,OAAO;CACT;CACA,IAAI,MAAM,SAAS,OACjB,OAAO,UAAU,MAAM,GAAG;CAE5B,IAAI,MAAM,SAAS,SACjB,OAAO,MAAM,KAAK,SAAS;CAE7B,OAAO,UAAU,MAAM,EAAE;AAC3B;;;ACfA,SAAS,oBAAoB,MAAsB;CAEjD,IAAI,QAAQ,KAAK,IAAI,GACnB,OAAO;CAET,OAAO;AACT;AAEA,SAAS,qBAAqB,QAAoC;CAChE,IAAI,OAAO,SAAA,IACT;CAEF,IAAI;EACF,OAAO,IAAI,OAAO,MAAM;CAC1B,QAAQ;EACN;CACF;AACF;;;;AAKA,SAAgB,SAAS,MAA8B;CACrD,MAAM,QAAoB,CAAC;CAC3B,MAAM,QAAkB,CAAC;CACzB,MAAM,uBAAiC,CAAC;CACxC,MAAM,mBAAsC,CAAC;CAE7C,MAAM,QAAQ,KAAK,MAAM,OAAO;CAEhC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,IAAI,OAAO,oBAAoB,MAAM,MAAM,EAAE,CAAC,CAAC,KAAK;EACpD,IAAI,CAAC,MACH;EAGF,MAAM,kBAAkB,KAAK,MAAM,KAAK;EACxC,MAAM,WAAW,gBAAgB;EACjC,IAAI,CAAC,UACH;EAGF,IAAI,aAAa,SAAS,aAAa,OAAO;GAC5C,MAAM,WAAW,gBAAgB;GACjC,MAAM,cAAc,gBAAgB;GACpC,MAAM,aAAa,KAAK,MAAM,OAAO,gBAAgB,MAAM,GAAG,CAAC;GAC/D,IACE,CAAC,YACD,OAAO,MAAM,UAAU,KACvB,aAAa,KACb,aAAA,KACA;IACA,IAAI,CAAC,OAAO,MAAM,UAAU,KAAK,aAAa,GAC5C,KAAK,KAAK,IAAI,YAAY,MAAM,MAAM;IAExC;GACF;GAEA,MAAM,UAAwB,CAAC;GAC/B,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,YAAY,KAAK;IAE/C,MAAM,aADU,MAAM,MAAM,GAAA,CACF,MAAM,KAAK;IACrC,MAAM,qBAAqB,UAAU,MAAM;IAC3C,MAAM,iBAAiB,UAAU,MAAM,IAAA,CAAK,MAAM,GAAG;IACrD,IAAI,kBAAkB,cAAc,MAAM;IAC1C,IAAI,oBAAoB,KACtB,kBAAkB;IAEpB,MAAM,sBAAsB,eAAe,cAAc,IAAI,KAAK;IAClE,MAAM,eAAe,UAAU,MAAM;IAErC,MAAM,QAAoB,EAAE,KAAK,gBAAgB;IACjD,IAAI,oBAAoB,SAAS,GAC/B,MAAM,sBAAsB;IAE9B,IAAI,iBAAiB,KAAK;KACxB,MAAM,QACJ,aAAa,QACT,qBAAqB,GAAG,aAAa,EAAE,IACvC,qBAAqB,IAAI,cAAc;KAC7C,IAAI,UAAU,KAAA,GACZ;KAEF,MAAM,QAAQ;IAChB;IACA,IAAI,uBAAuB,KAAK;KAC9B,IAAI,aAAa,OAAO;MACtB,MAAM,SAAS,qBAAqB,GAAG,mBAAmB,EAAE;MAC5D,IAAI,WAAW,KAAA,GACb;MAEF,MAAM,SAAS;KACjB,OAAO,IAAI,mBAAmB,UAAA,IAC5B,MAAM,SAAS;UAEf;IAEJ;IACA,QAAQ,KAAK,KAAK;GACpB;GAEA,MAAM,YAAY;IAChB,MAAM;IACN,aAAa,gBAAgB;IAC7B;GACF;GACA,KAAK;EACP,OAAO,IAAI,aAAa,gBAAgB;GACtC,MAAM,aAAa,KAAK,MAAM,OAAO,gBAAgB,MAAM,GAAG,CAAC;GAC/D,IAAI,OAAO,MAAM,UAAU,KAAK,aAAa,KAAK,aAAA,KAAgC;IAChF,IAAI,CAAC,OAAO,MAAM,UAAU,KAAK,aAAa,GAC5C,KAAK,KAAK,IAAI,YAAY,MAAM,MAAM;IAExC;GACF;GACA,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,YAAY,KAAK;IAE/C,MAAM,WADS,MAAM,MAAM,GAAA,CAAI,MAAM,KACrB,CAAA,CAAM;IACtB,IAAI,SACF,qBAAqB,KAAK,OAAO;GAErC;GACA,KAAK;EACP,OAAO,IAAI,aAAa,OAAO;GAC7B,MAAM,OAAO,gBAAgB;GAC7B,MAAM,KAAK,gBAAgB;GAC3B,IAAI,SAAS,KAAA,KAAa,OAAO,KAAA,KAAa,gBAAgB,WAAW,GACvE,iBAAiB,KAAK,CAAC,MAAM,EAAE,CAAC;EAEpC,OAAO;GAEL,MAAM,QAAQ,gBAAgB;GAC9B,IAAI,UAAU,KAAA,GACZ,MAAM,YAAY;EAEtB;CACF;CAEA,OAAO;EAAE;EAAO;EAAO;EAAsB;CAAiB;AAChE;;;;;;ACjJA,SAAgB,UAAU,MAAc,MAAiB,OAAmB,QAAQ,GAAa;CAC/F,MAAM,WAAqB,CAAC;CAC5B,IAAI,QAAA,IACF,OAAO;CAGT,KAAK,MAAM,SAAS,KAAK,SAAS;EAChC,IAAI,MAAM,UAAU,KAAA,KAAa,CAAC,MAAM,MAAM,KAAK,IAAI,GACrD;EAGF,IAAI,UAAU;EACd,IAAI,MAAM,WAAW,KAAA,GACnB,UAAU,QAAQ,QAAQ,MAAM,QAAQ,EAAE;EAE5C,UAAU,KAAK,SAAS,QAAQ,UAAU,MAAM,MAAM,MAAM,MAAM;EAClE,SAAS,KAAK,OAAO;EAErB,MAAM,OAAO,MAAM;EACnB,IAAI,SAAS,KAAA,GACX;EAEF,KAAK,MAAM,QAAQ,MAAM;GACvB,MAAM,mBAAmB,MAAM;GAC/B,IAAI,qBAAqB,KAAA,GACvB,SAAS,KAAK,GAAG,UAAU,SAAS,kBAAkB,OAAO,QAAQ,CAAC,CAAC;EAE3E;CACF;CAEA,OAAO;AACT;;;ACjCA,SAAS,kBAAkB,MAAsB;CAE/C,OAAO,KAAK,QAAQ,YAAY,EAAE;AACpC;;;;;AAaA,SAAgB,SAAS,MAAc,SAA2C;CAChF,MAAM,EAAE,OAAO,OAAO,sBAAsB;CAE5C,MAAM,QADU,kBAAkB,IACpB,CAAA,CAAQ,MAAM,OAAO;CACnC,MAAM,kCAAmC,IAAI,IAAI;CAEjD,SAAS,QAAQ,MAAc,WAA2B;EACxD,IAAI,CAAC,gBAAgB,IAAI,IAAI,GAC3B,gBAAgB,IAAI,MAAM,IAAI;EAEhC,IAAI,UAAU,WAAW,GACvB;EAEF,IAAI,gBAAgB,IAAI,IAAI,MAAM,MAChC,gBAAgB,IAAI,MAAM,CAAC,CAAC;EAE9B,MAAM,WAAW,gBAAgB,IAAI,IAAI;EACzC,IAAI,MAAM,QAAQ,QAAQ,GACxB,SAAS,KAAK,SAAS;CAE3B;CAGA,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,IAAI,SAAS,KAAA,KAAa,KAAK,WAAW,GACxC;EAKF,MAAM,QADmB,KAAK,QAAQ,SAAS,EACjC,CAAA,CAAiB,MAAM,KAAK,CAAC;EAC3C,MAAM,OAAO,MAAM,MAAM;EACzB,IAAI,KAAK,WAAW,GAClB;EAGF,IAAI,MAAM,SAAS,GAAG;GACpB,MAAM,iBAAiB,eAAe,MAAM,IAAI,KAAK;GACrD,MAAM,YAAY,MAAM;GACxB,IAAI,cAAc,KAAA,KAAa,CAAC,eAAe,SAAS,SAAS,GAC/D,QAAQ,MAAM,cAAc;GAG9B,KAAK,IAAI,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;IAC9C,MAAM,OAAO,eAAe;IAC5B,IAAI,SAAS,KAAA,GACX;IAEF,MAAM,OAAO,MAAM;IACnB,IAAI,SAAS,KAAA,GAAW;KACtB,MAAM,WAAW,UAAU,MAAM,MAAM,KAAK;KAC5C,KAAK,MAAM,WAAW,UAAU;MAC9B,QAAQ,SAAS,CAAC,CAAC;MACnB,IAAI,KAAK,aACP,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;OAClD,MAAM,cAAc,eAAe;OACnC,IAAI,gBAAgB,KAAA,GAClB;OAEF,MAAM,cAAc,MAAM;OAC1B,IACE,gBAAgB,KAAA,KAChB,YAAY,eACZ,KAAK,SAAS,YAAY,MAE1B,KAAK,MAAM,SAAS,UAAU,SAAS,aAAa,KAAK,GACvD,QAAQ,OAAO,CAAC,CAAC;MAGvB;KAEJ;IACF;IACA,MAAM,gBAAgB,kBAAkB;IACxC,IAAI,kBAAkB,KAAA,GACpB,cAAc,KAAK,IAAI;GAE3B;EACF,OACE,QAAQ,KAAK,KAAK,GAAG,CAAC,CAAC;CAE3B;CAEA,OAAO;AACT;;;ACrGA,SAAS,eAAe,OAA8B;CACpD,IAAI,MAAM,SAAS,SAAS,GAC1B,OAAO,MAAM;CAKf,MAAM,iBAAiB;CACvB,IAAI,WAAW;CACf,IAAI,MAAM,MAAM,QAAQ,KAAA,KAAa,MAAM,MAAM,IAAI,SAAS,GAC5D,WAAW,MAAM,MAAM;MAEvB,WAAW;CAEb,IAAI,MAAM,MAAM,cAAc,KAAA,GAC5B,YAAY,MAAM,MAAM;CAG1B,MAAM,uBAAO,IAAI,IAAY;CAC7B,IAAI,SAAS;CACb,KAAK,MAAM,MAAM,UACf,IAAI,CAAC,KAAK,IAAI,EAAE,GAAG;EACjB,KAAK,IAAI,EAAE;EACX,UAAU;CACZ;CAEF,MAAM,WAAW;CACjB,OAAO;AACT;AAIA,SAAS,OACP,OACA,OACA,WACY;CACZ,MAAM,WAAW,eAAe,KAAK;CACrC,MAAM,KAAiB,CAAC;CAExB,MAAM,QAAQ,SAAuB;EACnC,IAAI,aAAa,CAAC,MAAM,OAAO,IAAI,GACjC;EAEF,GAAG,SAAS,GAAG,SAAS,KAAK;CAC/B;CAEA,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,GAClC,KAAK,IAAI,IAAI,GAAG,KAAK,KAAK,QAAQ,KAAK;EACrC,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC;EAC5B,MAAM,QAAQ,KAAK,MAAM,CAAC;EAE1B,IAAI,MAAM,SAAS,GACjB,KAAK,OAAO,MAAM,MAAM,CAAC,CAAC;EAG5B,MAAM,SAAS,MAAM;EACrB,MAAM,SAAS,MAAM;EACrB,IAAI,MAAM,SAAS,KAAK,WAAW,KAAA,KAAa,WAAW,KAAA,KAAa,WAAW,QACjF,KAAK,OAAO,SAAS,SAAS,MAAM,MAAM,CAAC,CAAC;EAG9C,IAAI,MAAM,SAAS,GAAG;GACpB,MAAM,aACJ,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,cAAc;GACxE,KAAK,MAAM,MAAM,UAAU;IACzB,IAAI,oBAAoB;IACxB,IAAI,eAAe,aACjB,oBAAoB,kBAAkB,YAAY;IAEpD,IAAI,sBAAsB,MAAM,MAAM,GAAG,CAAC,GACxC,KAAK,OAAO,oBAAoB,MAAM,MAAM,CAAC,CAAC;GAElD;EACF;EAEA,IAAI,MAAM,SAAS,GAAG;GAEpB,MAAM,WAAW,KAAK,MAAM,EAAE;GAC9B,MAAM,YAAY,MAAM,MAAM,GAAG,CAAC;GAClC,MAAM,aACJ,SAAS,YAAY,MAAM,YAAY,UAAU,YAAY,MAAM,YAC/D,cACA;GACN,KAAK,MAAM,MAAM,UAAU;IACzB,IAAI,oBAAoB;IACxB,IAAI,eAAe,aACjB,oBAAoB,kBAAkB,YAAY;IAEpD,KAAK,OAAO,oBAAoB,KAAK;GACvC;EACF;CACF;CAGF,OAAO;AACT;AAEA,SAAS,gBAAgB,OAAsB,MAAc,OAAyB;CAEpF,MAAM,QAAQ,OAAO,OAAO,GAAG,OAAO,KAAK,GAAG,IAAI;CAClD,IAAI,WAAuB;CAE3B,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,GAAG;EAEnC,MAAM,MAAM,OAAO,OAAO,GAAG,OAAO,KAAK,GAAG,KAAK;EACjD,MAAM,eAA8C,CAAC;EACrD,IAAI,YAAY;EAChB,MAAM,gBAAgB;EACtB,KAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,GAAG,GAAG;GACrD,IAAI,MAAM,OAAO,SAAS,GAAG;IAC3B,SAAS,cAAc,SAAS,cAAc,KAAK;IACnD;GACF;GACA,IAAI,aAAa,eACf;GAEF,aAAa,aAAa;GAC1B,aAAa;EACf;EACA,IAAI,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GAAG;GACxC,MAAM,MAAM,OAAO,OAAO,cAAc,IAAI;GAC5C,KAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,GAAG,GAClD,SAAS,cAAc,SAAS,cAAc,KAAK;EAEvD;CACF;CAEA,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,QAAQ,GAAG;EAC1D,IAAI,IAAI;EACR,IAAI,QAAQ,OAAO,WAAW,iBAAiB,GAC7C,KAAK;EAEP,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC;CAC5B;CAEA,OAAO,MAAM,GAAG,MAAM;EACpB,IAAI,EAAE,OAAO,EAAE,IACb,OAAO,EAAE,KAAK,EAAE;EAElB,OAAO,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE;CAChC,CAAC;CAED,IAAI,uBAAkE;CACtE,IAAI,KAAK,YAAY,MAAM,MACzB,uBAAuB;MAClB,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,MAAM,MAC1E,uBAAuB;CAGzB,MAAM,KAAe,CAAC;CACtB,IAAI,eAAe;CACnB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,cAAc,OAAO,MAAM,GAAG,KAAK;EAC9D,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GACZ;EAEF,IAAI,aAAa,MAAM;EACvB,IAAI,yBAAyB,aAC3B,aAAa,WAAW,YAAY;OAC/B,IAAI,yBAAyB,eAClC,aAAa,WAAW,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,WAAW,MAAM,CAAC;EAExE,IAAI,CAAC,QAAQ,OAAO,YAAY,WAAW,KAAK,CAAC,GAAG,SAAS,UAAU,GACrE,GAAG,KAAK,UAAU;OAElB;CAEJ;CACA,OAAO;AACT;;;;AAKA,SAAgB,QAAQ,OAAsB,MAAc,QAAQ,GAAa;CAC/E,IAAI,KAAK,WAAW,KAAK,KAAK,SAAA,IAC5B,OAAO,CAAC;CAGV,MAAM,aAAa,KAAK,WAAW,KAAU,GAAG;CAChD,MAAM,OAAO,MAAM,SAAS;CAC5B,IAAI,SAAS,KAAA,MAAc,SAAS,KAAK,SAAS,KAAK,YAAY,SAAS,KAAK,QAC/E,OAAO,KAAK,YAAY,MAAM,GAAG,KAAK;CAGxC,IAAI,MAAM,OAAO,UAAU,GACzB,OAAO,CAAC;CAGV,KAAK,MAAM,CAAC,MAAM,OAAO,MAAM,kBAC7B,IAAI,WAAW,SAAS,IAAI,GAAG;EAC7B,MAAM,YAAY,WAAW,QAAQ,MAAM,EAAE;EAC7C,IAAI,MAAM,OAAO,SAAS,GAAG;GAC3B,MAAM,cAAc,CAAC,SAAS;GAC9B,SAAS,OAAO,YAAY,aAAa,KAAK;GAC9C,OAAO;EACT;CACF;CAGF,MAAM,cAAc,gBAAgB,OAAO,YAAY,KAAK;CAC5D,SAAS,OAAO,YAAY,aAAa,KAAK;CAC9C,OAAO;AACT;AAEA,SAAS,SAAS,OAAsB,MAAc,aAAuB,OAAqB;CAChG,MAAM,OAAO,OAAO,KAAK,MAAM,QAAQ;CACvC,IAAI,KAAK,UAAA,KAA4B;EACnC,MAAM,OAAO,KAAK;EAClB,IAAI,SAAS,KAAA,GACX,OAAO,MAAM,SAAS;CAE1B;CACA,MAAM,SAAS,QAAQ;EAAE;EAAa;CAAM;AAC9C;;;ACrNA,SAAS,qBACP,UACA,mBACU;CACV,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,YAAY,UAAU;EAC/B,IAAI,iBAAiB;EACrB,KAAK,MAAM,aAAa,UACtB,IAAI,OAAO,OAAO,mBAAmB,SAAS,GAAG;GAC/C,MAAM,QAAQ,kBAAkB,cAAc,CAAC;GAC/C,kBAAkB,IAAI,MAAM,IAAI,YAAY,CAAC,CAAC,KAAK,GAAG,EAAE;EAC1D,OAEE,kBAAkB;EAGtB,IAAI,eAAe,SAAA,MACjB;EAEF,IAAI;GACF,IAAI,KAAK,IAAI,OAAO,IAAI,eAAe,IAAI,GAAG,CAAC;EACjD,QAAQ,CAER;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,SAAiB,SAAqC;CACrF,IAAI,QAAQ,SAAA,WAA0B,QAAQ,SAAA,SAC5C,MAAM,IAAI,MAAM,sBAAsB;CAGxC,MAAM,EAAE,OAAO,OAAO,sBAAsB,qBAAqB,SAAS,OAAO;CAEjF,MAAM,oBAA8C,CAAC;CACrD,KAAK,MAAM,WAAW,sBACpB,KAAK,MAAM,MAAM,SACf,kBAAkB,MAAM,CAAC;CAG7B,IAAI,MAAM,mBAAmB,KAAA,GAC3B,kBAAkB,MAAM,kBAAkB,CAAC;CAG7C,MAAM,kBAAkB,SAAS,SAAS;EAAE;EAAO;EAAO;CAAkB,CAAC;CAE7E,KAAK,MAAM,QAAQ,OAAO,KAAK,iBAAiB,GAC9C,KAAK,kBAAkB,SAAS,CAAC,EAAA,CAAG,WAAW,GAC7C,OAAO,kBAAkB;CAM7B,MAAM,QAAuB;EAC3B;EACA,eAJoB,qBAAqB,sBAAsB,iBAI/D;EACA;EACA;EACA,UAAU,CAAC;EACX,UAAU;CACZ;CAEA,OAAO;EACL,QAAQ;EACR,QAAQ,SAAiB,MAAM,OAAO,IAAI;EAC1C,aAAa,SAAiB,WAAW,OAAO,IAAI;EACpD,UAAU,MAAc,UAAmB,QAAQ,OAAO,MAAM,KAAK;CACvE;AACF"}
@@ -0,0 +1,12 @@
1
+ /** Resource bounds for untrusted / oversized Hunspell inputs. */
2
+ export declare const MAX_AFF_BYTES = 8388608;
3
+ export declare const MAX_DIC_BYTES = 8388608;
4
+ export declare const MAX_AFFIX_ENTRIES = 10000;
5
+ export declare const MAX_APPLY_RULE_DEPTH = 12;
6
+ export declare const MAX_COMPOUND_REGEX_SOURCE = 4096;
7
+ export declare const MAX_AFFIX_PATTERN_LEN = 80;
8
+ export declare const MAX_CHECK_WORD_LEN = 256;
9
+ export declare const MAX_SUGGEST_WORD_LEN = 64;
10
+ export declare const MAX_SUGGEST_MEMO = 512;
11
+ export declare function escapeRegExp(text: string): string;
12
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1,12 @@
1
+ /** Resource bounds for untrusted / oversized Hunspell inputs. */
2
+ export declare const MAX_AFF_BYTES = 8388608;
3
+ export declare const MAX_DIC_BYTES = 8388608;
4
+ export declare const MAX_AFFIX_ENTRIES = 10000;
5
+ export declare const MAX_APPLY_RULE_DEPTH = 12;
6
+ export declare const MAX_COMPOUND_REGEX_SOURCE = 4096;
7
+ export declare const MAX_AFFIX_PATTERN_LEN = 80;
8
+ export declare const MAX_CHECK_WORD_LEN = 256;
9
+ export declare const MAX_SUGGEST_WORD_LEN = 64;
10
+ export declare const MAX_SUGGEST_MEMO = 512;
11
+ export declare function escapeRegExp(text: string): string;
12
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../src/limits.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,eAAO,MAAM,aAAa,UAAY,CAAC;AACvC,eAAO,MAAM,aAAa,UAAY,CAAC;AACvC,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,13 @@
1
+ import { AffFlags, AffixRules, ReplacementPair } from './types';
2
+ export type AffParseResult = {
3
+ rules: AffixRules;
4
+ flags: AffFlags;
5
+ /** Raw COMPOUNDRULE pattern strings (not yet RegExp). */
6
+ compoundRulePatterns: string[];
7
+ replacementTable: ReplacementPair[];
8
+ };
9
+ /**
10
+ * Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.
11
+ */
12
+ export declare function parseAff(data: string): AffParseResult;
13
+ //# sourceMappingURL=parseAff.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { AffFlags, AffixRules, ReplacementPair } from './types';
2
+ export type AffParseResult = {
3
+ rules: AffixRules;
4
+ flags: AffFlags;
5
+ /** Raw COMPOUNDRULE pattern strings (not yet RegExp). */
6
+ compoundRulePatterns: string[];
7
+ replacementTable: ReplacementPair[];
8
+ };
9
+ /**
10
+ * Parse Hunspell `.aff` content into rules, named flags, compound patterns, and REP pairs.
11
+ */
12
+ export declare function parseAff(data: string): AffParseResult;
13
+ //# sourceMappingURL=parseAff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseAff.d.ts","sourceRoot":"","sources":["../src/parseAff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAc,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAIjF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,yDAAyD;IACzD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,gBAAgB,EAAE,eAAe,EAAE,CAAC;CACrC,CAAC;AAqBF;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAqHrD"}
@@ -0,0 +1,13 @@
1
+ import { AffFlags, AffixRules, DictionaryTable } from './types';
2
+ export type ParseDicOptions = {
3
+ rules: AffixRules;
4
+ flags: AffFlags;
5
+ /** Mutated: flag code → stems that carry that compound flag. */
6
+ compoundRuleCodes: Record<string, string[]>;
7
+ };
8
+ /**
9
+ * Parse Hunspell `.dic` content into a word→flags lookup table.
10
+ * Also expands affix forms into the table (Typo.js approach).
11
+ */
12
+ export declare function parseDic(data: string, options: ParseDicOptions): DictionaryTable;
13
+ //# sourceMappingURL=parseDic.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { AffFlags, AffixRules, DictionaryTable } from './types';
2
+ export type ParseDicOptions = {
3
+ rules: AffixRules;
4
+ flags: AffFlags;
5
+ /** Mutated: flag code → stems that carry that compound flag. */
6
+ compoundRuleCodes: Record<string, string[]>;
7
+ };
8
+ /**
9
+ * Parse Hunspell `.dic` content into a word→flags lookup table.
10
+ * Also expands affix forms into the table (Typo.js approach).
11
+ */
12
+ export declare function parseDic(data: string, options: ParseDicOptions): DictionaryTable;
13
+ //# sourceMappingURL=parseDic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseDic.d.ts","sourceRoot":"","sources":["../src/parseDic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAOrE,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,gEAAgE;IAChE,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC7C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,eAAe,CAqFhF"}
@@ -0,0 +1,7 @@
1
+ import { AffFlags } from './types';
2
+ /**
3
+ * Split a Hunspell flag string according to the `.aff` `FLAG` mode.
4
+ * Default: one character per flag.
5
+ */
6
+ export declare function parseRuleCodes(textCodes: string | undefined, flags: AffFlags): string[];
7
+ //# sourceMappingURL=parseRuleCodes.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { AffFlags } from './types';
2
+ /**
3
+ * Split a Hunspell flag string according to the `.aff` `FLAG` mode.
4
+ * Default: one character per flag.
5
+ */
6
+ export declare function parseRuleCodes(textCodes: string | undefined, flags: AffFlags): string[];
7
+ //# sourceMappingURL=parseRuleCodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseRuleCodes.d.ts","sourceRoot":"","sources":["../src/parseRuleCodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,MAAM,EAAE,CAqBvF"}
@@ -0,0 +1,6 @@
1
+ import { HunspellState } from './types';
2
+ /**
3
+ * Suggest corrections for a misspelling (REP table + edit-distance ranking).
4
+ */
5
+ export declare function suggest(state: HunspellState, word: string, limit?: number): string[];
6
+ //# sourceMappingURL=suggest.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { HunspellState } from './types';
2
+ /**
3
+ * Suggest corrections for a misspelling (REP table + edit-distance ranking).
4
+ */
5
+ export declare function suggest(state: HunspellState, word: string, limit?: number): string[];
6
+ //# sourceMappingURL=suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AA+K7C;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CA6B/E"}