tdk-api-wrapper 1.7.0 → 1.7.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/README.md CHANGED
@@ -91,7 +91,7 @@ Aşağıdaki metotlar `TDK` sınıfı üzerinden statik olarak veya `TDKClient`
91
91
  - **`TDK.checkVowelHarmony(word)`**: Kelimenin büyük ünlü uyumuna uyup uymadığını (boolean) kontrol eder.
92
92
  - **`TDK.checkLabialHarmony(word)`**: Kelimenin küçük ünlü uyumuna (düzlük-yuvarlaklık uyumu) uyup uymadığını (boolean) kontrol eder.
93
93
  - **`TDK.getPartOfSpeech(word)`**: Kelimenin sözcük türünü (isim, sıfat, zarf vb.) döndürür.
94
- - **`TDK.checkSpelling(word)`**: Kelimenin doğru yazılıp yazılmadığını kontrol eder. Önce TDK'de doğrudan arar; bulamazsa TDK'nin "sık yapılan yanlışlar" listesini kontrol eder. Ardından **morfolojik ek sıyırma (stemming) motoru** devreye girer; kelime çekimli bir biçimse (`halılarımızın`, `kitabımız`, `çocuğa`, `okuyoruz`, `hakkımızda`, `başlıyor`) kökünü tespit edip `{ isCorrect: true, isInflected: true, root: "..." }` döner. Son aşamada ise TDK'nin ~81 bin kelimelik tam madde listesi üzerinde **klavye ve düzeltme işareti farkındalıklı** bir Damerau-Levenshtein edit-distance ile en yakın kelimeyi önerir: Türkçe Q klavyede yan yana duran tuşlar (`arabs` → `araba`, `swlam` → `selam`) ve ASCII/Türkçe harf ikilileri (`ı/i`, `ş/s`, `ö/o` …) tam bir düzeltme yerine bir düzeltmenin küçük bir kesri kadar sayılır; böylece hem en yakın hem de en olası madde kazanır (`yanlız` → `yalnız`).
94
+ - **`TDK.checkSpelling(word)`**: Kelimenin doğru yazılıp yazılmadığını kontrol eder. Önce TDK'de doğrudan arar; bulamazsa kütüphanenin dahili sık yapılan yanlışlar listesini (`COMMON_MISSPELLINGS` — [referans liste](https://gist.github.com/cemyilmazzzz122/e4bdd509fe9b03a3070a685ab603aaa8)) kontrol eder. Ardından **morfolojik ek sıyırma (stemming) motoru** devreye girer; kelime çekimli bir biçimse (`halılarımızın`, `kitabımız`, `çocuğa`, `okuyoruz`, `hakkımızda`, `başlıyor`) kökünü tespit edip `{ isCorrect: true, isInflected: true, root: "..." }` döner. Son aşamada ise TDK'nin ~81 bin kelimelik tam madde listesi üzerinde **klavye ve düzeltme işareti farkındalıklı** bir Damerau-Levenshtein edit-distance ile en yakın kelimeyi önerir: Türkçe Q klavyede yan yana duran tuşlar (`arabs` → `araba`, `swlam` → `selam`) ve ASCII/Türkçe harf ikilileri (`ı/i`, `ş/s`, `ö/o` …) tam bir düzeltme yerine bir düzeltmenin küçük bir kesri kadar sayılır; böylece hem en yakın hem de en olası madde kazanır (`yanlız` → `yalnız`).
95
95
  - **`TDK.getCompoundWords(word)`**: Aranan kelime ile oluşturulmuş birleşik kelimeleri (Örn: dolma kalem) listeler.
96
96
 
97
97
  ### 3. Morfoloji ve Kök Bulma (Morphology Engine)
@@ -181,4 +181,4 @@ try {
181
181
 
182
182
  ## Lisans
183
183
 
184
- Bu proje MIT Lisansı ile lisanslanmıştır. Kullanım hakları ve kısıtlamalar için kaynak kod içerisindeki lisans metnini inceleyebilirsiniz.
184
+ Bu proje GNU General Public License v3.0 (or later) ile lisanslanmıştır. Kullanım hakları ve kısıtlamalar için kaynak kod içerisindeki [LICENSE](./LICENSE) dosyasını inceleyebilirsiniz.
@@ -25,11 +25,7 @@ var TDKNetworkError = class extends TDKError {
25
25
  }
26
26
  };
