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/dist/cli.js CHANGED
@@ -71,168 +71,12 @@ var init_errors = __esm({
71
71
  }
72
72
  });
73
73
 
74
- // src/morphology.ts
75
- function isVowel(ch) {
76
- return TURKISH_VOWELS.includes(ch);
77
- }
78
- function restoreConsonantSoftening(stem) {
79
- if (stem.length < 2)
80
- return [];
81
- const last = stem.slice(-1);
82
- const base = stem.slice(0, -1);
83
- switch (last) {
84
- case "b":
85
- return [base + "p"];
86
- case "c":
87
- return [base + "\xE7"];
88
- case "d":
89
- return [base + "t"];
90
- case "\u011F":
91
- return [base + "k"];
92
- case "g":
93
- return [base + "k"];
94
- default:
95
- return [];
96
- }
97
- }
98
- function restoreVowelDrop(stem) {
99
- if (stem.length < 3)
100
- return [];
101
- const c1 = stem[stem.length - 2];
102
- const c2 = stem[stem.length - 1];
103
- if (!isVowel(c1) && !isVowel(c2)) {
104
- const vowelsInBase = stem.slice(0, -2).split("").filter(isVowel);
105
- if (vowelsInBase.length > 0) {
106
- const lastVowel = vowelsInBase[vowelsInBase.length - 1];
107
- let inserted = "i";
108
- if ("a\u0131".includes(lastVowel))
109
- inserted = "\u0131";
110
- else if ("ei".includes(lastVowel))
111
- inserted = "i";
112
- else if ("ou".includes(lastVowel))
113
- inserted = "u";
114
- else if ("\xF6\xFC".includes(lastVowel))
115
- inserted = "\xFC";
116
- return [stem.slice(0, -1) + inserted + c2];
117
- }
118
- }
119
- return [];
120
- }
121
- function restoreGemination(stem) {
122
- if (stem.length < 3)
123
- return [];
124
- const c1 = stem[stem.length - 2];
125
- const c2 = stem[stem.length - 1];
126
- if (c1 === c2 && !isVowel(c1)) {
127
- const single = stem.slice(0, -1);
128
- const hardened = restoreConsonantSoftening(single);
129
- return [single, ...hardened];
130
- }
131
- return [];
132
- }
133
- function restoreVowelNarrowing(stem) {
134
- if (stem.length < 2)
135
- return [];
136
- if (stem === "di")
137
- return ["de"];
138
- if (stem === "yi")
139
- return ["ye"];
140
- const lastChar = stem[stem.length - 1];
141
- const isLastNarrow = "\u0131iu\xFC".includes(lastChar);
142
- if (isLastNarrow) {
143
- const vowelsInBase = stem.slice(0, -1).split("").filter(isVowel);
144
- const lastVowel = vowelsInBase.length > 0 ? vowelsInBase[vowelsInBase.length - 1] : lastChar;
145
- const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
146
- return [stem.slice(0, -1) + widened];
147
- }
148
- if (!isVowel(lastChar)) {
149
- const vowelsInBase = stem.split("").filter(isVowel);
150
- if (vowelsInBase.length > 0) {
151
- const lastVowel = vowelsInBase[vowelsInBase.length - 1];
152
- const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
153
- return [stem + widened];
154
- }
155
- }
156
- return [];
157
- }
158
- function restoreInfinitive(stem) {
159
- if (stem.length < 2)
160
- return [];
161
- const vowelsInBase = stem.split("").filter(isVowel);
162
- if (vowelsInBase.length === 0)
163
- return [];
164
- const lastVowel = vowelsInBase[vowelsInBase.length - 1];
165
- return "a\u0131ou".includes(lastVowel) ? [stem + "mak"] : [stem + "mek"];
166
- }
167
- function getStemCandidates(word2, minStemLength = 2, maxDepth = 4) {
168
- if (!word2 || word2.trim().length === 0)
169
- return [];
170
- const raw = word2.trim();
171
- const normalized = raw.toLocaleLowerCase("tr-TR");
172
- const candidatesWithWeight = [];
173
- const seen = /* @__PURE__ */ new Set();
174
- if (raw.includes("'") || raw.includes("\u2019")) {
175
- const apostropheStem = normalized.split(/['’]/)[0];
176
- if (apostropheStem.length >= minStemLength) {
177
- candidatesWithWeight.push({ candidate: apostropheStem, baseLength: apostropheStem.length + 10 });
178
- seen.add(apostropheStem);
179
- }
180
- }
181
- const bareInfinitives = restoreInfinitive(normalized);
182
- for (const inf of bareInfinitives) {
183
- if (!seen.has(inf) && inf !== normalized) {
184
- seen.add(inf);
185
- candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
186
- }
187
- }
188
- let frontier = [normalized];
189
- for (let depth = 0; depth < maxDepth; depth++) {
190
- const nextFrontier = [];
191
- for (const current of frontier) {
192
- for (const suffix of TURKISH_SUFFIXES) {
193
- if (current.length - suffix.length >= minStemLength && current.endsWith(suffix)) {
194
- const stem = current.slice(0, -suffix.length);
195
- const hardened = restoreConsonantSoftening(stem);
196
- const vowelDropped = restoreVowelDrop(stem);
197
- const geminated = restoreGemination(stem);
198
- const isNarrowingSuffix = suffix.startsWith("yor") || suffix.includes("iyor") || suffix.includes("\u0131yor") || suffix.includes("uyor") || suffix.includes("\xFCyor");
199
- const isDeYeBuffer = (stem === "di" || stem === "yi") && suffix.startsWith("y");
200
- const narrowed = isNarrowingSuffix || isDeYeBuffer ? restoreVowelNarrowing(stem) : [];
201
- 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");
202
- const verbalBases = [stem, ...hardened, ...narrowed];
203
- const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
204
- const variants = [stem, ...hardened, ...vowelDropped, ...geminated, ...narrowed];
205
- for (const variant of variants) {
206
- if (!seen.has(variant) && variant !== normalized) {
207
- seen.add(variant);
208
- nextFrontier.push(variant);
209
- candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
210
- }
211
- }
212
- for (const inf of infinitives) {
213
- if (!seen.has(inf) && inf !== normalized) {
214
- seen.add(inf);
215
- nextFrontier.push(inf);
216
- const weight = isVerbSuffix ? stem.length + 5 : stem.length;
217
- candidatesWithWeight.push({ candidate: inf, baseLength: weight });
218
- }
219
- }
220
- }
221
- }
222
- }
223
- if (nextFrontier.length === 0)
224
- break;
225
- frontier = nextFrontier;
226
- }
227
- candidatesWithWeight.sort((a, b) => b.baseLength - a.baseLength);
228
- return [...new Set(candidatesWithWeight.map((c2) => c2.candidate))];
229
- }
230
- var TURKISH_VOWELS, TURKISH_SUFFIXES;
231
- var init_morphology = __esm({
232
- "src/morphology.ts"() {
74
+ // src/data/suffixes.ts
75
+ var TURKISH_SUFFIXES;
76
+ var init_suffixes = __esm({
77
+ "src/data/suffixes.ts"() {
233
78
  "use strict";
234
79
  init_cjs_shims();
235
- TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
236
80
  TURKISH_SUFFIXES = [
237
81
  // 9-letter composite suffixes
238
82
  "lerimizden",
@@ -638,36 +482,179 @@ var init_morphology = __esm({
638
482
  }
639
483
  });
640
484
 
641
- // src/tdk.ts
642
- function keyboardSubCost(a, b) {
643
- if (a === b)
644
- return 0;
645
- if (DIACRITIC_SIBLINGS[a] === b)
646
- return DIACRITIC_SUB_COST;
647
- const pa = KEYBOARD_COORDS[a];
648
- const pb = KEYBOARD_COORDS[b];
649
- if (!pa || !pb)
650
- return 1;
651
- const dx = Math.abs(pa[0] - pb[0]);
652
- const dy = Math.abs(pa[1] - pb[1]);
653
- if (dy === 0 && dx <= 1 + 1e-9)
654
- return KEYBOARD_ROW_SUB_COST;
655
- if (dy === 1 && dx <= 1 + 1e-9)
656
- return KEYBOARD_DIAGONAL_SUB_COST;
657
- return 1;
485
+ // src/morphology.ts
486
+ function isVowel(ch) {
487
+ return TURKISH_VOWELS.includes(ch);
658
488
  }
659
- var fs, path, os, https, tls, COMMON_MISSPELLINGS, SEY_EXCEPTIONS, KEYBOARD_ROWS, KEYBOARD_COORDS, DIACRITIC_SIBLINGS, KEYBOARD_ROW_SUB_COST, KEYBOARD_DIAGONAL_SUB_COST, DIACRITIC_SUB_COST, TRANSPOSITION_COST, TDK;
660
- var init_tdk = __esm({
661
- "src/tdk.ts"() {
489
+ function restoreConsonantSoftening(stem) {
490
+ if (stem.length < 2)
491
+ return [];
492
+ const last = stem.slice(-1);
493
+ const base = stem.slice(0, -1);
494
+ switch (last) {
495
+ case "b":
496
+ return [base + "p"];
497
+ case "c":
498
+ return [base + "\xE7"];
499
+ case "d":
500
+ return [base + "t"];
501
+ case "\u011F":
502
+ return [base + "k"];
503
+ case "g":
504
+ return [base + "k"];
505
+ default:
506
+ return [];
507
+ }
508
+ }
509
+ function restoreVowelDrop(stem) {
510
+ if (stem.length < 3)
511
+ return [];
512
+ const c1 = stem[stem.length - 2];
513
+ const c2 = stem[stem.length - 1];
514
+ if (!isVowel(c1) && !isVowel(c2)) {
515
+ const vowelsInBase = stem.slice(0, -2).split("").filter(isVowel);
516
+ if (vowelsInBase.length > 0) {
517
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
518
+ let inserted = "i";
519
+ if ("a\u0131".includes(lastVowel))
520
+ inserted = "\u0131";
521
+ else if ("ei".includes(lastVowel))
522
+ inserted = "i";
523
+ else if ("ou".includes(lastVowel))
524
+ inserted = "u";
525
+ else if ("\xF6\xFC".includes(lastVowel))
526
+ inserted = "\xFC";
527
+ return [stem.slice(0, -1) + inserted + c2];
528
+ }
529
+ }
530
+ return [];
531
+ }
532
+ function restoreGemination(stem) {
533
+ if (stem.length < 3)
534
+ return [];
535
+ const c1 = stem[stem.length - 2];
536
+ const c2 = stem[stem.length - 1];
537
+ if (c1 === c2 && !isVowel(c1)) {
538
+ const single = stem.slice(0, -1);
539
+ const hardened = restoreConsonantSoftening(single);
540
+ return [single, ...hardened];
541
+ }
542
+ return [];
543
+ }
544
+ function restoreVowelNarrowing(stem) {
545
+ if (stem.length < 2)
546
+ return [];
547
+ if (stem === "di")
548
+ return ["de"];
549
+ if (stem === "yi")
550
+ return ["ye"];
551
+ const lastChar = stem[stem.length - 1];
552
+ const isLastNarrow = "\u0131iu\xFC".includes(lastChar);
553
+ if (isLastNarrow) {
554
+ const vowelsInBase = stem.slice(0, -1).split("").filter(isVowel);
555
+ const lastVowel = vowelsInBase.length > 0 ? vowelsInBase[vowelsInBase.length - 1] : lastChar;
556
+ const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
557
+ return [stem.slice(0, -1) + widened];
558
+ }
559
+ if (!isVowel(lastChar)) {
560
+ const vowelsInBase = stem.split("").filter(isVowel);
561
+ if (vowelsInBase.length > 0) {
562
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
563
+ const widened = "a\u0131ou".includes(lastVowel) ? "a" : "e";
564
+ return [stem + widened];
565
+ }
566
+ }
567
+ return [];
568
+ }
569
+ function restoreInfinitive(stem) {
570
+ if (stem.length < 2)
571
+ return [];
572
+ const vowelsInBase = stem.split("").filter(isVowel);
573
+ if (vowelsInBase.length === 0)
574
+ return [];
575
+ const lastVowel = vowelsInBase[vowelsInBase.length - 1];
576
+ return "a\u0131ou".includes(lastVowel) ? [stem + "mak"] : [stem + "mek"];
577
+ }
578
+ function getStemCandidates(word2, minStemLength = 2, maxDepth = 4) {
579
+ if (!word2 || word2.trim().length === 0)
580
+ return [];
581
+ const raw = word2.trim();
582
+ const normalized = raw.toLocaleLowerCase("tr-TR");
583
+ const candidatesWithWeight = [];
584
+ const seen = /* @__PURE__ */ new Set();
585
+ if (raw.includes("'") || raw.includes("\u2019")) {
586
+ const apostropheStem = normalized.split(/['’]/)[0];
587
+ if (apostropheStem.length >= minStemLength) {
588
+ candidatesWithWeight.push({ candidate: apostropheStem, baseLength: apostropheStem.length + 10 });
589
+ seen.add(apostropheStem);
590
+ }
591
+ }
592
+ const bareInfinitives = restoreInfinitive(normalized);
593
+ for (const inf of bareInfinitives) {
594
+ if (!seen.has(inf) && inf !== normalized) {
595
+ seen.add(inf);
596
+ candidatesWithWeight.push({ candidate: inf, baseLength: normalized.length });
597
+ }
598
+ }
599
+ let frontier = [normalized];
600
+ for (let depth = 0; depth < maxDepth; depth++) {
601
+ const nextFrontier = [];
602
+ for (const current of frontier) {
603
+ for (const suffix of TURKISH_SUFFIXES) {
604
+ if (current.length - suffix.length >= minStemLength && current.endsWith(suffix)) {
605
+ const stem = current.slice(0, -suffix.length);
606
+ const hardened = restoreConsonantSoftening(stem);
607
+ const vowelDropped = restoreVowelDrop(stem);
608
+ const geminated = restoreGemination(stem);
609
+ const isNarrowingSuffix = suffix.startsWith("yor") || suffix.includes("iyor") || suffix.includes("\u0131yor") || suffix.includes("uyor") || suffix.includes("\xFCyor");
610
+ const isDeYeBuffer = (stem === "di" || stem === "yi") && suffix.startsWith("y");
611
+ const narrowed = isNarrowingSuffix || isDeYeBuffer ? restoreVowelNarrowing(stem) : [];
612
+ 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");
613
+ const verbalBases = [stem, ...hardened, ...narrowed];
614
+ const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
615
+ const variants = [stem, ...hardened, ...vowelDropped, ...geminated, ...narrowed];
616
+ for (const variant of variants) {
617
+ if (!seen.has(variant) && variant !== normalized) {
618
+ seen.add(variant);
619
+ nextFrontier.push(variant);
620
+ candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
621
+ }
622
+ }
623
+ for (const inf of infinitives) {
624
+ if (!seen.has(inf) && inf !== normalized) {
625
+ seen.add(inf);
626
+ nextFrontier.push(inf);
627
+ const weight = isVerbSuffix ? stem.length + 5 : stem.length;
628
+ candidatesWithWeight.push({ candidate: inf, baseLength: weight });
629
+ }
630
+ }
631
+ }
632
+ }
633
+ }
634
+ if (nextFrontier.length === 0)
635
+ break;
636
+ frontier = nextFrontier;
637
+ }
638
+ candidatesWithWeight.sort((a, b) => b.baseLength - a.baseLength);
639
+ return [...new Set(candidatesWithWeight.map((c2) => c2.candidate))];
640
+ }
641
+ var TURKISH_VOWELS;
642
+ var init_morphology = __esm({
643
+ "src/morphology.ts"() {
644
+ "use strict";
645
+ init_cjs_shims();
646
+ init_suffixes();
647
+ init_suffixes();
648
+ TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
649
+ }
650
+ });
651
+
652
+ // src/data/misspellings.ts
653
+ var COMMON_MISSPELLINGS, SEY_EXCEPTIONS;
654
+ var init_misspellings = __esm({
655
+ "src/data/misspellings.ts"() {
662
656
  "use strict";
663
657
  init_cjs_shims();
664
- init_errors();
665
- init_morphology();
666
- fs = __toESM(require("fs"));
667
- path = __toESM(require("path"));
668
- os = __toESM(require("os"));
669
- https = __toESM(require("https"));
670
- tls = __toESM(require("tls"));
671
658
  COMMON_MISSPELLINGS = {
672
659
  // -şey ile biten ve ayrı yazılması zorunlu söz öbekleri
673
660
  her\u015Fey: "her \u015Fey",
@@ -821,58 +808,17 @@ var init_tdk = __esm({
821
808
  babaanne: "babaanne"
822
809
  };
823
810
  SEY_EXCEPTIONS = /* @__PURE__ */ new Set(["d\xFC\u015Fey", "e\u015Fey", "konsey", "jersey", "\u015Fey"]);
824
- KEYBOARD_ROWS = [
825
- ["qwertyu\u0131op\u011F\xFC", 0],
826
- ["asdfghjkl\u015Fi", 0.5],
827
- ["zxcvbnm\xF6\xE7", 1]
828
- ];
829
- KEYBOARD_COORDS = (() => {
830
- const coords = {};
831
- KEYBOARD_ROWS.forEach(([keys, offset], row) => {
832
- [...keys].forEach((key, col) => {
833
- coords[key] = [col + offset, row];
834
- });
835
- });
836
- return coords;
837
- })();
838
- DIACRITIC_SIBLINGS = {
839
- \u0131: "i",
840
- i: "\u0131",
841
- \u00F6: "o",
842
- o: "\xF6",
843
- \u00FC: "u",
844
- u: "\xFC",
845
- \u015F: "s",
846
- s: "\u015F",
847
- \u00E7: "c",
848
- c: "\xE7",
849
- \u011F: "g",
850
- g: "\u011F",
851
- \u00E2: "a",
852
- a: "\xE2"
853
- };
854
- KEYBOARD_ROW_SUB_COST = 0.4;
855
- KEYBOARD_DIAGONAL_SUB_COST = 0.55;
856
- DIACRITIC_SUB_COST = 0.3;
857
- TRANSPOSITION_COST = 0.8;
858
- TDK = class {
859
- static BASE_URL = "https://sozluk.gov.tr";
860
- static AUDIO_API_HOST = "api.sozluk.gov.tr";
861
- static KUBBEALTI_HOST = "eski.lugatim.com";
862
- /**
863
- * `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
864
- * certificate during the TLS handshake, omitting the intermediates a
865
- * correctly configured server would include — a server-side misconfiguration,
866
- * not something we should paper over by disabling verification. These are
867
- * the two certificates the server *should* be sending (fetched from the
868
- * leaf's own Authority Information Access URLs), supplied here so Node can
869
- * still build a full, properly verified chain up to a root it already
870
- * trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
871
- * stops working and every Kubbealtı call fails closed to `null` — same
872
- * fail-closed contract as the rest of this file's fragile integrations.
873
- */
874
- static KUBBEALTI_EXTRA_CA = [
875
- `-----BEGIN CERTIFICATE-----
811
+ }
812
+ });
813
+
814
+ // src/data/kubbealti-ca.ts
815
+ var KUBBEALTI_EXTRA_CA;
816
+ var init_kubbealti_ca = __esm({
817
+ "src/data/kubbealti-ca.ts"() {
818
+ "use strict";
819
+ init_cjs_shims();
820
+ KUBBEALTI_EXTRA_CA = [
821
+ `-----BEGIN CERTIFICATE-----
876
822
  MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu
877
823
  MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe
878
824
  Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw
@@ -900,7 +846,7 @@ NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve
900
846
  86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy
901
847
  LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA==
902
848
  -----END CERTIFICATE-----`,
903
- `-----BEGIN CERTIFICATE-----
849
+ `-----BEGIN CERTIFICATE-----
904
850
  MIIF9DCCA9ygAwIBAgIRAPJLbRf52a18scn+p4eCaZ8wDQYJKoZIhvcNAQELBQAw
905
851
  TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
906
852
  cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjYwNTEzMDAwMDAw
@@ -934,7 +880,146 @@ RGMuHGnzS3hFIrRTfKxrzUZ9RzQWzEG3K6fJ3r2nqSltkeytis9DIBoFY9VmVyjL
934
880
  M71DMi+y1+TRSJVClEMwvA4yL++7q9XZx5r5wBRWB4kQTKH5qyoZnDw7iiuh1lID
935
881
  yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
936
882
  -----END CERTIFICATE-----`
937
- ];
883
+ ];
884
+ }
885
+ });
886
+
887
+ // src/data/keyboard-layout.ts
888
+ function keyboardSubCost(a, b) {
889
+ if (a === b)
890
+ return 0;
891
+ if (DIACRITIC_SIBLINGS[a] === b)
892
+ return DIACRITIC_SUB_COST;
893
+ const pa = KEYBOARD_COORDS[a];
894
+ const pb = KEYBOARD_COORDS[b];
895
+ if (!pa || !pb)
896
+ return 1;
897
+ const dx = Math.abs(pa[0] - pb[0]);
898
+ const dy = Math.abs(pa[1] - pb[1]);
899
+ if (dy === 0 && dx <= 1 + 1e-9)
900
+ return KEYBOARD_ROW_SUB_COST;
901
+ if (dy === 1 && dx <= 1 + 1e-9)
902
+ return KEYBOARD_DIAGONAL_SUB_COST;
903
+ return 1;
904
+ }
905
+ var KEYBOARD_ROWS, KEYBOARD_COORDS, DIACRITIC_SIBLINGS, KEYBOARD_ROW_SUB_COST, KEYBOARD_DIAGONAL_SUB_COST, DIACRITIC_SUB_COST, TRANSPOSITION_COST;
906
+ var init_keyboard_layout = __esm({
907
+ "src/data/keyboard-layout.ts"() {
908
+ "use strict";
909
+ init_cjs_shims();
910
+ KEYBOARD_ROWS = [
911
+ ["qwertyu\u0131op\u011F\xFC", 0],
912
+ ["asdfghjkl\u015Fi", 0.5],
913
+ ["zxcvbnm\xF6\xE7", 1]
914
+ ];
915
+ KEYBOARD_COORDS = (() => {
916
+ const coords = {};
917
+ KEYBOARD_ROWS.forEach(([keys, offset], row) => {
918
+ [...keys].forEach((key, col) => {
919
+ coords[key] = [col + offset, row];
920
+ });
921
+ });
922
+ return coords;
923
+ })();
924
+ DIACRITIC_SIBLINGS = {
925
+ \u0131: "i",
926
+ i: "\u0131",
927
+ \u00F6: "o",
928
+ o: "\xF6",
929
+ \u00FC: "u",
930
+ u: "\xFC",
931
+ \u015F: "s",
932
+ s: "\u015F",
933
+ \u00E7: "c",
934
+ c: "\xE7",
935
+ \u011F: "g",
936
+ g: "\u011F",
937
+ \u00E2: "a",
938
+ a: "\xE2"
939
+ };
940
+ KEYBOARD_ROW_SUB_COST = 0.4;
941
+ KEYBOARD_DIAGONAL_SUB_COST = 0.55;
942
+ DIACRITIC_SUB_COST = 0.3;
943
+ TRANSPOSITION_COST = 0.8;
944
+ }
945
+ });
946
+
947
+ // src/lib/edit-distance.ts
948
+ function damerauLevenshtein(a, b) {
949
+ const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
950
+ for (let i = 0; i <= a.length; i++)
951
+ dp[i][0] = i;
952
+ for (let j = 0; j <= b.length; j++)
953
+ dp[0][j] = j;
954
+ for (let i = 1; i <= a.length; i++) {
955
+ for (let j = 1; j <= b.length; j++) {
956
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
957
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
958
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
959
+ dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
960
+ }
961
+ }
962
+ }
963
+ return dp[a.length][b.length];
964
+ }
965
+ function keyboardAwareDistance(a, b) {
966
+ const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
967
+ for (let i = 0; i <= a.length; i++)
968
+ dp[i][0] = i;
969
+ for (let j = 0; j <= b.length; j++)
970
+ dp[0][j] = j;
971
+ for (let i = 1; i <= a.length; i++) {
972
+ for (let j = 1; j <= b.length; j++) {
973
+ const cost = keyboardSubCost(a[i - 1], b[j - 1]);
974
+ dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
975
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
976
+ dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
977
+ }
978
+ }
979
+ }
980
+ return dp[a.length][b.length];
981
+ }
982
+ var init_edit_distance = __esm({
983
+ "src/lib/edit-distance.ts"() {
984
+ "use strict";
985
+ init_cjs_shims();
986
+ init_keyboard_layout();
987
+ }
988
+ });
989
+
990
+ // src/lib/html.ts
991
+ function htmlToPlainText(html) {
992
+ 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();
993
+ }
994
+ var init_html = __esm({
995
+ "src/lib/html.ts"() {
996
+ "use strict";
997
+ init_cjs_shims();
998
+ }
999
+ });
1000
+
1001
+ // src/tdk.ts
1002
+ var fs, path, os, https, tls, TDK;
1003
+ var init_tdk = __esm({
1004
+ "src/tdk.ts"() {
1005
+ "use strict";
1006
+ init_cjs_shims();
1007
+ init_errors();
1008
+ init_morphology();
1009
+ init_misspellings();
1010
+ init_kubbealti_ca();
1011
+ init_edit_distance();
1012
+ init_html();
1013
+ fs = __toESM(require("fs"));
1014
+ path = __toESM(require("path"));
1015
+ os = __toESM(require("os"));
1016
+ https = __toESM(require("https"));
1017
+ tls = __toESM(require("tls"));
1018
+ init_misspellings();
1019
+ TDK = class {
1020
+ static BASE_URL = "https://sozluk.gov.tr";
1021
+ static AUDIO_API_HOST = "api.sozluk.gov.tr";
1022
+ static KUBBEALTI_HOST = "eski.lugatim.com";
938
1023
  // Configuration
939
1024
  static defaultTimeoutMs = 8e3;
940
1025
  static defaultRetries = 1;
@@ -1481,12 +1566,12 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1481
1566
  continue;
1482
1567
  if (Math.abs(candidate.length - cleanWord.length) > 2)
1483
1568
  continue;
1484
- const rawDist = this.damerauLevenshtein(cleanWord, candidate);
1569
+ const rawDist = damerauLevenshtein(cleanWord, candidate);
1485
1570
  if (rawDist === 0 || rawDist > 2)
1486
1571
  continue;
1487
1572
  const firstMismatch = candidate[0] === cleanWord[0] ? 0 : 1;
1488
1573
  const lengthMismatch = candidate.length === cleanWord.length ? 0 : 1;
1489
- const score = this.keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
1574
+ const score = keyboardAwareDistance(cleanWord, candidate) + (firstMismatch > 0 ? 1.2 : 0);
1490
1575
  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;
1491
1576
  if (better) {
1492
1577
  best = { candidate, score, rawDist, firstMismatch, lengthMismatch };
@@ -1617,14 +1702,11 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1617
1702
  const contentEnd = html.indexOf("<footer", contentStart);
1618
1703
  if (contentEnd === -1)
1619
1704
  return null;
1620
- return this.htmlToPlainText(html.slice(contentStart, contentEnd));
1705
+ return htmlToPlainText(html.slice(contentStart, contentEnd));
1621
1706
  } catch {
1622
1707
  return null;
1623
1708
  }
1624
1709
  }
1625
- static htmlToPlainText(html) {
1626
- 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();
1627
- }
1628
1710
  /**
1629
1711
  * GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
1630
1712
  * supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
@@ -1638,7 +1720,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1638
1720
  hostname: this.KUBBEALTI_HOST,
1639
1721
  path: path2,
1640
1722
  method: "GET",
1641
- ca: [...tls.rootCertificates, ...this.KUBBEALTI_EXTRA_CA],
1723
+ ca: [...tls.rootCertificates, ...KUBBEALTI_EXTRA_CA],
1642
1724
  headers: {
1643
1725
  "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"
1644
1726
  }
@@ -1730,7 +1812,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1730
1812
  const entries = await this.getKubbealti(word2);
1731
1813
  if (!entries)
1732
1814
  return null;
1733
- return entries.map((e) => this.htmlToPlainText(e.anlam));
1815
+ return entries.map((e) => htmlToPlainText(e.anlam));
1734
1816
  }
1735
1817
  /**
1736
1818
  * Autocomplete suggestions from Kubbealtı Lugatı's own typeahead endpoint
@@ -1766,7 +1848,7 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1766
1848
  const match = html.match(/<meta name="description" content="([^"]*)"/);
1767
1849
  if (!match)
1768
1850
  return null;
1769
- const description = this.htmlToPlainText(match[1]);
1851
+ const description = htmlToPlainText(match[1]);
1770
1852
  if (description === "\xC7a\u011Fda\u015F T\xFCrk\xE7enin Etimolojisi")
1771
1853
  return null;
1772
1854
  return description;
@@ -1997,56 +2079,6 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
1997
2079
  }
1998
2080
  return analyses;
1999
2081
  }
2000
- /**
2001
- * Damerau-Levenshtein edit-distance (optimal string alignment variant):
2002
- * like classic Levenshtein but also counts an adjacent-character
2003
- * transposition (e.g. "yanlız" -> "yalnız") as a single edit instead of
2004
- * two substitutions — a very common class of typo that plain Levenshtein
2005
- * otherwise misses.
2006
- */
2007
- static damerauLevenshtein(a, b) {
2008
- const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
2009
- for (let i = 0; i <= a.length; i++)
2010
- dp[i][0] = i;
2011
- for (let j = 0; j <= b.length; j++)
2012
- dp[0][j] = j;
2013
- for (let i = 1; i <= a.length; i++) {
2014
- for (let j = 1; j <= b.length; j++) {
2015
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
2016
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
2017
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
2018
- dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + cost);
2019
- }
2020
- }
2021
- }
2022
- return dp[a.length][b.length];
2023
- }
2024
- /**
2025
- * Keyboard- and diacritic-aware edit distance: same optimal-string-alignment
2026
- * recurrence as {@link damerauLevenshtein}, but a substitution is charged by
2027
- * {@link keyboardSubCost} (a fraction of an edit when the two letters are
2028
- * adjacent on a Turkish Q keyboard or are ASCII/diacritic siblings) and a
2029
- * transposition costs {@link TRANSPOSITION_COST}. Insertions and deletions
2030
- * still cost a full 1. Used only to *rank* spelling candidates; the plain
2031
- * integer distance still gates whether a suggestion is offered at all.
2032
- */
2033
- static keyboardAwareDistance(a, b) {
2034
- const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
2035
- for (let i = 0; i <= a.length; i++)
2036
- dp[i][0] = i;
2037
- for (let j = 0; j <= b.length; j++)
2038
- dp[0][j] = j;
2039
- for (let i = 1; i <= a.length; i++) {
2040
- for (let j = 1; j <= b.length; j++) {
2041
- const cost = keyboardSubCost(a[i - 1], b[j - 1]);
2042
- dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
2043
- if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
2044
- dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + TRANSPOSITION_COST);
2045
- }
2046
- }
2047
- }
2048
- return dp[a.length][b.length];
2049
- }
2050
2082
  /**
2051
2083
  * Fetches multiple words concurrently with a small delay to avoid rate limiting.
2052
2084
  */