tdk-api-wrapper 1.4.0 → 1.5.1

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.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  TDK: () => TDK,
34
+ TDKClient: () => TDKClient,
34
35
  TDKError: () => TDKError,
35
36
  TDKNetworkError: () => TDKNetworkError,
36
37
  TDKValidationError: () => TDKValidationError,
@@ -39,8 +40,10 @@ __export(src_exports, {
39
40
  getStemCandidates: () => getStemCandidates,
40
41
  isVowel: () => isVowel,
41
42
  restoreConsonantSoftening: () => restoreConsonantSoftening,
43
+ restoreGemination: () => restoreGemination,
42
44
  restoreInfinitive: () => restoreInfinitive,
43
- restoreVowelDrop: () => restoreVowelDrop
45
+ restoreVowelDrop: () => restoreVowelDrop,
46
+ restoreVowelNarrowing: () => restoreVowelNarrowing
44
47
  });
45
48
  module.exports = __toCommonJS(src_exports);
46
49
 
@@ -378,6 +381,12 @@ var TURKISH_SUFFIXES = [
378
381
  "s\u0131n",
379
382
  "sun",
380
383
  "s\xFCn",
384
+ "sen",
385
+ "san",
386
+ "sem",
387
+ "sam",
388
+ "sek",
389
+ "sak",
381
390
  "siz",
382
391
  "s\u0131z",
383
392
  "suz",
@@ -515,6 +524,43 @@ function restoreVowelDrop(stem) {
515
524
  }
516
525
  return [];
517
526
  }
527
+ function restoreGemination(stem) {
528
+ if (stem.length < 3)
529
+ return [];
530
+ const c1 = stem[stem.length - 2];
531
+ const c2 = stem[stem.length - 1];
532
+ if (c1 === c2 && !isVowel(c1)) {
533
+ const single = stem.slice(0, -1);
534
+ const hardened = restoreConsonantSoftening(single);
535
+ return [single, ...hardened];
536
+ }
537
+ return [];
538
+ }
539
+ function restoreVowelNarrowing(stem) {
540
+ if (stem.length < 2)
541
+ return [];
542
+ if (stem === "di")
543
+ return ["de"];
544
+ if (stem === "yi")
545
+ return ["ye"];
546
+ const lastChar = stem[stem.length - 1];
547
+ const isLastNarrow = "\u0131iu\xFC".includes(lastChar);
548
+ if (isLastNarrow) {
549
+ const vowelsInBase = stem.slice(0, -1).split("").filter(isVowel);
550
+ const lastVowel = vowelsInBase.length > 0 ? vowelsInBase[vowelsInBase.length - 1] : lastChar;
551
+ const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
552
+ return [stem.slice(0, -1) + widened];
553
+ }
554
+ if (!isVowel(lastChar)) {
555
+ const vowelsInBase = stem.split("").filter(isVowel);
556
+ if (vowelsInBase.length > 0) {
557
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
558
+ const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
559
+ return [stem + widened];
560
+ }
561
+ }
562
+ return [];
563
+ }
518
564
  function restoreInfinitive(stem) {
519
565
  if (stem.length < 2)
520
566
  return [];
@@ -538,6 +584,13 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
538
584
  seen.add(apostropheStem);
539
585
  }
540
586
  }
587
+ const bareInfinitives = restoreInfinitive(normalized);
588
+ for (const inf of bareInfinitives) {
589
+ if (!seen.has(inf) && inf !== normalized) {
590
+ seen.add(inf);
591
+ candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
592
+ }
593
+ }
541
594
  let frontier = [normalized];
542
595
  for (let depth = 0; depth < maxDepth; depth++) {
543
596
  const nextFrontier = [];
@@ -547,9 +600,14 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
547
600
  const stem = current.slice(0, -suffix.length);
548
601
  const hardened = restoreConsonantSoftening(stem);
549
602
  const vowelDropped = restoreVowelDrop(stem);
550
- const verbalBases = [stem, ...hardened];
603
+ const geminated = restoreGemination(stem);
604
+ const isNarrowingSuffix = suffix.startsWith("yor") || suffix.includes("iyor") || suffix.includes("\u0131yor") || suffix.includes("uyor") || suffix.includes("\xFCyor");
605
+ const isDeYeBuffer = (stem === "di" || stem === "yi") && suffix.startsWith("y");
606
+ const narrowed = isNarrowingSuffix || isDeYeBuffer ? restoreVowelNarrowing(stem) : [];
607
+ const isVerbSuffix = isNarrowingSuffix || suffix.includes("ecek") || suffix.includes("acak") || suffix.includes("mi\u015F") || suffix.includes("m\u0131\u015F") || suffix.includes("m\xFC\u015F") || suffix.includes("mu\u015F") || suffix.includes("mek") || suffix.includes("mak") || suffix.includes("erek") || suffix.includes("arak") || suffix.includes("dik") || suffix.includes("d\u0131k") || suffix.includes("duk") || suffix.includes("d\xFCk") || suffix.includes("tik") || suffix.includes("t\u0131k") || suffix.includes("tuk") || suffix.includes("t\xFCk") || suffix.includes("sen") || suffix.includes("san") || suffix.includes("sem") || suffix.includes("sam") || suffix.includes("sek") || suffix.includes("sak");
608
+ const verbalBases = [stem, ...hardened, ...narrowed];
551
609
  const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
552
- const variants = [stem, ...hardened, ...vowelDropped, ...infinitives];
610
+ const variants = [stem, ...hardened, ...vowelDropped, ...geminated, ...narrowed];
553
611
  for (const variant of variants) {
554
612
  if (!seen.has(variant) && variant !== normalized) {
555
613
  seen.add(variant);
@@ -557,6 +615,14 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
557
615
  candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
558
616
  }
559
617
  }
618
+ for (const inf of infinitives) {
619
+ if (!seen.has(inf) && inf !== normalized) {
620
+ seen.add(inf);
621
+ nextFrontier.push(inf);
622
+ const weight = isVerbSuffix ? stem.length + 5 : stem.length;
623
+ candidatesWithWeight.push({ candidate: inf, baseLength: weight });
624
+ }
625
+ }
560
626
  }