27
27
 
28
- // src/morphology.ts
29
- var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
30
- function isVowel(ch) {
31
- return TURKISH_VOWELS.includes(ch);
32
- }
28
+ // src/data/suffixes.ts
33
29
  var TURKISH_SUFFIXES = [
34
30
  // 9-letter composite suffixes
35
31
  "lerimizden",
@@ -432,6 +428,12 @@ var TURKISH_SUFFIXES = [
432
428
  "u",
433
429
  "\xFC"
434
430
  ];
431
+
432
+ // src/morphology.ts
433
+ var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
434
+ function isVowel(ch) {
435
+ return TURKISH_VOWELS.includes(ch);
436
+ }
435
437
  function restoreConsonantSoftening(stem) {
436
438
  if (stem.length < 2)
437
439
  return [];
@@ -585,12 +587,7 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
585
587
  return [...new Set(candidatesWithWeight.map((c) => c.candidate))];
586
588
  }
587
589
 
588
- // src/tdk.ts
589
- import * as fs from "fs";
590
- import * as path from "path";
591
- import * as os from "os";
592
- import * as https from "https";
593
- import * as tls from "tls";
590
+ // src/data/misspellings.ts
594
591
  var COMMON_MISSPELLINGS = {
595
592
  // -şey ile biten ve ayrı yazılması zorunlu söz öbekleri
596
593
  her\u015Fey: "her \u015Fey",
@@ -744,75 +741,10 @@ var COMMON_MISSPELLINGS = {
744
741
  babaanne: "babaanne"
745
742
  };
746
743
  var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
747
- var KEYBOARD_ROWS = [
748
- ["qwertyu\u0131op\u011F\xFC", 0],
749
- ["asdfghjkl\u015Fi", 0.5],
750
- ["zxcvbnm\xF6\xE7", 1]
751
- ];
752
- var KEYBOARD_COORDS = (() => {
753
- const coords = {};
754
- KEYBOARD_ROWS.forEach(([keys, offset], row) => {
755
- [...keys].forEach((key, col) => {
756
- coords[key] = [col + offset, row];
757
- });
758
- });
759
- return coords;
760
- })();
761
- var DIACRITIC_SIBLINGS = {
762
- \u0131: "i",
763
- i: "\u0131",
764
- \u00F6: "o",
765
- o: "\xF6",
766
- \u00FC: "u",
767
- u: "\xFC",
768
- \u015F: "s",
769
- s: "\u015F",
770
- \u00E7: "c",
771
- c: "\xE7",
772
- \u011F: "g",
773
- g: "\u011F",
774
- \u00E2: "a",
775
- a: "\xE2"
776
- };
777
- var KEYBOARD_ROW_SUB_COST = 0.4;
778
- var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
779
- var DIACRITIC_SUB_COST = 0.3;
780
- var TRANSPOSITION_COST = 0.8;
781
- function keyboardSubCost(a, b) {
782
- if (a === b)
783
- return 0;
784
- if (DIACRITIC_SIBLINGS[a] === b)
785
- return DIACRITIC_SUB_COST;
786
- const pa = KEYBOARD_COORDS[a];
787
- const pb = KEYBOARD_COORDS[b];
788
- if (!pa || !pb)
789
- return 1;
790
- const dx = Math.abs(pa[0] - pb[0]);
791
- const dy = Math.abs(pa[1] - pb[1]);
792
- if (dy === 0 && dx <= 1 + 1e-9)
793
- return KEYBOARD_ROW_SUB_COST;
794
- if (dy === 1 && dx <= 1 + 1e-9)
795
- return KEYBOARD_DIAGONAL_SUB_COST;
796
- return 1;
797
- }
798
- var TDK = class {
799
- static BASE_URL = "https://sozluk.gov.tr";
800
- static AUDIO_API_HOST = "api.sozluk.gov.tr";
801
- static KUBBEALTI_HOST = "eski.lugatim.com";
802
- /**
803
- * `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
804
- * certificate during the TLS handshake, omitting the intermediates a
805
- * correctly configured server would include — a server-side misconfiguration,
806
- * not something we should paper over by disabling verification. These are
807
- * the two certificates the server *should* be sending (fetched from the
808
- * leaf's own Authority Information Access URLs), supplied here so Node can
809
- * still build a full, properly verified chain up to a root it already
810
- * trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
811
- * stops working and every Kubbealtı call fails closed to `null` — same
812
- * fail-closed contract as the rest of this file's fragile integrations.
813
- */
814
- static KUBBEALTI_EXTRA_CA = [
815
- `-----BEGIN CERTIFICATE-----
744
+
745
+ // src/data/kubbealti-ca.ts
746
+ var KUBBEALTI_EXTRA_CA = [
747
+ `-----BEGIN CERTIFICATE-----
816
748
  MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu
817
749
  MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe
818
750
  Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw
@@ -840,7 +772,7 @@ NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve
840
772
  86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy
841
773
  LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA==
842
774
  -----END CERTIFICATE-----`,
843
- `-----BEGIN CERTIFICATE-----
775
+ `-----BEGIN CERTIFICATE-----
844
776
  MIIF9DCCA9ygAwIBAgIRAPJLbRf52a18scn+p4eCaZ8wDQYJKoZIhvcNAQELBQAw
845
777
  TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
846
778
  cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjYwNTEzMDAwMDAw
@@ -874,7 +806,112 @@ RGMuHGnzS3hFIrRTfKxrzUZ9RzQWzEG3K6fJ3r2nqSltkeytis9DIBoFY9VmVyjL
874
806
  M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID
875
807
  yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
876
808
  -----END CERTIFICATE-----`
877
- ];
809
+ ];
810
+
811
+ // src/data/keyboard-layout.ts
812
+ var KEYBOARD_ROWS = [
813
+ ["qwertyu\u0131op\u011F\xFC", 0],
814
+ ["asdfghjkl\u015Fi", 0.5],
815
+ ["zxcvbnm\xF6\xE7", 1]
816
+ ];
817
+ var KEYBOARD_COORDS = (() => {
818
+ const coords = {};
819
+ KEYBOARD_ROWS.forEach(([keys, offset], row) => {
820
+ [...keys].forEach((key, col) => {
821
+ coords[key] = [col + offset, row];
822
+ });
823
+ });
824
+ return coords;
825
+ })();
826
+ var DIACRITIC_SIBLINGS = {
827
+ \u0131: "i",
828
+ i: "\u0131",
829
+ \u00F6: "o",
830
+ o: "\xF6",
831
+ \u00FC: "u",
832
+ u: "\xFC",
833
+ \u015F: "s",
834
+ s: "\u015F",
835
+ \u00E7: "c",
836
+ c: "\xE7",
837
+ \u011F: "g",
838
+ g: "\u011F",
839
+ \u00E2: "a",
840
+ a: "\xE2"
841
+ };
842
+ var KEYBOARD_ROW_SUB_COST = 0.4;
843
+ var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
844
+ var DIACRITIC_SUB_COST = 0.3;
845
+ var TRANSPOSITION_COST = 0.8;
846
+ function keyboardSubCost(a, b) {
847
+ if (a === b)
848
+ return 0;
849
+ if (DIACRITIC_SIBLINGS[a] === b)
850
+ return DIACRITIC_SUB_COST;
851
+ const pa = KEYBOARD_COORDS[a];
852
+ const pb = KEYBOARD_COORDS[b];
853
+ if (!pa || !pb)
854
+ return 1;
855
+ const dx = Math.abs(pa[0] - pb[0]);
856
+ const dy = Math.abs(pa[1] - pb[1]);
857
+ if (dy === 0 && dx <= 1 + 1e-9)
858
+ return KEYBOARD_ROW_SUB_COST;
859
+ if (dy === 1 && dx <= 1 + 1e-9)
860
+ return KEYBOARD_DIAGONAL_SUB_COST;
861
+ return 1;
862
+ }
863
+
864
+ // src/lib/edit-distance.ts
865
+ function damerauLevenshtein(a, b) {
866
+ const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
867
+ for (let i = 0; i <= a.length; i++)
868
+ dp[i][0] = i;
869
+ for (let j = 0; j <= b.length; j++)
870
+ dp[0][j] = j;
871
+ for (let i = 1; i <= a.length; i++) {
872
+ for (let j = 1; j <= b.length; j++) {
873
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
874
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
875
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
876
+ dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
877
+ }
878
+ }
879
+ }
880
+ return dp[a.length][b.length];
881
+ }
882
+ function keyboardAwareDistance(a, b) {
883
+ const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
884
+ for (let i = 0; i <= a.length; i++)
885
+ dp[i][0] = i;
886
+ for (let j = 0; j <= b.length; j++)
887
+ dp[0][j] = j;
888
+ for (let i = 1; i <= a.length; i++) {
889
+ for (let j = 1; j <= b.length; j++) {
890
+ const cost = keyboardSubCost(a[i - 1], b[j - 1]);
891
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
892
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
893
+ dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
894
+ }
895
+ }
896
+ }
897
+ return dp[a.length][b.length];
898
+ }
899
+
900
+ // src/lib/html.ts
901
+ function htmlToPlainText(html) {
902
+ return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;|&rsquo;/gi, "'").replace(/&amp;/gi, "&").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
903
+ }
904
+
905
+ // src/tdk.ts
906
+ import * as fs from "fs";
907
+ import * as path from "path";
908
+ import * as os from "os";
909
+ import * as https from "https";
910
+ import * as tls from "tls";
911
+ var TDK = class {
912
+ static BASE_URL = "https://sozluk.gov.tr";
913
+ static AUDIO_API_HOST = "api.sozluk.gov.tr";
914
+ static KUBBEALTI_HOST = "eski.lugatim.com";
878
915
  // Configuration
879
916
  static defaultTimeoutMs = 8e3;
880
917
  static defaultRetries = 1;
@@ -1421,12 +1458,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1421
1458
  continue;
1422
1459
  if (Math.abs(candidate.length - cleanWord.length) > 2)
1423
1460
  continue;
1424
- const rawDist = this.damerauLevenshtein(cleanWord, candidate);
1461
+ const rawDist = damerauLevenshtein(cleanWord, candidate);
1425
1462
  if (rawDist === 0 || rawDist > 2)
1426
1463
  continue;
1427
1464
  const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
1428
1465
  const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
1429
- const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
1466
+ const score = keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
1430
1467
  const better = !best || score < best.score - 1e-9 || Math.abs(score - best.score) < 1e-9 && firstMismatch < best.firstMismatch || Math.abs(score - best.score) < 1e-9 && firstMismatch === best.firstMismatch && lengthMismatch < best.lengthMismatch;
1431
1468
  if (better) {
1432
1469
  best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
@@ -1557,14 +1594,11 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1557
1594
  const contentEnd = html.indexOf("<footer", contentStart);
1558
1595
  if (contentEnd === -1)
1559
1596
  return null;
1560
- return this.htmlToPlainText(html.slice(contentStart, contentEnd));
1597
+ return htmlToPlainText(html.slice(contentStart, contentEnd));
1561
1598
  } catch {
1562
1599
  return null;
1563
1600
  }
1564
1601
  }
