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/LICENSE +674 -21
- package/README.md +2 -2
- package/dist/{chunk-ZFWZXNGY.mjs → chunk-YY4WA2AO.mjs} +126 -142
- package/dist/{chunk-MELLECWT.mjs → chunk-ZG7YRT7V.mjs} +1 -1
- package/dist/cli.js +332 -300
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +8 -37
- package/dist/index.d.ts +8 -37
- package/dist/index.js +125 -141
- package/dist/index.mjs +2 -2
- package/dist/{mcp-NAXIX4TQ.mjs → mcp-GBTXQJLW.mjs} +2 -2
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
TDK
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YY4WA2AO.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
var rawArgs = process.argv.slice(2);
|
|
@@ -138,7 +138,7 @@ async function startRepl() {
|
|
|
138
138
|
async function run() {
|
|
139
139
|
const binaryName = (process.argv[1] || "").toLowerCase();
|
|
140
140
|
if (command === "mcp" || rawArgs.includes("--mcp") || binaryName.includes("tdk-mcp")) {
|
|
141
|
-
const { runMcpServer } = await import("./mcp-
|
|
141
|
+
const { runMcpServer } = await import("./mcp-GBTXQJLW.mjs");
|
|
142
142
|
await runMcpServer();
|
|
143
143
|
return;
|
|
144
144
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -188,19 +188,6 @@ declare class TDK {
|
|
|
188
188
|
private static readonly BASE_URL;
|
|
189
189
|
private static readonly AUDIO_API_HOST;
|
|
190
190
|
private static readonly KUBBEALTI_HOST;
|
|
191
|
-
/**
|
|
192
|
-
* `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
|
|
193
|
-
* certificate during the TLS handshake, omitting the intermediates a
|
|
194
|
-
* correctly configured server would include — a server-side misconfiguration,
|
|
195
|
-
* not something we should paper over by disabling verification. These are
|
|
196
|
-
* the two certificates the server *should* be sending (fetched from the
|
|
197
|
-
* leaf's own Authority Information Access URLs), supplied here so Node can
|
|
198
|
-
* still build a full, properly verified chain up to a root it already
|
|
199
|
-
* trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
|
|
200
|
-
* stops working and every Kubbealtı call fails closed to `null` — same
|
|
201
|
-
* fail-closed contract as the rest of this file's fragile integrations.
|
|
202
|
-
*/
|
|
203
|
-
private static readonly KUBBEALTI_EXTRA_CA;
|
|
204
191
|
private static defaultTimeoutMs;
|
|
205
192
|
private static defaultRetries;
|
|
206
193
|
private static maxCacheSize;
|
|
@@ -399,7 +386,6 @@ declare class TDK {
|
|
|
399
386
|
* spans) — cutting there avoids that trailing cruft.
|
|
400
387
|
*/
|
|
401
388
|
private static fetchRuleText;
|
|
402
|
-
private static htmlToPlainText;
|
|
403
389
|
/**
|
|
404
390
|
* GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
|
|
405
391
|
* supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
|
|
@@ -502,24 +488,6 @@ declare class TDK {
|
|
|
502
488
|
* headword. This is an inherent limitation of the data source, not a bug.
|
|
503
489
|
*/
|
|
504
490
|
static analyzeText(text: string): Promise<WordAnalysis[]>;
|
|
505
|
-
/**
|
|
506
|
-
* Damerau-Levenshtein edit-distance (optimal string alignment variant):
|
|
507
|
-
* like classic Levenshtein but also counts an adjacent-character
|
|
508
|
-
* transposition (e.g. "yanlız" -> "yalnız") as a single edit instead of
|
|
509
|
-
* two substitutions — a very common class of typo that plain Levenshtein
|
|
510
|
-
* otherwise misses.
|
|
511
|
-
*/
|
|
512
|
-
private static damerauLevenshtein;
|
|
513
|
-
/**
|
|
514
|
-
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
515
|
-
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
516
|
-
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
517
|
-
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
518
|
-
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
519
|
-
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
520
|
-
* integer distance still gates whether a suggestion is offered at all.
|
|
521
|
-
*/
|
|
522
|
-
private static keyboardAwareDistance;
|
|
523
491
|
/**
|
|
524
492
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
525
493
|
*/
|
|
@@ -623,6 +591,13 @@ declare class TDKNetworkError extends TDKError {
|
|
|
623
591
|
});
|
|
624
592
|
}
|
|
625
593
|
|
|
594
|
+
/**
|
|
595
|
+
* Turkish suffix catalogue used by the morphology engine's BFS stem-stripper
|
|
596
|
+
* (see `morphology.ts`). Ordered strictly by descending length so that
|
|
597
|
+
* longer composite suffixes match before their individual subcomponents.
|
|
598
|
+
*/
|
|
599
|
+
declare const TURKISH_SUFFIXES: readonly string[];
|
|
600
|
+
|
|
626
601
|
/**
|
|
627
602
|
* Turkish Morphology Engine & Stem Candidate Generator.
|
|
628
603
|
*
|
|
@@ -633,13 +608,9 @@ declare class TDKNetworkError extends TDKError {
|
|
|
633
608
|
* 4. Infinitive restoration (-mek / -mak for verbal stems)
|
|
634
609
|
* 5. Apostrophe stripping for proper nouns (İstanbul'da -> İstanbul)
|
|
635
610
|
*/
|
|
611
|
+
|
|
636
612
|
declare const TURKISH_VOWELS = "ae\u0131io\u00F6u\u00FC";
|
|
637
613
|
declare function isVowel(ch: string): boolean;
|
|
638
|
-
/**
|
|
639
|
-
* Turkish suffixes ordered strictly by descending length so that longer
|
|
640
|
-
* composite suffixes match before their individual subcomponents.
|
|
641
|
-
*/
|
|
642
|
-
declare const TURKISH_SUFFIXES: readonly string[];
|
|
643
614
|
/**
|
|
644
615
|
* Reverses Turkish consonant softening (ünsüz yumuşaması):
|
|
645
616
|
* When a root ends with p, ç, t, k, it softens to b, c, d, ğ, g before a vowel.
|
package/dist/index.d.ts
CHANGED
|
@@ -188,19 +188,6 @@ declare class TDK {
|
|
|
188
188
|
private static readonly BASE_URL;
|
|
189
189
|
private static readonly AUDIO_API_HOST;
|
|
190
190
|
private static readonly KUBBEALTI_HOST;
|
|
191
|
-
/**
|
|
192
|
-
* `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
|
|
193
|
-
* certificate during the TLS handshake, omitting the intermediates a
|
|
194
|
-
* correctly configured server would include — a server-side misconfiguration,
|
|
195
|
-
* not something we should paper over by disabling verification. These are
|
|
196
|
-
* the two certificates the server *should* be sending (fetched from the
|
|
197
|
-
* leaf's own Authority Information Access URLs), supplied here so Node can
|
|
198
|
-
* still build a full, properly verified chain up to a root it already
|
|
199
|
-
* trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
|
|
200
|
-
* stops working and every Kubbealtı call fails closed to `null` — same
|
|
201
|
-
* fail-closed contract as the rest of this file's fragile integrations.
|
|
202
|
-
*/
|
|
203
|
-
private static readonly KUBBEALTI_EXTRA_CA;
|
|
204
191
|
private static defaultTimeoutMs;
|
|
205
192
|
private static defaultRetries;
|
|
206
193
|
private static maxCacheSize;
|
|
@@ -399,7 +386,6 @@ declare class TDK {
|
|
|
399
386
|
* spans) — cutting there avoids that trailing cruft.
|
|
400
387
|
*/
|
|
401
388
|
private static fetchRuleText;
|
|
402
|
-
private static htmlToPlainText;
|
|
403
389
|
/**
|
|
404
390
|
* GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
|
|
405
391
|
* supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
|
|
@@ -502,24 +488,6 @@ declare class TDK {
|
|
|
502
488
|
* headword. This is an inherent limitation of the data source, not a bug.
|
|
503
489
|
*/
|
|
504
490
|
static analyzeText(text: string): Promise<WordAnalysis[]>;
|
|
505
|
-
/**
|
|
506
|
-
* Damerau-Levenshtein edit-distance (optimal string alignment variant):
|
|
507
|
-
* like classic Levenshtein but also counts an adjacent-character
|
|
508
|
-
* transposition (e.g. "yanlız" -> "yalnız") as a single edit instead of
|
|
509
|
-
* two substitutions — a very common class of typo that plain Levenshtein
|
|
510
|
-
* otherwise misses.
|
|
511
|
-
*/
|
|
512
|
-
private static damerauLevenshtein;
|
|
513
|
-
/**
|
|
514
|
-
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
515
|
-
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
516
|
-
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
517
|
-
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
518
|
-
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
519
|
-
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
520
|
-
* integer distance still gates whether a suggestion is offered at all.
|
|
521
|
-
*/
|
|
522
|
-
private static keyboardAwareDistance;
|
|
523
491
|
/**
|
|
524
492
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
525
493
|
*/
|
|
@@ -623,6 +591,13 @@ declare class TDKNetworkError extends TDKError {
|
|
|
623
591
|
});
|
|
624
592
|
}
|
|
625
593
|
|
|
594
|
+
/**
|
|
595
|
+
* Turkish suffix catalogue used by the morphology engine's BFS stem-stripper
|
|
596
|
+
* (see `morphology.ts`). Ordered strictly by descending length so that
|
|
597
|
+
* longer composite suffixes match before their individual subcomponents.
|
|
598
|
+
*/
|
|
599
|
+
declare const TURKISH_SUFFIXES: readonly string[];
|
|
600
|
+
|
|
626
601
|
/**
|
|
627
602
|
* Turkish Morphology Engine & Stem Candidate Generator.
|
|
628
603
|
*
|
|
@@ -633,13 +608,9 @@ declare class TDKNetworkError extends TDKError {
|
|
|
633
608
|
* 4. Infinitive restoration (-mek / -mak for verbal stems)
|
|
634
609
|
* 5. Apostrophe stripping for proper nouns (İstanbul'da -> İstanbul)
|
|
635
610
|
*/
|
|
611
|
+
|
|
636
612
|
declare const TURKISH_VOWELS = "ae\u0131io\u00F6u\u00FC";
|
|
637
613
|
declare function isVowel(ch: string): boolean;
|
|
638
|
-
/**
|
|
639
|
-
* Turkish suffixes ordered strictly by descending length so that longer
|
|
640
|
-
* composite suffixes match before their individual subcomponents.
|
|
641
|
-
*/
|
|
642
|
-
declare const TURKISH_SUFFIXES: readonly string[];
|
|
643
614
|
/**
|
|
644
615
|
* Reverses Turkish consonant softening (ünsüz yumuşaması):
|
|
645
616
|
* When a root ends with p, ç, t, k, it softens to b, c, d, ğ, g before a vowel.
|
package/dist/index.js
CHANGED
|
@@ -76,11 +76,7 @@ var TDKNetworkError = class extends TDKError {
|
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
// src/
|
|
80
|
-
var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
|
|
81
|
-
function isVowel(ch) {
|
|
82
|
-
return TURKISH_VOWELS.includes(ch);
|
|
83
|
-
}
|
|
79
|
+
// src/data/suffixes.ts
|
|
84
80
|
var TURKISH_SUFFIXES = [
|
|
85
81
|
// 9-letter composite suffixes
|
|
86
82
|
"lerimizden",
|
|
@@ -483,6 +479,12 @@ var TURKISH_SUFFIXES = [
|
|
|
483
479
|
"u",
|
|
484
480
|
"\xFC"
|
|
485
481
|
];
|
|
482
|
+
|
|
483
|
+
// src/morphology.ts
|
|
484
|
+
var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
|
|
485
|
+
function isVowel(ch) {
|
|
486
|
+
return TURKISH_VOWELS.includes(ch);
|
|
487
|
+
}
|
|
486
488
|
function restoreConsonantSoftening(stem) {
|
|
487
489
|
if (stem.length < 2)
|
|
488
490
|
return [];
|
|
@@ -636,12 +638,7 @@ function getStemCandidates(word, minStemLength = 2, maxDepth = 4) {
|
|
|
636
638
|
return [...new Set(candidatesWithWeight.map((c) => c.candidate))];
|
|
637
639
|
}
|
|
638
640
|
|
|
639
|
-
// src/
|
|
640
|
-
var fs = __toESM(require("fs"));
|
|
641
|
-
var path = __toESM(require("path"));
|
|
642
|
-
var os = __toESM(require("os"));
|
|
643
|
-
var https = __toESM(require("https"));
|
|
644
|
-
var tls = __toESM(require("tls"));
|
|
641
|
+
// src/data/misspellings.ts
|
|
645
642
|
var COMMON_MISSPELLINGS = {
|
|
646
643
|
// -şey ile biten ve ayrı yazılması zorunlu söz öbekleri
|
|
647
644
|
her\u015Fey: "her \u015Fey",
|
|
@@ -795,75 +792,10 @@ var COMMON_MISSPELLINGS = {
|
|
|
795
792
|
babaanne: "babaanne"
|
|
796
793
|
};
|
|
797
794
|
var SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
];
|
|
803
|
-
var KEYBOARD_COORDS = (() => {
|
|
804
|
-
const coords = {};
|
|
805
|
-
KEYBOARD_ROWS.forEach(([keys, offset], row) => {
|
|
806
|
-
[...keys].forEach((key, col) => {
|
|
807
|
-
coords[key] = [col + offset, row];
|
|
808
|
-
});
|
|
809
|
-
});
|
|
810
|
-
return coords;
|
|
811
|
-
})();
|
|
812
|
-
var DIACRITIC_SIBLINGS = {
|
|
813
|
-
\u0131: "i",
|
|
814
|
-
i: "\u0131",
|
|
815
|
-
\u00F6: "o",
|
|
816
|
-
o: "\xF6",
|
|
817
|
-
\u00FC: "u",
|
|
818
|
-
u: "\xFC",
|
|
819
|
-
\u015F: "s",
|
|
820
|
-
s: "\u015F",
|
|
821
|
-
\u00E7: "c",
|
|
822
|
-
c: "\xE7",
|
|
823
|
-
\u011F: "g",
|
|
824
|
-
g: "\u011F",
|
|
825
|
-
\u00E2: "a",
|
|
826
|
-
a: "\xE2"
|
|
827
|
-
};
|
|
828
|
-
var KEYBOARD_ROW_SUB_COST = 0.4;
|
|
829
|
-
var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
|
|
830
|
-
var DIACRITIC_SUB_COST = 0.3;
|
|
831
|
-
var TRANSPOSITION_COST = 0.8;
|
|
832
|
-
function keyboardSubCost(a, b) {
|
|
833
|
-
if (a === b)
|
|
834
|
-
return 0;
|
|
835
|
-
if (DIACRITIC_SIBLINGS[a] === b)
|
|
836
|
-
return DIACRITIC_SUB_COST;
|
|
837
|
-
const pa = KEYBOARD_COORDS[a];
|
|
838
|
-
const pb = KEYBOARD_COORDS[b];
|
|
839
|
-
if (!pa || !pb)
|
|
840
|
-
return 1;
|
|
841
|
-
const dx = Math.abs(pa[0] - pb[0]);
|
|
842
|
-
const dy = Math.abs(pa[1] - pb[1]);
|
|
843
|
-
if (dy === 0 && dx <= 1 + 1e-9)
|
|
844
|
-
return KEYBOARD_ROW_SUB_COST;
|
|
845
|
-
if (dy === 1 && dx <= 1 + 1e-9)
|
|
846
|
-
return KEYBOARD_DIAGONAL_SUB_COST;
|
|
847
|
-
return 1;
|
|
848
|
-
}
|
|
849
|
-
var TDK = class {
|
|
850
|
-
static BASE_URL = "https://sozluk.gov.tr";
|
|
851
|
-
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
852
|
-
static KUBBEALTI_HOST = "eski.lugatim.com";
|
|
853
|
-
/**
|
|
854
|
-
* `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
|
|
855
|
-
* certificate during the TLS handshake, omitting the intermediates a
|
|
856
|
-
* correctly configured server would include — a server-side misconfiguration,
|
|
857
|
-
* not something we should paper over by disabling verification. These are
|
|
858
|
-
* the two certificates the server *should* be sending (fetched from the
|
|
859
|
-
* leaf's own Authority Information Access URLs), supplied here so Node can
|
|
860
|
-
* still build a full, properly verified chain up to a root it already
|
|
861
|
-
* trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
|
|
862
|
-
* stops working and every Kubbealtı call fails closed to `null` — same
|
|
863
|
-
* fail-closed contract as the rest of this file's fragile integrations.
|
|
864
|
-
*/
|
|
865
|
-
static KUBBEALTI_EXTRA_CA = [
|
|
866
|
-
`-----BEGIN CERTIFICATE-----
|
|
795
|
+
|
|
796
|
+
// src/data/kubbealti-ca.ts
|
|
797
|
+
var KUBBEALTI_EXTRA_CA = [
|
|
798
|
+
`-----BEGIN CERTIFICATE-----
|
|
867
799
|
MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu
|
|
868
800
|
MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe
|
|
869
801
|
Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw
|
|
@@ -891,7 +823,7 @@ NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve
|
|
|
891
823
|
86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy
|
|
892
824
|
LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA==
|
|
893
825
|
-----END CERTIFICATE-----`,
|
|
894
|
-
|
|
826
|
+
`-----BEGIN CERTIFICATE-----
|
|
895
827
|
MIIF9DCCA9ygAwIBAgIRAPJLbRf52a18scn+p4eCaZ8wDQYJKoZIhvcNAQELBQAw
|
|
896
828
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
|
897
829
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjYwNTEzMDAwMDAw
|
|
@@ -925,7 +857,112 @@ RGMuHGnzS3hFIrRTfKxrzUZ9RzQWzEG3K6fJ3r2nqSltkeytis9DIBoFY9VmVyjL
|
|
|
925
857
|
M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID
|
|
926
858
|
yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
927
859
|
-----END CERTIFICATE-----`
|
|
928
|
-
|
|
860
|
+
];
|
|
861
|
+
|
|
862
|
+
// src/data/keyboard-layout.ts
|
|
863
|
+
var KEYBOARD_ROWS = [
|
|
864
|
+
["qwertyu\u0131op\u011F\xFC", 0],
|
|
865
|
+
["asdfghjkl\u015Fi", 0.5],
|
|
866
|
+
["zxcvbnm\xF6\xE7", 1]
|
|
867
|
+
];
|
|
868
|
+
var KEYBOARD_COORDS = (() => {
|
|
869
|
+
const coords = {};
|
|
870
|
+
KEYBOARD_ROWS.forEach(([keys, offset], row) => {
|
|
871
|
+
[...keys].forEach((key, col) => {
|
|
872
|
+
coords[key] = [col + offset, row];
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
return coords;
|
|
876
|
+
})();
|
|
877
|
+
var DIACRITIC_SIBLINGS = {
|
|
878
|
+
\u0131: "i",
|
|
879
|
+
i: "\u0131",
|
|
880
|
+
\u00F6: "o",
|
|
881
|
+
o: "\xF6",
|
|
882
|
+
\u00FC: "u",
|
|
883
|
+
u: "\xFC",
|
|
884
|
+
\u015F: "s",
|
|
885
|
+
s: "\u015F",
|
|
886
|
+
\u00E7: "c",
|
|
887
|
+
c: "\xE7",
|
|
888
|
+
\u011F: "g",
|
|
889
|
+
g: "\u011F",
|
|
890
|
+
\u00E2: "a",
|
|
891
|
+
a: "\xE2"
|
|
892
|
+
};
|
|
893
|
+
var KEYBOARD_ROW_SUB_COST = 0.4;
|
|
894
|
+
var KEYBOARD_DIAGONAL_SUB_COST = 0.55;
|
|
895
|
+
var DIACRITIC_SUB_COST = 0.3;
|
|
896
|
+
var TRANSPOSITION_COST = 0.8;
|
|
897
|
+
function keyboardSubCost(a, b) {
|
|
898
|
+
if (a === b)
|
|
899
|
+
return 0;
|
|
900
|
+
if (DIACRITIC_SIBLINGS[a] === b)
|
|
901
|
+
return DIACRITIC_SUB_COST;
|
|
902
|
+
const pa = KEYBOARD_COORDS[a];
|
|
903
|
+
const pb = KEYBOARD_COORDS[b];
|
|
904
|
+
if (!pa || !pb)
|
|
905
|
+
return 1;
|
|
906
|
+
const dx = Math.abs(pa[0] - pb[0]);
|
|
907
|
+
const dy = Math.abs(pa[1] - pb[1]);
|
|
908
|
+
if (dy === 0 && dx <= 1 + 1e-9)
|
|
909
|
+
return KEYBOARD_ROW_SUB_COST;
|
|
910
|
+
if (dy === 1 && dx <= 1 + 1e-9)
|
|
911
|
+
return KEYBOARD_DIAGONAL_SUB_COST;
|
|
912
|
+
return 1;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/lib/edit-distance.ts
|
|
916
|
+
function damerauLevenshtein(a, b) {
|
|
917
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
918
|
+
for (let i = 0; i <= a.length; i++)
|
|
919
|
+
dp[i][0] = i;
|
|
920
|
+
for (let j = 0; j <= b.length; j++)
|
|
921
|
+
dp[0][j] = j;
|
|
922
|
+
for (let i = 1; i <= a.length; i++) {
|
|
923
|
+
for (let j = 1; j <= b.length; j++) {
|
|
924
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
925
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
926
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
927
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return dp[a.length][b.length];
|
|
932
|
+
}
|
|
933
|
+
function keyboardAwareDistance(a, b) {
|
|
934
|
+
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
935
|
+
for (let i = 0; i <= a.length; i++)
|
|
936
|
+
dp[i][0] = i;
|
|
937
|
+
for (let j = 0; j <= b.length; j++)
|
|
938
|
+
dp[0][j] = j;
|
|
939
|
+
for (let i = 1; i <= a.length; i++) {
|
|
940
|
+
for (let j = 1; j <= b.length; j++) {
|
|
941
|
+
const cost = keyboardSubCost(a[i - 1], b[j - 1]);
|
|
942
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
943
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
944
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return dp[a.length][b.length];
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/lib/html.ts
|
|
952
|
+
function htmlToPlainText(html) {
|
|
953
|
+
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'|’/gi, "'").replace(/&/gi, "&").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/tdk.ts
|
|
957
|
+
var fs = __toESM(require("fs"));
|
|
958
|
+
var path = __toESM(require("path"));
|
|
959
|
+
var os = __toESM(require("os"));
|
|
960
|
+
var https = __toESM(require("https"));
|
|
961
|
+
var tls = __toESM(require("tls"));
|
|
962
|
+
var TDK = class {
|
|
963
|
+
static BASE_URL = "https://sozluk.gov.tr";
|
|
964
|
+
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
965
|
+
static KUBBEALTI_HOST = "eski.lugatim.com";
|
|
929
966
|
// Configuration
|
|
930
967
|
static defaultTimeoutMs = 8e3;
|
|
931
968
|
static defaultRetries = 1;
|
|
@@ -1472,12 +1509,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1472
1509
|
continue;
|
|
1473
1510
|
if (Math.abs(candidate.length - cleanWord.length) > 2)
|
|
1474
1511
|
continue;
|
|
1475
|
-
const rawDist =
|
|
1512
|
+
const rawDist = damerauLevenshtein(cleanWord, candidate);
|
|
1476
1513
|
if (rawDist === 0 || rawDist > 2)
|
|
1477
1514
|
continue;
|
|
1478
1515
|
const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
|
|
1479
1516
|
const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
|
|
1480
|
-
const score =
|
|
1517
|
+
const score = keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
|
|
1481
1518
|
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;
|
|
1482
1519
|
if (better) {
|
|
1483
1520
|
best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
|
|
@@ -1608,14 +1645,11 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1608
1645
|
const contentEnd = html.indexOf("<footer", contentStart);
|
|
1609
1646
|
if (contentEnd === -1)
|
|
1610
1647
|
return null;
|
|
1611
|
-
return
|
|
1648
|
+
return htmlToPlainText(html.slice(contentStart, contentEnd));
|
|
1612
1649
|
} catch {
|
|
1613
1650
|
return null;
|
|
1614
1651
|
}
|
|
1615
1652
|
}
|
|
1616
|
-
static htmlToPlainText(html) {
|
|
1617
|
-
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'|’/gi, "'").replace(/&/gi, "&").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
1618
|
-
}
|
|
1619
1653
|
/**
|
|
1620
1654
|
* GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
|
|
1621
1655
|
* supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
|
|
@@ -1629,7 +1663,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1629
1663
|
hostname: this.KUBBEALTI_HOST,
|
|
1630
1664
|
path: path2,
|
|
1631
1665
|
method: "GET",
|
|
1632
|
-
ca: [...tls.rootCertificates, ...
|
|
1666
|
+
ca: [...tls.rootCertificates, ...KUBBEALTI_EXTRA_CA],
|
|
1633
1667
|
headers: {
|
|
1634
1668
|
"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"
|
|
1635
1669
|
}
|
|
@@ -1721,7 +1755,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1721
1755
|
const entries = await this.getKubbealti(word);
|
|
1722
1756
|
if (!entries)
|
|
1723
1757
|
return null;
|
|
1724
|
-
return entries.map((e) =>
|
|
1758
|
+
return entries.map((e) => htmlToPlainText(e.anlam));
|
|
1725
1759
|
}
|
|
1726
1760
|
/**
|
|
1727
1761
|
* Autocomplete suggestions from Kubbealtı Lugatı's own typeahead endpoint
|
|
@@ -1757,7 +1791,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1757
1791
|
const match = html.match(/<meta name="description" content="([^"]*)"/);
|
|
1758
1792
|
if (!match)
|
|
1759
1793
|
return null;
|
|
1760
|
-
const description =
|
|
1794
|
+
const description = htmlToPlainText(match[1]);
|
|
1761
1795
|
if (description === "\xC7a\u011Fda\u015F T\xFCrk\xE7enin Etimolojisi")
|
|
1762
1796
|
return null;
|
|
1763
1797
|
return description;
|
|
@@ -1988,56 +2022,6 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
1988
2022
|
}
|
|
1989
2023
|
return analyses;
|
|
1990
2024
|
}
|
|
1991
|
-
/**
|
|
1992
|
-
* Damerau-Levenshtein edit-distance (optimal string alignment variant):
|
|
1993
|
-
* like classic Levenshtein but also counts an adjacent-character
|
|
1994
|
-
* transposition (e.g. "yanlız" -> "yalnız") as a single edit instead of
|
|
1995
|
-
* two substitutions — a very common class of typo that plain Levenshtein
|
|
1996
|
-
* otherwise misses.
|
|
1997
|
-
*/
|
|
1998
|
-
static damerauLevenshtein(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 = a[i - 1] === b[j - 1] ? 0 : 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] + cost);
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
}
|
|
2013
|
-
return dp[a.length][b.length];
|
|
2014
|
-
}
|
|
2015
|
-
/**
|
|
2016
|
-
* Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
|
|
2017
|
-
* recurrence as {@link damerauLevenshtein}, but a substitution is charged by
|
|
2018
|
-
* {@link keyboardSubCost} (a fraction of an edit when the two letters are
|
|
2019
|
-
* adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
|
|
2020
|
-
* transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
|
|
2021
|
-
* still cost a full 1. Used only to *rank* spelling candidates; the plain
|
|
2022
|
-
* integer distance still gates whether a suggestion is offered at all.
|
|
2023
|
-
*/
|
|
2024
|
-
static keyboardAwareDistance(a, b) {
|
|
2025
|
-
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
|
|
2026
|
-
for (let i = 0; i <= a.length; i++)
|
|
2027
|
-
dp[i][0] = i;
|
|
2028
|
-
for (let j = 0; j <= b.length; j++)
|
|
2029
|
-
dp[0][j] = j;
|
|
2030
|
-
for (let i = 1; i <= a.length; i++) {
|
|
2031
|
-
for (let j = 1; j <= b.length; j++) {
|
|
2032
|
-
const cost = keyboardSubCost(a[i - 1], b[j - 1]);
|
|
2033
|
-
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
2034
|
-
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
2035
|
-
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
return dp[a.length][b.length];
|
|
2040
|
-
}
|
|
2041
2025
|
/**
|
|
2042
2026
|
* Fetches multiple words concurrently with a small delay to avoid rate limiting.
|
|
2043
2027
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpServer,
|
|
3
3
|
runMcpServer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZG7YRT7V.mjs";
|
|
5
5
|
import {
|
|
6
6
|
TDK,
|
|
7
7
|
TDKClient,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
restoreInfinitive,
|
|
18
18
|
restoreVowelDrop,
|
|
19
19
|
restoreVowelNarrowing
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-YY4WA2AO.mjs";
|
|
21
21
|
export {
|
|
22
22
|
TDK,
|
|
23
23
|
TDKClient,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tdk-api-wrapper",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "TDK (Türk Dil Kurumu) unofficial live data API wrapper and MCP server for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"model-context-protocol"
|
|
39
39
|
],
|
|
40
40
|
"author": "cemyilmazzzz122 <cemyleo12@icloud.com> (https://github.com/cemyilmazzzz122)",
|
|
41
|
-
"license": "
|
|
41
|
+
"license": "GPL-3.0-or-later",
|
|
42
42
|
"homepage": "https://github.com/cemyilmazzzz122/tdk-api#readme",
|
|
43
43
|
"repository": {
|
|
44
44
|
"type": "git",
|