tdk-api-wrapper 1.5.1 → 1.6.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 +1 -1
- package/dist/{chunk-NEC5MIQM.mjs → chunk-ZFWZXNGY.mjs} +81 -4
- package/dist/cli.js +81 -4
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +81 -4
- package/dist/index.mjs +1 -1
- package/package.json +23 -3
- package/.github/workflows/publish.yml +0 -41
- package/src/cli.ts +0 -535
- package/src/errors.ts +0 -38
- package/src/index.ts +0 -4
- package/src/morphology.ts +0 -333
- package/src/tdk.ts +0 -1962
- package/src/types.ts +0 -185
- package/test/grammar.test.js +0 -52
- package/test/morphology.test.js +0 -129
- package/test/proofread.test.js +0 -88
- package/test/tools.test.js +0 -51
- package/tsconfig.json +0 -11
package/README.md
CHANGED
|
@@ -90,7 +90,7 @@ Aşağıdaki metotlar `TDK` sınıfı üzerinden statik olarak veya `TDKClient`
|
|
|
90
90
|
- **`TDK.checkVowelHarmony(word)`**: Kelimenin büyük ünlü uyumuna uyup uymadığını (boolean) kontrol eder.
|
|
91
91
|
- **`TDK.checkLabialHarmony(word)`**: Kelimenin küçük ünlü uyumuna (düzlük-yuvarlaklık uyumu) uyup uymadığını (boolean) kontrol eder.
|
|
92
92
|
- **`TDK.getPartOfSpeech(word)`**: Kelimenin sözcük türünü (isim, sıfat, zarf vb.) döndürür.
|
|
93
|
-
- **`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 Damerau-Levenshtein edit-distance ile en yakın kelimeyi önerir (`yanlız` → `yalnız`).
|
|
93
|
+
- **`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
94
|
- **`TDK.getCompoundWords(word)`**: Aranan kelime ile oluşturulmuş birleşik kelimeleri (Örn: dolma kalem) listeler.
|
|
95
95
|
|
|
96
96
|
### 3. Morfoloji ve Kök Bulma (Morphology Engine)
|
|
@@ -744,6 +744,57 @@ var COMMON_MISSPELLINGS = {
|
|
|
744
744
|
babaanne: "babaanne"
|
|
745
745
|
};
|
|
746
746
|
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
|
+
}
|
|
747
798
|
var TDK = class {
|
|
748
799
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
749
800
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
@@ -1371,14 +1422,14 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1371
1422
|
if (Math.abs(candidate.length - cleanWord.length) > 2)
|
|
1372
1423
|
continue;
|
|
1373
1424
|
const rawDist = this.damerauLevenshtein(cleanWord, candidate);
|
|
1374
|
-
if (rawDist === 0)
|
|
1425
|
+
if (rawDist === 0 || rawDist > 2)
|
|
1375
1426
|
continue;
|
|
1376
1427
|
const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
|
|
1377
1428
|
const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
|
|
1378
|
-
const
|
|
1379
|
-
const better = !best ||
|
|
1429
|
+
const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
|
|
1430
|
+
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;
|
|
1380
1431
|
if (better) {
|
|
1381
|
-
best = { candidate,
|
|
1432
|
+
best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
|
|
1382
1433
|
}
|
|
1383
1434
|
}
|
|
1384
1435
|
if (best && best.rawDist <= 2 && (best.firstMismatch === 0 || best.rawDist <= 1)) {
|
|
@@ -1910,6 +1961,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1910
1961
|
}
|
|
1911
1962
|
return dp[a.length][b.length];
|
|
1912
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
|
+
}
|
|
1913
1990
|
/**
|
|
1914
1991
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
1915
1992
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -769,6 +769,57 @@ var COMMON_MISSPELLINGS = {
|
|
|
769
769
|
babaanne: "babaanne"
|
|
770
770
|
};
|
|
771
771
|
var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
|
|
772
|
+
var KEYBOARD_ROWS = [
|
|
773
|
+
["qwertyu\u0131op\u011F\xFC", 0],
|
|
774
|
+
["asdfghjkl\u015Fi", 0.5],
|
|
775
|
+
["zxcvbnm\xF6\xE7", 1]
|
|
776
|
+
];
|
|
777
|
+
var KEYBOARD_COORDS = (() => {
|
|
778
|
+
const coords = {};
|
|
779
|
+
KEYBOARD_ROWS.forEach(([keys, offset], row) => {
|
|
780
|
+
[...keys].forEach((key, col) => {
|
|
781
|
+
coords[key] = [col + offset, row];
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
return coords;
|
|
785
|
+
})();
|
|
786
|
+
var DIACRITIC_SIBLINGS = {
|
|
787
|
+
\u0131: "i",
|
|
788
|
+
i: "\u0131",
|
|
789
|
+
\u00F6: "o",
|
|
790
|
+
o: "\xF6",
|
|
791
|
+
\u00FC: "u",
|
|
792
|
+
u: "\xFC",
|
|
793
|
+
\u015F: "s",
|
|
794
|
+
s: "\u015F",
|
|
795
|
+
\u00E7: "c",
|
|
796
|
+
c: "\xE7",
|
|
797
|
+
\u011F: "g",
|
|
798
|
+
g: "\u011F",
|
|
799
|
+
\u00E2: "a",
|
|
800
|
+
a: "\xE2"
|
|
801
|
+
};
|
|
802
|
+
var KEYBOARD_ROW_SUB_COST = 0.4;
|
|
803
|
+
var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
|
|
804
|
+
var DIACRITIC_SUB_COST = 0.3;
|
|
805
|
+
var TRANSPOSITION_COST = 0.8;
|
|
806
|
+
function keyboardSubCost(a, b) {
|
|
807
|
+
if (a === b)
|
|
808
|
+
return 0;
|
|
809
|
+
if (DIACRITIC_SIBLINGS[a] === b)
|
|
810
|
+
return DIACRITIC_SUB_COST;
|
|
811
|
+
const pa = KEYBOARD_COORDS[a];
|
|
812
|
+
const pb = KEYBOARD_COORDS[b];
|
|
813
|
+
if (!pa || !pb)
|
|
814
|
+
return 1;
|
|
815
|
+
const dx = Math.abs(pa[0] - pb[0]);
|
|
816
|
+
const dy = Math.abs(pa[1] - pb[1]);
|
|
817
|
+
if (dy === 0 && dx <= 1 + 1e-9)
|
|
818
|
+
return KEYBOARD_ROW_SUB_COST;
|
|
819
|
+
if (dy === 1 && dx <= 1 + 1e-9)
|
|
820
|
+
return KEYBOARD_DIAGONAL_SUB_COST;
|
|
821
|
+
return 1;
|
|
822
|
+
}
|
|
772
823
|
var TDK = class {
|
|
773
824
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
774
825
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
@@ -1396,14 +1447,14 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1396
1447
|
if (Math.abs(candidate.length - cleanWord.length) > 2)
|
|
1397
1448
|
continue;
|
|
1398
1449
|
const rawDist = this.damerauLevenshtein(cleanWord, candidate);
|
|
1399
|
-
if (rawDist === 0)
|
|
1450
|
+
if (rawDist === 0 || rawDist > 2)
|
|
1400
1451
|
continue;
|
|
1401
1452
|
const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
|
|
1402
1453
|
const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
|
|
1403
|
-
const
|
|
1404
|
-
const better = !best ||
|
|
1454
|
+
const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
|
|
1455
|
+
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;
|
|
1405
1456
|
if (better) {
|
|
1406
|
-
best = { candidate,
|
|
1457
|
+
best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
|
|
1407
1458
|
}
|
|
1408
1459
|
}
|
|
1409
1460
|
if (best && best.rawDist <= 2 && (best.firstMismatch === 0 || best.rawDist <= 1)) {
|
|
@@ -1935,6 +1986,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1935
1986
|
}
|
|
1936
1987
|
return dp[a.length][b.length];
|
|
1937
1988
|
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
1991
|
+
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
1992
|
+
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
1993
|
+
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
1994
|
+
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
1995
|
+
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
1996
|
+
* integer distance still gates whether a suggestion is offered at all.
|
|
1997
|
+
*/
|
|
1998
|
+
static keyboardAwareDistance(a, b) {
|
|
1999
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
2000
|
+
for (let i = 0; i <= a.length; i++)
|
|
2001
|
+
dp[i][0] = i;
|
|
2002
|
+
for (let j = 0; j <= b.length; j++)
|
|
2003
|
+
dp[0][j] = j;
|
|
2004
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2005
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2006
|
+
const cost = keyboardSubCost(a[i - 1], b[j - 1]);
|
|
2007
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
2008
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
2009
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
return dp[a.length][b.length];
|
|
2014
|
+
}
|
|
1938
2015
|
/**
|
|
1939
2016
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
1940
2017
|
*/
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -508,6 +508,16 @@ declare class TDK {
|
|
|
508
508
|
* otherwise misses.
|
|
509
509
|
*/
|
|
510
510
|
private static damerauLevenshtein;
|
|
511
|
+
/**
|
|
512
|
+
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
513
|
+
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
514
|
+
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
515
|
+
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
516
|
+
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
517
|
+
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
518
|
+
* integer distance still gates whether a suggestion is offered at all.
|
|
519
|
+
*/
|
|
520
|
+
private static keyboardAwareDistance;
|
|
511
521
|
/**
|
|
512
522
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
513
523
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -508,6 +508,16 @@ declare class TDK {
|
|
|
508
508
|
* otherwise misses.
|
|
509
509
|
*/
|
|
510
510
|
private static damerauLevenshtein;
|
|
511
|
+
/**
|
|
512
|
+
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
513
|
+
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
514
|
+
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
515
|
+
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
516
|
+
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
517
|
+
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
518
|
+
* integer distance still gates whether a suggestion is offered at all.
|
|
519
|
+
*/
|
|
520
|
+
private static keyboardAwareDistance;
|
|
511
521
|
/**
|
|
512
522
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
513
523
|
*/
|
package/dist/index.js
CHANGED
|
@@ -793,6 +793,57 @@ var COMMON_MISSPELLINGS = {
|
|
|
793
793
|
babaanne: "babaanne"
|
|
794
794
|
};
|
|
795
795
|
var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
|
|
796
|
+
var KEYBOARD_ROWS = [
|
|
797
|
+
["qwertyu\u0131op\u011F\xFC", 0],
|
|
798
|
+
["asdfghjkl\u015Fi", 0.5],
|
|
799
|
+
["zxcvbnm\xF6\xE7", 1]
|
|
800
|
+
];
|
|
801
|
+
var KEYBOARD_COORDS = (() => {
|
|
802
|
+
const coords = {};
|
|
803
|
+
KEYBOARD_ROWS.forEach(([keys, offset], row) => {
|
|
804
|
+
[...keys].forEach((key, col) => {
|
|
805
|
+
coords[key] = [col + offset, row];
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
return coords;
|
|
809
|
+
})();
|
|
810
|
+
var DIACRITIC_SIBLINGS = {
|
|
811
|
+
\u0131: "i",
|
|
812
|
+
i: "\u0131",
|
|
813
|
+
\u00F6: "o",
|
|
814
|
+
o: "\xF6",
|
|
815
|
+
\u00FC: "u",
|
|
816
|
+
u: "\xFC",
|
|
817
|
+
\u015F: "s",
|
|
818
|
+
s: "\u015F",
|
|
819
|
+
\u00E7: "c",
|
|
820
|
+
c: "\xE7",
|
|
821
|
+
\u011F: "g",
|
|
822
|
+
g: "\u011F",
|
|
823
|
+
\u00E2: "a",
|
|
824
|
+
a: "\xE2"
|
|
825
|
+
};
|
|
826
|
+
var KEYBOARD_ROW_SUB_COST = 0.4;
|
|
827
|
+
var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
|
|
828
|
+
var DIACRITIC_SUB_COST = 0.3;
|
|
829
|
+
var TRANSPOSITION_COST = 0.8;
|
|
830
|
+
function keyboardSubCost(a, b) {
|
|
831
|
+
if (a === b)
|
|
832
|
+
return 0;
|
|
833
|
+
if (DIACRITIC_SIBLINGS[a] === b)
|
|
834
|
+
return DIACRITIC_SUB_COST;
|
|
835
|
+
const pa = KEYBOARD_COORDS[a];
|
|
836
|
+
const pb = KEYBOARD_COORDS[b];
|
|
837
|
+
if (!pa || !pb)
|
|
838
|
+
return 1;
|
|
839
|
+
const dx = Math.abs(pa[0] - pb[0]);
|
|
840
|
+
const dy = Math.abs(pa[1] - pb[1]);
|
|
841
|
+
if (dy === 0 && dx <= 1 + 1e-9)
|
|
842
|
+
return KEYBOARD_ROW_SUB_COST;
|
|
843
|
+
if (dy === 1 && dx <= 1 + 1e-9)
|
|
844
|
+
return KEYBOARD_DIAGONAL_SUB_COST;
|
|
845
|
+
return 1;
|
|
846
|
+
}
|
|
796
847
|
var TDK = class {
|
|
797
848
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
798
849
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
@@ -1420,14 +1471,14 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1420
1471
|
if (Math.abs(candidate.length - cleanWord.length) > 2)
|
|
1421
1472
|
continue;
|
|
1422
1473
|
const rawDist = this.damerauLevenshtein(cleanWord, candidate);
|
|
1423
|
-
if (rawDist === 0)
|
|
1474
|
+
if (rawDist === 0 || rawDist > 2)
|
|
1424
1475
|
continue;
|
|
1425
1476
|
const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
|
|
1426
1477
|
const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
|
|
1427
|
-
const
|
|
1428
|
-
const better = !best ||
|
|
1478
|
+
const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
|
|
1479
|
+
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;
|
|
1429
1480
|
if (better) {
|
|
1430
|
-
best = { candidate,
|
|
1481
|
+
best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
|
|
1431
1482
|
}
|
|
1432
1483
|
}
|
|
1433
1484
|
if (best && best.rawDist <= 2 && (best.firstMismatch === 0 || best.rawDist <= 1)) {
|
|
@@ -1959,6 +2010,32 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1959
2010
|
}
|
|
1960
2011
|
return dp[a.length][b.length];
|
|
1961
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
2015
|
+
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
2016
|
+
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
2017
|
+
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
2018
|
+
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
2019
|
+
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
2020
|
+
* integer distance still gates whether a suggestion is offered at all.
|
|
2021
|
+
*/
|
|
2022
|
+
static keyboardAwareDistance(a, b) {
|
|
2023
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
2024
|
+
for (let i = 0; i <= a.length; i++)
|
|
2025
|
+
dp[i][0] = i;
|
|
2026
|
+
for (let j = 0; j <= b.length; j++)
|
|
2027
|
+
dp[0][j] = j;
|
|
2028
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2029
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2030
|
+
const cost = keyboardSubCost(a[i - 1], b[j - 1]);
|
|
2031
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
2032
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
2033
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
return dp[a.length][b.length];
|
|
2038
|
+
}
|
|
1962
2039
|
/**
|
|
1963
2040
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
1964
2041
|
*/
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tdk-api-wrapper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "TDK (Türk Dil Kurumu) unofficial live data API wrapper for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -25,10 +25,30 @@
|
|
|
25
25
|
"api",
|
|
26
26
|
"turkish",
|
|
27
27
|
"dictionary",
|
|
28
|
-
"turkce"
|
|
28
|
+
"turkce",
|
|
29
|
+
"sozluk.gov.tr",
|
|
30
|
+
"spell-check",
|
|
31
|
+
"morphology",
|
|
32
|
+
"kubbealti",
|
|
33
|
+
"nisanyan",
|
|
34
|
+
"wiktionary"
|
|
29
35
|
],
|
|
30
|
-
"author": "",
|
|
36
|
+
"author": "cemyilmazzzz122 <cemyleo12@icloud.com> (https://github.com/cemyilmazzzz122)",
|
|
31
37
|
"license": "MIT",
|
|
38
|
+
"homepage": "https://github.com/cemyilmazzzz122/tdk-api#readme",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/cemyilmazzzz122/tdk-api.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/cemyilmazzzz122/tdk-api/issues"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist"
|
|
51
|
+
],
|
|
32
52
|
"devDependencies": {
|
|
33
53
|
"@types/node": "^22.0.0",
|
|
34
54
|
"tsup": "^8.0.2",
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
name: Publish to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
publish:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
steps:
|
|
11
|
-
- uses: actions/checkout@v4
|
|
12
|
-
|
|
13
|
-
- uses: actions/setup-node@v4
|
|
14
|
-
with:
|
|
15
|
-
node-version: '20'
|
|
16
|
-
registry-url: 'https://registry.npmjs.org'
|
|
17
|
-
|
|
18
|
-
- run: npm ci
|
|
19
|
-
|
|
20
|
-
- name: Check if version changed
|
|
21
|
-
id: version-check
|
|
22
|
-
run: |
|
|
23
|
-
LOCAL_VERSION=$(node -p "require('./package.json').version")
|
|
24
|
-
PKG_NAME=$(node -p "require('./package.json').name")
|
|
25
|
-
PUBLISHED_VERSION=$(npm view "$PKG_NAME" version 2>/dev/null || echo "0.0.0")
|
|
26
|
-
echo "Local: $LOCAL_VERSION / Published: $PUBLISHED_VERSION"
|
|
27
|
-
if [ "$LOCAL_VERSION" != "$PUBLISHED_VERSION" ]; then
|
|
28
|
-
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
29
|
-
else
|
|
30
|
-
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
- name: Build
|
|
34
|
-
if: steps.version-check.outputs.changed == 'true'
|
|
35
|
-
run: npm run build
|
|
36
|
-
|
|
37
|
-
- name: Publish
|
|
38
|
-
if: steps.version-check.outputs.changed == 'true'
|
|
39
|
-
run: npm publish
|
|
40
|
-
env:
|
|
41
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|