561
627
  }
562
628
  }
@@ -574,6 +640,159 @@ var path = __toESM(require("path"));
574
640
  var os = __toESM(require("os"));
575
641
  var https = __toESM(require("https"));
576
642
  var tls = __toESM(require("tls"));
643
+ var COMMON_MISSPELLINGS = {
644
+ // -şey ile biten ve ayrı yazılması zorunlu söz öbekleri
645
+ her\u015Fey: "her \u015Fey",
646
+ hersey: "her \u015Fey",
647
+ bir\u015Fey: "bir \u015Fey",
648
+ birsey: "bir \u015Fey",
649
+ hi\u00E7bir\u015Fey: "hi\xE7bir \u015Fey",
650
+ hicbirsey: "hi\xE7bir \u015Fey",
651
+ \u00E7ok\u015Fey: "\xE7ok \u015Fey",
652
+ coksey: "\xE7ok \u015Fey",
653
+ \u015Feyler: "\u015Feyler",
654
+ seyler: "\u015Feyler",
655
+ herhangibir\u015Fey: "herhangi bir \u015Fey",
656
+ herhangibirsey: "herhangi bir \u015Fey",
657
+ // Sıkça birleşik yazılan ama ayrı yazılması gereken sözler
658
+ herg\u00FCn: "her g\xFCn",
659
+ hergun: "her g\xFCn",
660
+ herzaman: "her zaman",
661
+ heran: "her an",
662
+ heryer: "her yer",
663
+ herbiri: "her biri",
664
+ pek\u00E7ok: "pek \xE7ok",
665
+ pekcok: "pek \xE7ok",
666
+ pekaz: "pek az",
667
+ yada: "ya da",
668
+ tabiki: "tabii ki",
669
+ tabiiki: "tabii ki",
670
+ sa\u011Fol: "sa\u011F ol",
671
+ sagol: "sa\u011F ol",
672
+ sa\u011Folun: "sa\u011F olun",
673
+ sagolun: "sa\u011F olun",
674
+ ho\u015F\u00E7akal: "ho\u015F\xE7a kal",
675
+ hoscakal: "ho\u015F\xE7a kal",
676
+ ho\u015Fgeldin: "ho\u015F geldin",
677
+ hosgeldin: "ho\u015F geldin",
678
+ ho\u015Fgeldiniz: "ho\u015F geldiniz",
679
+ hosgeldiniz: "ho\u015F geldiniz",
680
+ ho\u015Fbulduk: "ho\u015F bulduk",
681
+ hosbulduk: "ho\u015F bulduk",
682
+ yan\u0131s\u0131ra: "yan\u0131 s\u0131ra",
683
+ yanisira: "yan\u0131 s\u0131ra",
684
+ pe\u015Fis\u0131ra: "pe\u015Fi s\u0131ra",
685
+ pesisira: "pe\u015Fi s\u0131ra",
686
+ ard\u0131s\u0131ra: "ard\u0131 s\u0131ra",
687
+ ardisira: "ard\u0131 s\u0131ra",
688
+ artarda: "art arda",
689
+ y\u00FCzy\u00FCze: "y\xFCz y\xFCze",
690
+ yuzyuze: "y\xFCz y\xFCze",
691
+ elele: "el ele",
692
+ g\u00F6zg\u00F6ze: "g\xF6z g\xF6ze",
693
+ ba\u015Fba\u015Fa: "ba\u015F ba\u015Fa",
694
+ basbasa: "ba\u015F ba\u015Fa",
695
+ yanyana: "yan yana",
696
+ i\u00E7i\u00E7e: "i\xE7 i\xE7e",
697
+ icice: "i\xE7 i\xE7e",
698
+ \u00FCst\u00FCste: "\xFCst \xFCste",
699
+ ustuste: "\xFCst \xFCste",
700
+ altalta: "alt alta",
701
+ \u00F6ns\u00F6z: "\xF6n s\xF6z",
702
+ onsoz: "\xF6n s\xF6z",
703
+ \u00F6nyarg\u0131: "\xF6n yarg\u0131",
704
+ onyargi: "\xF6n yarg\u0131",
705
+ farketmek: "fark etmek",
706
+ farketti: "fark etti",
707
+ farkettim: "fark ettim",
708
+ farkeder: "fark eder",
709
+ farketmez: "fark etmez",
710
+ terketmek: "terk etmek",
711
+ terketti: "terk etti",
712
+ ay\u0131rdetmek: "ay\u0131rt etmek",
713
+ ay\u0131rtetmek: "ay\u0131rt etmek",
714
+ arzetmek: "arz etmek",
715
+ arzederim: "arz ederim",
716
+ varolmak: "var olmak",
717
+ yokolmak: "yok olmak",
718
+ haketmek: "hak etmek",
719
+ haketti: "hak etti",
720
+ hakkaten: "hakikaten",
721
+ hi\u00E7kimse: "hi\xE7 kimse",
722
+ hickimse: "hi\xE7 kimse",
723
+ // Ünlü düşmesi yapılmaması gereken yer bildiren sözler (TDK Kural 15)
724
+ burda: "burada",
725
+ burdan: "buradan",
726
+ \u015Furda: "\u015Furada",
727
+ surda: "\u015Furada",
728
+ \u015Furdan: "\u015Furadan",
729
+ surdan: "\u015Furadan",
730
+ orda: "orada",
731
+ ordan: "oradan",
732
+ i\u00E7erde: "i\xE7eride",
733
+ icerde: "i\xE7eride",
734
+ i\u00E7erden: "i\xE7eriden",
735
+ icerden: "i\xE7eriden",
736
+ d\u0131\u015Farda: "d\u0131\u015Far\u0131da",
737
+ disarda: "d\u0131\u015Far\u0131da",
738
+ d\u0131\u015Fardan: "d\u0131\u015Far\u0131dan",
739
+ disardan: "d\u0131\u015Far\u0131dan",
740
+ yukarda: "yukar\u0131da",
741
+ yukardan: "yukar\u0131dan",
742
+ // Sıkça yanlış yazılan sözcükler
743
+ herkez: "herkes",
744
+ yanl\u0131z: "yaln\u0131z",
745
+ yaln\u0131\u015F: "yanl\u0131\u015F",
746
+ orjinal: "orijinal",
747
+ labaratuar: "laboratuvar",
748
+ laboratuar: "laboratuvar",
749
+ \u015F\u00F6f\u00F6r: "\u015Fof\xF6r",
750
+ sofor: "\u015Fof\xF6r",
751
+ egzos: "egzoz",
752
+ eksoz: "egzoz",
753
+ ekzoz: "egzoz",
754
+ kiprik: "kirpik",
755
+ kirbit: "kibrit",
756
+ klavuz: "k\u0131lavuz",
757
+ k\u0131ravat: "kravat",
758
+ s\u00FCpriz: "s\xFCrpriz",
759
+ supriz: "s\xFCrpriz",
760
+ raslant\u0131: "rastlant\u0131",
761
+ hastahane: "hastane",
762
+ pastahane: "pastane",
763
+ postahane: "postane",
764
+ eczahane: "eczane",
765
+ meyva: "meyve",
766
+ sarm\u0131sak: "sar\u0131msak",
767
+ dinazor: "dinozor",
768
+ pantalon: "pantolon",
769
+ tesbih: "tespih",
770
+ ah\u00E7\u0131: "a\u015F\xE7\u0131",
771
+ matba: "matbaa",
772
+ idda: "iddia",
773
+ iddaa: "iddia",
774
+ muhattap: "muhatap",
775
+ tra\u015F: "t\u0131ra\u015F",
776
+ karn\u0131bahar: "karnabahar",
777
+ kareografi: "koreografi",
778
+ poa\u00E7a: "po\u011Fa\xE7a",
779
+ poha\u00E7a: "po\u011Fa\xE7a",
780
+ \u015Farz: "\u015Farj",
781
+ sarj: "\u015Farj",
782
+ makina: "makine",
783
+ m\u00FCsade: "m\xFCsaade",
784
+ entellekt\u00FCel: "entelekt\xFCel",
785
+ inisiyatif: "inisiyatif",
786
+ insiyatif: "inisiyatif",
787
+ sezeryan: "sezaryen",
788
+ dok\u00FCman: "dok\xFCman",
789
+ d\u00F6k\u00FCman: "dok\xFCman",
790
+ erozyon: "erozyon",
791
+ erizyon: "erozyon",
792
+ anane: "anneanne",
793
+ babaanne: "babaanne"
794
+ };
795
+ var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
577
796
  var TDK = class {
578
797
  static BASE_URL = "https://sozluk.gov.tr";
579
798
  static AUDIO_API_HOST = "api.sozluk.gov.tr";
@@ -654,6 +873,10 @@ M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID
654
873
  yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
655
874
  -----END CERTIFICATE-----`
656
875
  ];
876
+ // Configuration
877
+ static defaultTimeoutMs = 8e3;
878
+ static defaultRetries = 1;
879
+ static maxCacheSize = 1e3;
657
880
  // Cache Mechanism
658
881
  static isCacheEnabled = false;
659
882
  static wordCache = /* @__PURE__ */ new Map();
@@ -661,6 +884,19 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
661
884
  static autocompleteCache = [];
662
885
  static autocompleteSet = /* @__PURE__ */ new Set();
663
886
  static stemCache = /* @__PURE__ */ new Map();
887
+ /**
888
+ * Configures global client options such as network timeout, retries, and cache size.
889
+ */
890
+ static configure(config) {
891
+ if (config.timeoutMs !== void 0)
892
+ this.defaultTimeoutMs = Math.max(100, config.timeoutMs);
893
+ if (config.retries !== void 0)
894
+ this.defaultRetries = Math.max(0, config.retries);
895
+ if (config.cache !== void 0)
896
+ this.enableCache(config.cache);
897
+ if (config.maxCacheSize !== void 0)
898
+ this.maxCacheSize = Math.max(10, config.maxCacheSize);
899
+ }
664
900
  /**
665
901
  * Enables or disables in-memory caching for API requests.
666
902
  */
@@ -680,9 +916,50 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
680
916
  this.autocompleteSet.clear();
681
917
  this.stemCache.clear();
682
918
  }
919
+ static setBoundedCache(map, key, value) {
920
+ if (map.size >= this.maxCacheSize) {
921
+ const firstKey = map.keys().next().value;
922
+ if (firstKey !== void 0)
923
+ map.delete(firstKey);
924
+ }
925
+ map.set(key, value);
926
+ }
683
927
  static delay(ms) {
684
928
  return new Promise((resolve) => setTimeout(resolve, ms));
685
929
  }
930
+ /**
931
+ * Internal helper that performs HTTP fetch with timeout and automatic retry on network/5xx errors.
932
+ */
933
+ static async fetchWithRetry(url, options = {}, retries = this.defaultRetries, timeoutMs = this.defaultTimeoutMs) {
934
+ let lastError;
935
+ for (let attempt = 0; attempt <= retries; attempt++) {
936
+ try {
937
+ const signal = AbortSignal.timeout(timeoutMs);
938
+ const headers = {
939
+ "User-Agent": "TDK-API-Nodejs-Wrapper/1.0",
940
+ ...options.headers || {}
941
+ };
942
+ const res = await fetch(url, { ...options, headers, signal });
943
+ if (res.ok || res.status >= 400 && res.status < 500) {
944
+ return res;
945
+ }
946
+ if (attempt < retries) {
947
+ await this.delay(200 * (attempt + 1));
948
+ continue;
949
+ }
950
+ return res;
951
+ } catch (err) {
952
+ lastError = err;
953
+ if (attempt < retries) {
954
+ await this.delay(200 * (attempt + 1));
955
+ continue;
956
+ }
957
+ }
958
+ }
959
+ throw new TDKNetworkError(`Request to ${url} failed after ${retries + 1} attempts.`, {
960
+ cause: lastError
961
+ });
962
+ }
686
963
  /**
687
964
  * Fetches detailed information for a given word from the TDK Dictionary.
688
965
  */
@@ -697,9 +974,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
697
974
  const url = `${this.BASE_URL}/gts?ara=${encodeURIComponent(cleanWord)}`;
698
975
  let response;
699
976
  try {
700
- response = await fetch(url, {
701
- headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
702
- });
977
+ response = await this.fetchWithRetry(url);
703
978
  } catch (error) {
704
979
  throw new TDKNetworkError("Failed to fetch word from TDK: request failed.", { cause: error });
705
980
  }
@@ -716,12 +991,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
716
991
  }
717
992
  if (!Array.isArray(data) && data && "error" in data) {
718
993
  if (this.isCacheEnabled)
719
- this.wordCache.set(cleanWord, []);
994
+ this.setBoundedCache(this.wordCache, cleanWord, []);
720
995
  return [];
721
996
  }
722
997
  const results = data;
723
998
  if (this.isCacheEnabled) {
724
- this.wordCache.set(cleanWord, results);
999
+ this.setBoundedCache(this.wordCache, cleanWord, results);
725
1000
  }
726
1001
  return results;
727
1002
  }
@@ -848,17 +1123,17 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
848
1123
  return this.stemCache.get(clean);
849
1124
  }
850
1125
  if (await this.isHeadword(clean)) {
851
- this.stemCache.set(clean, clean);
1126
+ this.setBoundedCache(this.stemCache, clean, clean);
852
1127
  return clean;
853
1128
  }
854
1129
  const candidates = getStemCandidates(clean);
855
1130
  for (const candidate of candidates) {
856
1131
  if (await this.isHeadword(candidate)) {
857
- this.stemCache.set(clean, candidate);
1132
+ this.setBoundedCache(this.stemCache, clean, candidate);
858
1133
  return candidate;
859
1134
  }
860
1135
  }
861
- this.stemCache.set(clean, null);
1136
+ this.setBoundedCache(this.stemCache, clean, null);
862
1137
  return null;
863
1138
  }
864
1139
  /**
@@ -1081,25 +1356,45 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1081
1356
  * Checks spelling and returns suggestions if wrong.
1082
1357
  */
1083
1358
  static async checkSpelling(word) {
1359
+ if (!word || word.trim() === "") {
1360
+ return { isCorrect: false, word };
1361
+ }
1362
+ const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
1084
1363
  const results = await this.getWord(word);
1085
1364
  if (results.length > 0) {
1086
1365
  return { isCorrect: true, word };
1087
1366
  }
1367
+ if (COMMON_MISSPELLINGS[cleanWord]) {
1368
+ return { isCorrect: false, word, suggestion: COMMON_MISSPELLINGS[cleanWord] };
1369
+ }
1370
+ const seyMatch = cleanWord.match(/^(.+?)(?:şey|sey)([ıiuaeüodekmnl]+)?$/);
1371
+ if (seyMatch && !SEY_EXCEPTIONS.has(cleanWord)) {
1372
+ let prefix = seyMatch[1];
1373
+ const suffix = seyMatch[2] || "";
1374
+ if (prefix === "hicbir")
1375
+ prefix = "hi\xE7bir";
1376
+ if (prefix === "cok")
1377
+ prefix = "\xE7ok";
1378
+ return {
1379
+ isCorrect: false,
1380
+ word,
1381
+ suggestion: `${prefix} \u015Fey${suffix}`
1382
+ };
1383
+ }
1088
1384
  const daily = await this.getDailyContent();
1089
1385
  if (daily) {
1090
- const syydMatch = daily.syyd.find((s) => s.yanliskelime.toLocaleLowerCase("tr-TR") === word.toLocaleLowerCase("tr-TR"));
1386
+ const syydMatch = daily.syyd.find((s) => s.yanliskelime.toLocaleLowerCase("tr-TR") === cleanWord);
1091
1387
  if (syydMatch) {
1092
1388
  return { isCorrect: false, word, suggestion: syydMatch.dogrukelime };
1093
1389
  }
1094
- const mixMatch = daily.karistirma.find((s) => s.yanlis.toLocaleLowerCase("tr-TR") === word.toLocaleLowerCase("tr-TR"));
1390
+ const mixMatch = daily.karistirma.find((s) => s.yanlis.toLocaleLowerCase("tr-TR") === cleanWord);
1095
1391
  if (mixMatch) {
1096
1392
  return { isCorrect: false, word, suggestion: mixMatch.dogru };
1097
1393
  }
1098
1394
  }
1099
1395
  const root = await this.findRoot(word);
1100
1396
  if (root) {
1101
- const cleanWord2 = word.trim().toLocaleLowerCase("tr-TR");
1102
- const isInflected = root !== cleanWord2;
1397
+ const isInflected = root !== cleanWord;
1103
1398
  return {
1104
1399
  isCorrect: true,
1105
1400
  word,
@@ -1110,24 +1405,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1110
1405
  if (this.autocompleteCache.length === 0) {
1111
1406
  this.autocompleteCache = await this.fetchAutocompleteData();
1112
1407
  }
1113
- const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
1408
+ for (const candidate of this.autocompleteCache) {
1409
+ if (candidate.includes(" ")) {
1410
+ const candidateNoSpace = candidate.replace(/\s+/g, "").toLocaleLowerCase("tr-TR");
1411
+ if (candidateNoSpace === cleanWord) {
1412
+ return { isCorrect: false, word, suggestion: candidate };
1413
+ }
1414
+ }
1415
+ }
1114
1416
  let best = null;
1115
1417
  for (const candidate of this.autocompleteCache) {
1116
1418
  if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR"))
1117
1419
  continue;
1118
1420
  if (Math.abs(candidate.length - cleanWord.length) > 2)
1119
1421
  continue;
1120
- const distance = this.damerauLevenshtein(cleanWord, candidate);
1121
- if (distance === 0)
1422
+ const rawDist = this.damerauLevenshtein(cleanWord, candidate);
1423
+ if (rawDist === 0)
1122
1424
  continue;
1123
1425
  const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
1124
1426
  const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
1427
+ const distance = rawDist + (firstMismatch > 0 ? 1.2 : 0);
1125
1428
  const better = !best || distance < best.distance || distance === best.distance && firstMismatch < best.firstMismatch || distance === best.distance && firstMismatch === best.firstMismatch && lengthMismatch < best.lengthMismatch;
1126
1429
  if (better) {
1127
- best = { candidate, distance, firstMismatch, lengthMismatch };
1430
+ best = { candidate, distance, rawDist, firstMismatch, lengthMismatch };
1128
1431
  }
1129
1432
  }
1130
- if (best && best.distance <= 2) {
1433
+ if (best && best.rawDist <= 2 && (best.firstMismatch === 0 || best.rawDist <= 1)) {
1131
1434
  return { isCorrect: false, word, suggestion: best.candidate };
1132
1435
  }
1133
1436
  return { isCorrect: false, word };
@@ -1534,14 +1837,16 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1534
1837
  meaningCount: meaningsA.length,
1535
1838
  origin: originA,
1536
1839
  syllables: this.syllabicate(a),
1537
- harmony: this.checkVowelHarmony(a)
1840
+ harmony: this.checkVowelHarmony(a),
1841
+ labialHarmony: this.checkLabialHarmony(a)
1538
1842
  },
1539
1843
  b: {
1540
1844
  word: b,
1541
1845
  meaningCount: meaningsB.length,
1542
1846
  origin: originB,
1543
1847
  syllables: this.syllabicate(b),
1544
- harmony: this.checkVowelHarmony(b)
1848
+ harmony: this.checkVowelHarmony(b),
1849
+ labialHarmony: this.checkLabialHarmony(b)
1545
1850
  }
1546
1851
  };
1547
1852
  }
@@ -1672,9 +1977,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1672
1977
  }
1673
1978
  /**
1674
1979
  * Syllabicates a Turkish word based on general grammar rules.
1980
+ * Handles syllable separation for vowels, single consonants, double consonants,
1981
+ * and western loanword three-consonant clusters (e.g. e-lek-trik, kon-trol, or-kes-tra).
1675
1982
  */
1676
1983
  static syllabicate(word) {
1677
1984
  const vowels = /[aeıioöuüAEIİOÖUÜ]/;
1985
+ const ONSET_CLUSTERS = /* @__PURE__ */ new Set(["tr", "pr", "kr", "gr", "br", "fr", "dr", "pl", "kl", "fl", "bl", "gl"]);
1678
1986
  const result = [];
1679
1987
  let currentSyllable = "";
1680
1988
  for (let i = word.length - 1; i >= 0; i--) {
@@ -1685,8 +1993,13 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1685
1993
  currentSyllable = word[i - 1] + currentSyllable;
1686
1994
  i--;
1687
1995
  } else if (i - 2 >= 0 && !vowels.test(word[i - 2])) {
1688
- currentSyllable = word[i - 1] + currentSyllable;
1689
- i--;
1996
+ if (i - 3 >= 0 && !vowels.test(word[i - 3]) && ONSET_CLUSTERS.has((word[i - 2] + word[i - 1]).toLowerCase())) {
1997
+ currentSyllable = word[i - 2] + word[i - 1] + currentSyllable;
1998
+ i -= 2;
1999
+ } else {
2000
+ currentSyllable = word[i - 1] + currentSyllable;
2001
+ i--;
2002
+ }
1690
2003
  }
1691
2004
  }
1692
2005
  result.unshift(currentSyllable);
@@ -1716,10 +2029,408 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1716
2029
  const hasFront = frontVowels.test(lower);
1717
2030
  return !(hasBack && hasFront);
1718
2031
  }
2032
+ /**
2033
+ * Checks if a word follows Turkish Minor Vowel Harmony (Küçük Ünlü Uyumu / Labial Harmony).
2034
+ * Rules:
2035
+ * 1. After an unrounded vowel (a, e, ı, i), only unrounded vowels (a, e, ı, i) can follow.
2036
+ * 2. After a rounded vowel (o, ö, u, ü), either an unrounded wide (a, e) or rounded narrow (u, ü) vowel can follow.
2037
+ * Single-syllable words and words with <=1 vowel are considered compliant by convention.
2038
+ */
2039
+ static checkLabialHarmony(word) {
2040
+ const lower = word.toLocaleLowerCase("tr-TR");
2041
+ const vowels = lower.split("").filter((ch) => "ae\u0131io\xF6u\xFC".includes(ch));
2042
+ if (vowels.length <= 1)
2043
+ return true;
2044
+ for (let i = 0; i < vowels.length - 1; i++) {
2045
+ const v1 = vowels[i];
2046
+ const v2 = vowels[i + 1];
2047
+ if ("ae\u0131i".includes(v1)) {
2048
+ if (!"ae\u0131i".includes(v2))
2049
+ return false;
2050
+ } else if ("o\xF6u\xFC".includes(v1)) {
2051
+ if (!"aeu\xFC".includes(v2))
2052
+ return false;
2053
+ }
2054
+ }
2055
+ return true;
2056
+ }
2057
+ /**
2058
+ * Searches TDK headwords using a wildcard / pattern string.
2059
+ * Wildcards:
2060
+ * '_' or '?' matches any single character
2061
+ * '*' matches zero or more characters
2062
+ * Example: "k_l_m" matches "kalem", "kelam", "kilim".
2063
+ * Runs in-memory against TDK's 81k headword list.
2064
+ */
2065
+ static async patternSearch(pattern, options) {
2066
+ if (!pattern || pattern.trim() === "")
2067
+ return [];
2068
+ await this.ensureAutocompleteLoaded();
2069
+ const cleanPattern = pattern.trim().toLocaleLowerCase("tr-TR");
2070
+ const escaped = cleanPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/[_?]/g, "[\\p{L}]").replace(/\*/g, "[\\p{L}]*");
2071
+ const regex = new RegExp(`^${escaped}$`, "u");
2072
+ const max = options?.maxResults ?? 50;
2073
+ const matches = [];
2074
+ for (const headword of this.autocompleteCache) {
2075
+ const lower = headword.toLocaleLowerCase("tr-TR");
2076
+ if (regex.test(lower)) {
2077
+ matches.push(headword);
2078
+ if (matches.length >= max)
2079
+ break;
2080
+ }
2081
+ }
2082
+ return matches;
2083
+ }
2084
+ /**
2085
+ * Finds headwords in TDK that can be formed from the given letters (anagrams).
2086
+ * If exact-length anagrams exist, they are returned.
2087
+ * If none exist (or exactLength is false), valid sub-anagrams (words using a subset of the letters,
2088
+ * minimum 3 letters) are returned, sorted by length descending.
2089
+ */
2090
+ static async findAnagrams(letters, options) {
2091
+ if (!letters || letters.trim() === "")
2092
+ return [];
2093
+ await this.ensureAutocompleteLoaded();
2094
+ const clean = letters.trim().toLocaleLowerCase("tr-TR").replace(/[^a-zçğıöşüâîû]/gi, "");
2095
+ if (clean.length === 0)
2096
+ return [];
2097
+ const forceExact = options?.exactLength === true;
2098
+ const max = options?.maxResults ?? 50;
2099
+ const getFrequency = (str) => {
2100
+ const freq = {};
2101
+ for (const ch of str) {
2102
+ freq[ch] = (freq[ch] || 0) + 1;
2103
+ }
2104
+ return freq;
2105
+ };
2106
+ const targetFreq = getFrequency(clean);
2107
+ const exactMatches = [];
2108
+ const subMatches = [];
2109
+ for (const headword of this.autocompleteCache) {
2110
+ const lower = headword.toLocaleLowerCase("tr-TR");
2111
+ if (lower.includes(" ") || lower.includes("-"))
2112
+ continue;
2113
+ if (lower.length > clean.length || lower.length < 3)
2114
+ continue;
2115
+ const wordFreq = getFrequency(lower);
2116
+ let isValid = true;
2117
+ for (const [ch, count] of Object.entries(wordFreq)) {
2118
+ if (!targetFreq[ch] || targetFreq[ch] < count) {
2119
+ isValid = false;
2120
+ break;
2121
+ }
2122
+ }
2123
+ if (isValid && lower !== clean) {
2124
+ if (lower.length === clean.length) {
2125
+ exactMatches.push(headword);
2126
+ } else {
2127
+ subMatches.push(headword);
2128
+ }
2129
+ }
2130
+ }
2131
+ if (exactMatches.length > 0 || forceExact) {
2132
+ return exactMatches.slice(0, max);
2133
+ }
2134
+ subMatches.sort((a, b) => b.length - a.length || a.localeCompare(b, "tr-TR"));
2135
+ return subMatches.slice(0, max);
2136
+ }
2137
+ /**
2138
+ * Finds words in TDK that rhyme with the given word (sharing the same ending suffix/letters).
2139
+ * @param word The target word
2140
+ * @param options.minLetters Minimum number of ending characters that must match (default: 3)
2141
+ * @param options.maxResults Maximum number of rhyme results to return (default: 50)
2142
+ */
2143
+ static async findRhymes(word, options) {
2144
+ if (!word || word.trim() === "")
2145
+ return [];
2146
+ await this.ensureAutocompleteLoaded();
2147
+ const clean = word.trim().toLocaleLowerCase("tr-TR");
2148
+ const minLetters = Math.min(options?.minLetters ?? 3, clean.length);
2149
+ const max = options?.maxResults ?? 50;
2150
+ const suffix = clean.slice(-minLetters);
2151
+ const results = [];
2152
+ for (const headword of this.autocompleteCache) {
2153
+ const lower = headword.toLocaleLowerCase("tr-TR");
2154
+ if (lower !== clean && lower.endsWith(suffix) && !lower.includes(" ")) {
2155
+ results.push(headword);
2156
+ if (results.length >= max)
2157
+ break;
2158
+ }
2159
+ }
2160
+ return results;
2161
+ }
2162
+ /**
2163
+ * Performs comprehensive spelling, grammar, and syntax proofreading on a Turkish text.
2164
+ * Detects:
2165
+ * 1. Conjunction 'da/de' erroneously joined to verbs or words (e.g. "gitsende" -> "gitsen de")
2166
+ * 2. Conjunction 'ki' erroneously joined to verbs (e.g. "gördümki" -> "gördüm ki"), respecting SOMBAHÇEMİ exceptions
2167
+ * 3. Question particle 'mi/mı/mu/mü' erroneously joined to words (e.g. "geldimi" -> "geldi mi")
2168
+ * 4. Misspelled words with dictionary suggestions (via edit-distance & morphology)
2169
+ */
2170
+ static async proofread(text) {
2171
+ if (!text || text.trim() === "") {
2172
+ return { text: text || "", issues: [], isCorrect: true };
2173
+ }
2174
+ await this.ensureAutocompleteLoaded();
2175
+ const issues = [];
2176
+ const SOMBAHCEMI = /* @__PURE__ */ new Set([
2177
+ "sanki",
2178
+ "oysaki",
2179
+ "mademki",
2180
+ "belki",
2181
+ "halbuki",
2182
+ "\xE7\xFCnk\xFC",
2183
+ "me\u011Ferki",
2184
+ "illaki"
2185
+ ]);
2186
+ const PHRASE_MISTAKES = [
2187
+ {
2188
+ regex: /\bhiç\s+bir\b/gi,
2189
+ suggestion: "hi\xE7bir",
2190
+ message: "'hi\xE7bir' belgisiz s\u0131fat\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2191
+ type: "spelling"
2192
+ },
2193
+ {
2194
+ regex: /\bbir\s+çok\b/gi,
2195
+ suggestion: "bir\xE7ok",
2196
+ message: "'bir\xE7ok' belgisiz s\u0131fat\u0131/zamiri biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2197
+ type: "spelling"
2198
+ },
2199
+ {
2200
+ regex: /\bbir\s+kaç\b/gi,
2201
+ suggestion: "birka\xE7",
2202
+ message: "'birka\xE7' belgisiz s\u0131fat\u0131/zamiri biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2203
+ type: "spelling"
2204
+ },
2205
+ {
2206
+ regex: /\bbir\s+az\b/gi,
2207
+ suggestion: "biraz",
2208
+ message: "'biraz' s\xF6zc\xFC\u011F\xFC biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2209
+ type: "spelling"
2210
+ },
2211
+ {
2212
+ regex: /\bher\s+hangi\b/gi,
2213
+ suggestion: "herhangi",
2214
+ message: "'herhangi' s\xF6zc\xFC\u011F\xFC biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2215
+ type: "spelling"
2216
+ },
2217
+ {
2218
+ regex: /\bgit\s+gide\b/gi,
2219
+ suggestion: "gitgide",
2220
+ message: "'gitgide' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2221
+ type: "spelling"
2222
+ },
2223
+ {
2224
+ regex: /\bbirden\s+bire\b/gi,
2225
+ suggestion: "birdenbire",
2226
+ message: "'birdenbire' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2227
+ type: "spelling"
2228
+ },
2229
+ {
2230
+ regex: /\brast\s+gele\b/gi,
2231
+ suggestion: "rastgele",
2232
+ message: "'rastgele' zarf\u0131 biti\u015Fik yaz\u0131lmal\u0131d\u0131r.",
2233
+ type: "spelling"
2234
+ }
2235
+ ];
2236
+ const coveredRanges = [];
2237
+ for (const pm of PHRASE_MISTAKES) {
2238
+ let pmMatch;
2239
+ while ((pmMatch = pm.regex.exec(text)) !== null) {
2240
+ const start = pmMatch.index;
2241
+ const end = start + pmMatch[0].length;
2242
+ coveredRanges.push({ start, end });
2243
+ issues.push({
2244
+ type: pm.type,
2245
+ word: pmMatch[0],
2246
+ startIndex: start,
2247
+ endIndex: end,
2248
+ suggestion: pm.suggestion,
2249
+ message: pm.message
2250
+ });
2251
+ }
2252
+ }
2253
+ const tokenRegex = /[\p{L}0-9'’]+/gu;
2254
+ let match;
2255
+ while ((match = tokenRegex.exec(text)) !== null) {
2256
+ const rawWord = match[0];
2257
+ const startIndex = match.index;
2258
+ const endIndex = startIndex + rawWord.length;
2259
+ const lower = rawWord.toLocaleLowerCase("tr-TR");
2260
+ if (/^\d+$/.test(lower))
2261
+ continue;
2262
+ if (coveredRanges.some((r) => startIndex >= r.start && endIndex <= r.end))
2263
+ continue;
2264
+ let flagged = false;
2265
+ const questionMatch = lower.match(/^(.+?)(m[ıiuü](?:sin|sın|sun|sün|siniz|sınız|sunuz|sünüz|yiz|yız|yuz|yüz|m|k)?)$/);
2266
+ if (questionMatch) {
2267
+ const base = questionMatch[1];
2268
+ const particle = questionMatch[2];
2269
+ if (base.length >= 2 && (await this.isHeadword(base) || await this.findRoot(base) !== null)) {
2270
+ if (!await this.isHeadword(lower)) {
2271
+ issues.push({
2272
+ type: "question_particle",
2273
+ word: rawWord,
2274
+ startIndex,
2275
+ endIndex,
2276
+ suggestion: `${base} ${particle}`,
2277
+ message: `'${particle}' soru eki kendinden \xF6nceki kelimeden ayr\u0131 yaz\u0131lmal\u0131d\u0131r.`
2278
+ });
2279
+ flagged = true;
2280
+ }
2281
+ }
2282
+ }
2283
+ const VERB_CONJUGATION_REGEX = /(?:d[ıiuü][kmmn]?|t[ıiuü][kmmn]?|d[ıiuü]n[ıiuü]z?|t[ıiuü]n[ıiuü]z?|m[ıiuü]ş(?:[szn][ıiuü]z?|lar)?|yor(?:um|sun|uz|lar)?|ecek(?:sin|iz|ler)?|acak(?:sın|ız|lar)?|s[ae][mnk]|s[ae]n[ıiz]?|meli|malı|me[mz]|ma[mz])$/i;
2284
+ if (!flagged && lower.endsWith("ki") && lower.length > 3) {
2285
+ const base = lower.slice(0, -2);
2286
+ if (!SOMBAHCEMI.has(lower)) {
2287
+ if (!await this.isHeadword(lower)) {
2288
+ const root = await this.findRoot(base);
2289
+ const isVerb = (base === "demek" || base === "kald\u0131" || base === "yeter" || base === "bilmem" || VERB_CONJUGATION_REGEX.test(base)) && (root ? root.endsWith("mek") || root.endsWith("mak") : true);
2290
+ if (isVerb) {
2291
+ issues.push({
2292
+ type: "conjunction_ki",
2293
+ word: rawWord,
2294
+ startIndex,
2295
+ endIndex,
2296
+ suggestion: `${base} ki`,
2297
+ message: `'ki' ba\u011Flac\u0131 ayr\u0131 yaz\u0131lmal\u0131d\u0131r.`
2298
+ });
2299
+ flagged = true;
2300
+ }
2301
+ }
2302
+ }
2303
+ }
2304
+ if (!flagged && (lower.endsWith("de") || lower.endsWith("da") || lower.endsWith("te") || lower.endsWith("ta")) && lower.length > 3) {
2305
+ const base = lower.slice(0, -2);
2306
+ const ending = lower.slice(-2);
2307
+ if (!await this.isHeadword(lower)) {
2308
+ const root = await this.findRoot(base);
2309
+ const isVerb = VERB_CONJUGATION_REGEX.test(base) && (root ? root.endsWith("mek") || root.endsWith("mak") : false);
2310
+ if (isVerb) {
2311
+ const correctEnding = ending.startsWith("t") ? ending === "te" ? "de" : "da" : ending;
2312
+ issues.push({
2313
+ type: "conjunction_da",
2314
+ word: rawWord,
2315
+ startIndex,
2316
+ endIndex,
2317
+ suggestion: `${base} ${correctEnding}`,
2318
+ message: `'da/de' ba\u011Flac\u0131 fiillerden sonra her zaman ayr\u0131 yaz\u0131l\u0131r (ba\u011Fla\xE7 olan da/de sertle\u015Fmez).`
2319
+ });
2320
+ flagged = true;
2321
+ }
2322
+ }
2323
+ }
2324
+ const seyMatch = lower.match(/^(.+?)(?:şey|sey)([ıiuaeüodekmnl]+)?$/);
2325
+ if (!flagged && seyMatch && !SEY_EXCEPTIONS.has(lower)) {
2326
+ let prefix = seyMatch[1];
2327
+ const suffix = seyMatch[2] || "";
2328
+ if (prefix === "hicbir")
2329
+ prefix = "hi\xE7bir";
2330
+ if (prefix === "cok")
2331
+ prefix = "\xE7ok";
2332
+ issues.push({
2333
+ type: "spelling",
2334
+ word: rawWord,
2335
+ startIndex,
2336
+ endIndex,
2337
+ suggestion: `${prefix} \u015Fey${suffix}`,
2338
+ message: "'\u015Fey' s\xF6zc\xFC\u011F\xFC kendinden \xF6nceki kelimeden ayr\u0131 yaz\u0131lmal\u0131d\u0131r."
2339
+ });
2340
+ flagged = true;
2341
+ }
2342
+ if (!flagged && lower === "yada") {
2343
+ issues.push({
2344
+ type: "spelling",
2345
+ word: rawWord,
2346
+ startIndex,
2347
+ endIndex,
2348
+ suggestion: "ya da",
2349
+ message: "'ya da' ba\u011Flac\u0131 her zaman ayr\u0131 yaz\u0131l\u0131r."
2350
+ });
2351
+ flagged = true;
2352
+ }
2353
+ if (!flagged && (lower === "burda" || lower === "\u015Furda" || lower === "surda" || lower === "orda" || lower === "i\xE7erde" || lower === "icerde" || lower === "d\u0131\u015Farda" || lower === "disarda" || lower === "yukarda")) {
2354
+ const correct = COMMON_MISSPELLINGS[lower] || lower;
2355
+ issues.push({
2356
+ type: "spelling",
2357
+ word: rawWord,
2358
+ startIndex,
2359
+ endIndex,
2360
+ suggestion: correct,
2361
+ message: `'${rawWord}' s\xF6zc\xFC\u011F\xFCnde \xFCnl\xFC d\xFC\u015Fmesi yap\u0131lmaz.`
2362
+ });
2363
+ flagged = true;
2364
+ }
2365
+ if (!flagged) {
2366
+ const check = await this.checkSpelling(rawWord);
2367
+ if (!check.isCorrect) {
2368
+ issues.push({
2369
+ type: "spelling",
2370
+ word: rawWord,
2371
+ startIndex,
2372
+ endIndex,
2373
+ suggestion: check.suggestion,
2374
+ message: check.suggestion ? `'${rawWord}' yanl\u0131\u015F yaz\u0131lm\u0131\u015F olabilir.` : `'${rawWord}' s\xF6zl\xFCkte bulunamad\u0131.`
2375
+ });
2376
+ }
2377
+ }
2378
+ }
2379
+ issues.sort((a, b) => a.startIndex - b.startIndex);
2380
+ return {
2381
+ text,
2382
+ issues,
2383
+ isCorrect: issues.length === 0
2384
+ };
2385
+ }
2386
+ };
2387
+ var TDKClient = class {
2388
+ constructor(config) {
2389
+ if (config) {
2390
+ TDK.configure(config);
2391
+ }
2392
+ }
2393
+ getWord(word) {
2394
+ return TDK.getWord(word);
2395
+ }
2396
+ getMeanings(word) {
2397
+ return TDK.getMeanings(word);
2398
+ }
2399
+ checkSpelling(word) {
2400
+ return TDK.checkSpelling(word);
2401
+ }
2402
+ findRoot(word) {
2403
+ return TDK.findRoot(word);
2404
+ }
2405
+ stem(word) {
2406
+ return TDK.stem(word);
2407
+ }
2408
+ proofread(text) {
2409
+ return TDK.proofread(text);
2410
+ }
2411
+ patternSearch(pattern, options) {
2412
+ return TDK.patternSearch(pattern, options);
2413
+ }
2414
+ findAnagrams(letters, options) {
2415
+ return TDK.findAnagrams(letters, options);
2416
+ }
2417
+ findRhymes(word, options) {
2418
+ return TDK.findRhymes(word, options);
2419
+ }
2420
+ syllabicate(word) {
2421
+ return TDK.syllabicate(word);
2422
+ }
2423
+ checkVowelHarmony(word) {
2424
+ return TDK.checkVowelHarmony(word);
2425
+ }
2426
+ checkLabialHarmony(word) {
2427
+ return TDK.checkLabialHarmony(word);
2428
+ }
1719
2429
  };
1720
2430
  // Annotate the CommonJS export names for ESM import in node:
1721
2431
  0 && (module.exports = {
1722
2432
  TDK,
2433
+ TDKClient,
1723
2434
  TDKError,
1724
2435
  TDKNetworkError,
1725
2436
  TDKValidationError,
@@ -1728,6 +2439,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1728
2439
  getStemCandidates,
1729
2440
  isVowel,
1730
2441
  restoreConsonantSoftening,
2442
+ restoreGemination,
1731
2443
  restoreInfinitive,
1732
- restoreVowelDrop
2444
+ restoreVowelDrop,
2445
+ restoreVowelNarrowing
1733
2446
  });