1565
- static htmlToPlainText(html) {
1566
- return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;|&rsquo;/gi, "'").replace(/&amp;/gi, "&").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
1567
- }
1568
1602
  /**
1569
1603
  * GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
1570
1604
  * supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
@@ -1578,7 +1612,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1578
1612
  hostname: this.KUBBEALTI_HOST,
1579
1613
  path: path2,
1580
1614
  method: "GET",
1581
- ca: [...tls.rootCertificates, ...this.KUBBEALTI_EXTRA_CA],
1615
+ ca: [...tls.rootCertificates, ...KUBBEALTI_EXTRA_CA],
1582
1616
  headers: {
1583
1617
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
1584
1618
  }
@@ -1670,7 +1704,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1670
1704
  const entries = await this.getKubbealti(word);
1671
1705
  if (!entries)
1672
1706
  return null;
1673
- return entries.map((e) => this.htmlToPlainText(e.anlam));
1707
+ return entries.map((e) => htmlToPlainText(e.anlam));
1674
1708
  }
1675
1709
  /**
1676
1710
  * Autocomplete suggestions from Kubbealtı Lugatı's own typeahead endpoint
@@ -1706,7 +1740,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1706
1740
  const match = html.match(/<meta name="description" content="([^"]*)"/);
1707
1741
  if (!match)
1708
1742
  return null;
1709
- const description = this.htmlToPlainText(match[1]);
1743
+ const description = htmlToPlainText(match[1]);
1710
1744
  if (description === "\xC7a\u011Fda\u015F T\xFCrk\xE7enin Etimolojisi")
1711
1745
  return null;
1712
1746
  return description;
@@ -1937,56 +1971,6 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1937
1971
  }
1938
1972
  return analyses;
1939
1973
  }
1940
- /**
1941
- * Damerau-Levenshtein edit-distance (optimal string alignment variant):
1942
- * like classic Levenshtein but also counts an adjacent-character
1943
- * transposition (e.g. "yanlız" -> "yalnız") as a single edit instead of
1944
- * two substitutions — a very common class of typo that plain Levenshtein
1945
- * otherwise misses.
1946
- */
1947
- static damerauLevenshtein(a, b) {
1948
- const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
1949
- for (let i = 0; i <= a.length; i++)
1950
- dp[i][0] = i;
1951
- for (let j = 0; j <= b.length; j++)
1952
- dp[0][j] = j;
1953
- for (let i = 1; i <= a.length; i++) {
1954
- for (let j = 1; j <= b.length; j++) {
1955
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
1956
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
1957
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
1958
- dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
1959
- }
1960
- }
1961
- }
1962
- return dp[a.length][b.length];
1963
- }
1964
- /**
1965
- * Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
1966
- * recurrence as {@link damerauLevenshtein}, but a substitution is charged by
1967
- * {@link keyboardSubCost} (a fraction of an edit when the two letters are
1968
- * adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
1969
- * transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
1970
- * still cost a full 1. Used only to *rank* spelling candidates; the plain
1971
- * integer distance still gates whether a suggestion is offered at all.
1972
- */
1973
- static keyboardAwareDistance(a, b) {
1974
- const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
1975
- for (let i = 0; i <= a.length; i++)
1976
- dp[i][0] = i;
1977
- for (let j = 0; j <= b.length; j++)
1978
- dp[0][j] = j;
1979
- for (let i = 1; i <= a.length; i++) {
1980
- for (let j = 1; j <= b.length; j++) {
1981
- const cost = keyboardSubCost(a[i - 1], b[j - 1]);
1982
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
1983
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
1984
- dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
1985
- }
1986
- }
1987
- }
1988
- return dp[a.length][b.length];
1989
- }
1990
1974
  /**
1991
1975
  * Fetches multiple words concurrently with a small delay to avoid rate limiting.
1992
1976
  */
@@ -2460,9 +2444,9 @@ export {
2460
2444
  TDKError,
2461
2445
  TDKValidationError,
2462
2446
  TDKNetworkError,
2447
+ TURKISH_SUFFIXES,
2463
2448
  TURKISH_VOWELS,
2464
2449
  isVowel,
2465
- TURKISH_SUFFIXES,
2466
2450
  restoreConsonantSoftening,
2467
2451
  restoreVowelDrop,
2468
2452
  restoreGemination,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TDK
3
- } from "./chunk-ZFWZXNGY.mjs";
3
+ } from "./chunk-YY4WA2AO.mjs";
4
4
 
5
5
  // src/mcp.ts
6
6
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";