tdk-api-wrapper 1.2.2 → 1.3.0
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 +11 -0
- package/dist/{chunk-SNY3KUCF.mjs → chunk-SLNXKZKR.mjs} +238 -1
- package/dist/cli.js +283 -3
- package/dist/cli.mjs +46 -3
- package/dist/index.d.mts +75 -1
- package/dist/index.d.ts +75 -1
- package/dist/index.js +238 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/cli.ts +41 -1
- package/src/tdk.ts +240 -1
- package/src/types.ts +10 -0
package/README.md
CHANGED
|
@@ -35,6 +35,9 @@ tdk kural kısaltmalar
|
|
|
35
35
|
tdk karsilastir kalem kağıt
|
|
36
36
|
tdk analiz "Bu güzel kalem masanın üstünde duruyor"
|
|
37
37
|
tdk oneri kale
|
|
38
|
+
tdk kubbealti merhaba
|
|
39
|
+
tdk nisanyan merhaba
|
|
40
|
+
tdk viki merhaba
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
Herhangi bir komuta `--json` bayrağı eklendiğinde çıktı, insan-okunur metin yerine tek satırlık JSON olarak basılır (script/otomasyon kullanımı için):
|
|
@@ -90,6 +93,14 @@ Aşağıdaki metotlar `TDK` sınıfı üzerinden statik olarak erişilebilir dur
|
|
|
90
93
|
- **`TDK.getKurallar()`**: TDK'nin `/icerik` akışının o an döndürdüğü yazım kuralı sayfa(lar)ını `{ adi, url }` şeklinde listeler. Not: bu sabit bir katalog değildir — `/icerik` her istekte, yaklaşık yirmi kurallık bir havuzdan rastgele tek bir kural döndürür.
|
|
91
94
|
- **`TDK.getRule(name)`**: Adı verilen (küçük/büyük harf duyarsız, alt dize eşleşmesi) yazım kuralının tam metnini `tdk.gov.tr`'den çekip düz metne çevirir. `getKurallar()`'ın rastgeleliği yüzünden istenen kuralı bulana kadar eşzamanlı gruplar hâlinde (toplam en fazla 25 deneme, ~5 round-trip'e sığdırılmış) yeniden dener; bulamazsa veya sayfa ayrıştırılamazsa `null` döner.
|
|
92
95
|
|
|
96
|
+
### 5. Diğer Sözlük Kaynakları
|
|
97
|
+
|
|
98
|
+
TDK dışındaki bu üç kaynak da her zaman kullanılabilir/dokümante edilmiş resmî API'ler değildir; her biri **fragile scraping** (kırılgan, dokümante edilmemiş entegrasyon) — kaynak taraflarında bir değişiklik olursa `null`/`[]` dönerler, hataya düşmezler. Verinin telif/kullanım koşulları kaynağa göre farklıdır: Wiktionary içeriği CC BY-SA lisanslıdır (açık); Nişanyan Sözlük ücretsiz, açık bir kişisel/akademik kaynaktır; **Kubbealtı Lugatı ise ticari bir sözlük ürünüdür** — bu kütüphane onu da dokümante edilmemiş bir uç noktadan çekebiliyor olsa da, kullanımınızı Kubbealtı'nın kendi kullanım şartlarına göre değerlendirmeniz önerilir.
|
|
99
|
+
|
|
100
|
+
- **`TDK.getKubbealti(word)`**: Kubbealtı Lugatı'nın ("Misalli Büyük Türkçe Sözlük") verilerini `{ kelime, anlam }` dizisi olarak döner (`anlam` zengin tipografi içeren ham HTML'dir). `getKubbealtiMeanings(word)` aynı veriyi düz metne çevirir. `getKubbealtiSuggestions(prefix)` Kubbealtı'nın kendi otomatik tamamlama uç noktasını kullanır (TDK'nin `getSuggestions()`'ından bağımsız, ayrı bir veri kaynağı). Not: Kubbealtı'nın veri sunucusu (`eski.lugatim.com`) sertifika zincirini eksik gönderiyor; bu kütüphane eksik ara sertifikaları ekleyerek zinciri düzgün doğruluyor (doğrulamayı kapatmıyor) — Let's Encrypt bu ara sertifikayı döndürürse bu entegrasyon `null` dönmeye başlar.
|
|
101
|
+
- **`TDK.getNisanyan(word)`**: Nişanyan Sözlük'ten kelimenin etimoloji paragrafını düz metin olarak döner; kelime bulunamazsa `null`.
|
|
102
|
+
- **`TDK.getWiktionary(word)`**: Türkçe Vikisözlük'ten (`tr.wiktionary.org`) resmî MediaWiki API'si (`action=query&prop=extracts`) üzerinden veri çeker — bu üçü arasında scraping olmayan, resmî ve en kararlı olanı. `{ raw, sections }` döner; `sections` metni `== Köken ==`, `=== Söyleniş ===` gibi başlıklara göre bir sözlüğe ayırır. `getWiktionarySection(word, sectionName)` tek bir bölümü (örn. `"Köken"`) büyük/küçük harf duyarsız süzer.
|
|
103
|
+
|
|
93
104
|
## Hata Yönetimi
|
|
94
105
|
|
|
95
106
|
Kütüphane, ayırt edilebilir hata sınıfları fırlatır (hepsi `Error`'dan türer):
|
|
@@ -30,9 +30,87 @@ import * as fs from "fs";
|
|
|
30
30
|
import * as path from "path";
|
|
31
31
|
import * as os from "os";
|
|
32
32
|
import * as https from "https";
|
|
33
|
+
import * as tls from "tls";
|
|
33
34
|
var TDK = class {
|
|
34
35
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
35
36
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
37
|
+
static KUBBEALTI_HOST = "eski.lugatim.com";
|
|
38
|
+
/**
|
|
39
|
+
* `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
|
|
40
|
+
* certificate during the TLS handshake, omitting the intermediates a
|
|
41
|
+
* correctly configured server would include — a server-side misconfiguration,
|
|
42
|
+
* not something we should paper over by disabling verification. These are
|
|
43
|
+
* the two certificates the server *should* be sending (fetched from the
|
|
44
|
+
* leaf's own Authority Information Access URLs), supplied here so Node can
|
|
45
|
+
* still build a full, properly verified chain up to a root it already
|
|
46
|
+
* trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
|
|
47
|
+
* stops working and every Kubbealtı call fails closed to `null` — same
|
|
48
|
+
* fail-closed contract as the rest of this file's fragile integrations.
|
|
49
|
+
*/
|
|
50
|
+
static KUBBEALTI_EXTRA_CA = [
|
|
51
|
+
`-----BEGIN CERTIFICATE-----
|
|
52
|
+
MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu
|
|
53
|
+
MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe
|
|
54
|
+
Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw
|
|
55
|
+
FAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZUjIwggEiMA0GCSqGSIb3
|
|
56
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQDZ0LxwBppqh84luqMerV/eeL/fXQ7mLQQv1Lnp
|
|
57
|
+
WKZbyvGpx6wh6AfnslAnF6ewTkcHA+gSOoBvm3Dfm06AuGiF+KRut4fAcowqnAQQ
|
|
58
|
+
CW98+QPP/eOv/wug7Iyk4NkOxf2I6g2f55T6nJoOTLFcukeRq80JGQEYan+dPFr9
|
|
59
|
+
OGUgQK2hGKgNkW87pappsOAuUJcroYhRt5uUis4qaZireiseu32gzDJNBAiKtsvd
|
|
60
|
+
6HX4v25bpkRNcS/B/Gtc9kVbUpD+2PLPxdei3Tim55k4tfAEXwD2qyiPTxrTNq6l
|
|
61
|
+
N+AMr5g2c1dNqkOTwjxeV6L5lpP1rGiYvLnRaPlOqyZRPW+5AgMBAAGjge4wgesw
|
|
62
|
+
DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBIGA1UdEwEB/wQI
|
|
63
|
+
MAYBAf8CAQAwHQYDVR0OBBYEFEAVLSZ57TIgnt+ach3WMh+BDIEMMB8GA1UdIwQY
|
|
64
|
+
MBaAFN7nW2DQIm1AKH0/DQH+pLVStFGUMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEF
|
|
65
|
+
BQcwAoYWaHR0cDovL3lyLmkubGVuY3Iub3JnLzATBgNVHSAEDDAKMAgGBmeBDAEC
|
|
66
|
+
ATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veXIuYy5sZW5jci5vcmcvMA0GCSqG
|
|
67
|
+
SIb3DQEBCwUAA4ICAQB0ZUQWZ9/Yn9COEpo+JfecMnB0h0vwDm/M66IqXqw3LoaL
|
|
68
|
+
mx9lZvRTeDIS67PUeI3yCA2W6PKRD0/FE/G57lOmS+Xy5AaaL00ICGOqjNcCaMWW
|
|
69
|
+
8o8nevHOd4i4lqgtznE/28QwlcdJyF8yBiWHpnyjhEpmNWJURgOCOg2xpwRMBCsj
|
|
70
|
+
MScqYPtOhBeuYQvSwAEeTML2Ukh6uGuX4E14q65Ja8cdjF5bAldnP1eE4FBaAwsZ
|
|
71
|
+
G2fOqqrKV03Y85Nw2btedP1AtliQuJZs/Jo/gXxXdc7LrH3McgnpnbTiAncX7yES
|
|
72
|
+
hP6kzQejllqMCIt52HOjxDGWafS7Xw+DKwqmH+Eqy8dcbOuag/1AYlQoKNVK3F5q
|
|
73
|
+
Hh6tEDiMqQcLIibGKteE6iHo4A/bIScbzrhXUYuism42ZYzmc48FMVIH3qy4L84E
|
|
74
|
+
TdAH2gtxw0PAhvRVXp8HP7wfngpzsN/8xOTpeRSbM4+Qbc56G6+Bifmv6sk1ieQb
|
|
75
|
+
NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve
|
|
76
|
+
86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy
|
|
77
|
+
LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA==
|
|
78
|
+
-----END CERTIFICATE-----`,
|
|
79
|
+
`-----BEGIN CERTIFICATE-----
|
|
80
|
+
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
|
|
112
|
+
-----END CERTIFICATE-----`
|
|
113
|
+
];
|
|
36
114
|
// Cache Mechanism
|
|
37
115
|
static isCacheEnabled = false;
|
|
38
116
|
static wordCache = /* @__PURE__ */ new Map();
|
|
@@ -542,7 +620,166 @@ var TDK = class {
|
|
|
542
620
|
}
|
|
543
621
|
}
|
|
544
622
|
static htmlToPlainText(html) {
|
|
545
|
-
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/&
|
|
623
|
+
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();
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
|
|
627
|
+
* supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
|
|
628
|
+
* certificate chain (see the constant's doc comment). Fails closed to
|
|
629
|
+
* `null` on any error — network, TLS, HTTP, or JSON parse.
|
|
630
|
+
*/
|
|
631
|
+
static fetchKubbealtiJson(path2) {
|
|
632
|
+
return new Promise((resolve) => {
|
|
633
|
+
const req = https.request(
|
|
634
|
+
{
|
|
635
|
+
hostname: this.KUBBEALTI_HOST,
|
|
636
|
+
path: path2,
|
|
637
|
+
method: "GET",
|
|
638
|
+
ca: [...tls.rootCertificates, ...this.KUBBEALTI_EXTRA_CA],
|
|
639
|
+
headers: {
|
|
640
|
+
"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"
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
(res) => {
|
|
644
|
+
if (res.statusCode !== 200) {
|
|
645
|
+
res.resume();
|
|
646
|
+
resolve(null);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
let body = "";
|
|
650
|
+
res.on("data", (chunk) => body += chunk);
|
|
651
|
+
res.on("end", () => {
|
|
652
|
+
try {
|
|
653
|
+
resolve(JSON.parse(body));
|
|
654
|
+
} catch {
|
|
655
|
+
resolve(null);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
);
|
|
660
|
+
req.on("error", () => resolve(null));
|
|
661
|
+
req.end();
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Returns Kubbealtı Lugatı ("Misalli Büyük Türkçe Sözlük") entries for a
|
|
666
|
+
* word, scraped from the site's own data API — undocumented, and Kubbealtı
|
|
667
|
+
* Lugatı is a commercial dictionary product, unlike TDK's or Wiktionary's
|
|
668
|
+
* openly-published data, so use this in line with their terms. `anlam` is
|
|
669
|
+
* raw HTML (rich typography markup); use `getKubbealtiMeanings()` for
|
|
670
|
+
* plain text. Returns `null` on any fetch/parse failure, `[]` if the word
|
|
671
|
+
* isn't found.
|
|
672
|
+
*/
|
|
673
|
+
static async getKubbealti(word) {
|
|
674
|
+
if (!word || word.trim() === "")
|
|
675
|
+
return null;
|
|
676
|
+
const data = await this.fetchKubbealtiJson(`/rest/s/${encodeURIComponent(word.trim())}/`);
|
|
677
|
+
if (!data || !Array.isArray(data.content))
|
|
678
|
+
return null;
|
|
679
|
+
return data.content.map((entry) => ({ kelime: entry.kelime, anlam: entry.anlam }));
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Same as `getKubbealti()` but with each entry's `anlam` HTML stripped to
|
|
683
|
+
* plain text via `htmlToPlainText()`.
|
|
684
|
+
*/
|
|
685
|
+
static async getKubbealtiMeanings(word) {
|
|
686
|
+
const entries = await this.getKubbealti(word);
|
|
687
|
+
if (!entries)
|
|
688
|
+
return null;
|
|
689
|
+
return entries.map((e) => this.htmlToPlainText(e.anlam));
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Autocomplete suggestions from Kubbealtı Lugatı's own typeahead endpoint
|
|
693
|
+
* (separate from `getSuggestions()`, which uses TDK's data).
|
|
694
|
+
*/
|
|
695
|
+
static async getKubbealtiSuggestions(prefix) {
|
|
696
|
+
if (!prefix || prefix.trim() === "")
|
|
697
|
+
return [];
|
|
698
|
+
const data = await this.fetchKubbealtiJson(`/rest/word-search/${encodeURIComponent(prefix.trim())}`);
|
|
699
|
+
if (!Array.isArray(data))
|
|
700
|
+
return [];
|
|
701
|
+
return data.map((item) => item.display).filter(Boolean);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Returns the etymology paragraph for a word from Nişanyan Sözlük, scraped
|
|
705
|
+
* from that page's server-rendered `<meta name="description">` tag (the
|
|
706
|
+
* page already puts the full etymology text there for SEO, so no need to
|
|
707
|
+
* parse the site's internal SvelteKit data format). Returns `null` if the
|
|
708
|
+
* word isn't found (the page falls back to a generic site tagline in that
|
|
709
|
+
* case) or the request fails.
|
|
710
|
+
*/
|
|
711
|
+
static async getNisanyan(word) {
|
|
712
|
+
if (!word || word.trim() === "")
|
|
713
|
+
return null;
|
|
714
|
+
try {
|
|
715
|
+
const response = await fetch(
|
|
716
|
+
`https://www.nisanyansozluk.com/kelime/${encodeURIComponent(word.trim().toLocaleLowerCase("tr-TR"))}`,
|
|
717
|
+
{ headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" } }
|
|
718
|
+
);
|
|
719
|
+
if (!response.ok)
|
|
720
|
+
return null;
|
|
721
|
+
const html = await response.text();
|
|
722
|
+
const match = html.match(/<meta name="description" content="([^"]*)"/);
|
|
723
|
+
if (!match)
|
|
724
|
+
return null;
|
|
725
|
+
const description = this.htmlToPlainText(match[1]);
|
|
726
|
+
if (description === "\xC7a\u011Fda\u015F T\xFCrk\xE7enin Etimolojisi")
|
|
727
|
+
return null;
|
|
728
|
+
return description;
|
|
729
|
+
} catch {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Returns the Turkish Wiktionary (`tr.wiktionary.org`) entry for a word,
|
|
735
|
+
* via MediaWiki's official Action API (`action=query&prop=extracts`) — no
|
|
736
|
+
* scraping involved, this is a stable, documented public API. `sections`
|
|
737
|
+
* splits the plain-text extract on its `== Heading ==`/`=== Heading ===`
|
|
738
|
+
* markers (e.g. "Köken", "Söyleniş", "Ad") for convenience; `raw` has the
|
|
739
|
+
* unsplit text. Returns `null` if the page doesn't exist or the request
|
|
740
|
+
* fails.
|
|
741
|
+
*/
|
|
742
|
+
static async getWiktionary(word) {
|
|
743
|
+
if (!word || word.trim() === "")
|
|
744
|
+
return null;
|
|
745
|
+
try {
|
|
746
|
+
const url = `https://tr.wiktionary.org/w/api.php?action=query&prop=extracts&titles=${encodeURIComponent(
|
|
747
|
+
word.trim()
|
|
748
|
+
)}&format=json&explaintext=1&formatversion=2`;
|
|
749
|
+
const response = await fetch(url, { headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" } });
|
|
750
|
+
if (!response.ok)
|
|
751
|
+
return null;
|
|
752
|
+
const data = await response.json();
|
|
753
|
+
const page = data?.query?.pages?.[0];
|
|
754
|
+
if (!page || page.missing || !page.extract)
|
|
755
|
+
return null;
|
|
756
|
+
const raw = page.extract;
|
|
757
|
+
const sections = {};
|
|
758
|
+
const parts = raw.split(/\n(={2,4})\s*(.+?)\s*\1\n/);
|
|
759
|
+
for (let i = 1; i < parts.length; i += 3) {
|
|
760
|
+
const title = parts[i + 1]?.trim();
|
|
761
|
+
const content = parts[i + 2]?.trim();
|
|
762
|
+
if (title)
|
|
763
|
+
sections[title] = content ?? "";
|
|
764
|
+
}
|
|
765
|
+
return { raw, sections };
|
|
766
|
+
} catch {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Convenience filter over `getWiktionary()`: returns just one section's
|
|
772
|
+
* text (e.g. `getWiktionarySection(word, "Köken")` for etymology), matched
|
|
773
|
+
* case-insensitively. Returns `null` if the word or the section isn't found.
|
|
774
|
+
*/
|
|
775
|
+
static async getWiktionarySection(word, sectionName) {
|
|
776
|
+
const entry = await this.getWiktionary(word);
|
|
777
|
+
if (!entry)
|
|
778
|
+
return null;
|
|
779
|
+
const key = Object.keys(entry.sections).find(
|
|
780
|
+
(k) => k.toLocaleLowerCase("tr-TR") === sectionName.trim().toLocaleLowerCase("tr-TR")
|
|
781
|
+
);
|
|
782
|
+
return key ? entry.sections[key] : null;
|
|
546
783
|
}
|
|
547
784
|
/**
|
|
548
785
|
* Returns compound words that contain this word.
|
package/dist/cli.js
CHANGED
|
@@ -55,9 +55,87 @@ var fs = __toESM(require("fs"));
|
|
|
55
55
|
var path = __toESM(require("path"));
|
|
56
56
|
var os = __toESM(require("os"));
|
|
57
57
|
var https = __toESM(require("https"));
|
|
58
|
+
var tls = __toESM(require("tls"));
|
|
58
59
|
var TDK = class {
|
|
59
60
|
static BASE_URL = "https://sozluk.gov.tr";
|
|
60
61
|
static AUDIO_API_HOST = "api.sozluk.gov.tr";
|
|
62
|
+
static KUBBEALTI_HOST = "eski.lugatim.com";
|
|
63
|
+
/**
|
|
64
|
+
* `eski.lugatim.com` (Kubbealtı Lugatı's data API) sends only its leaf
|
|
65
|
+
* certificate during the TLS handshake, omitting the intermediates a
|
|
66
|
+
* correctly configured server would include — a server-side misconfiguration,
|
|
67
|
+
* not something we should paper over by disabling verification. These are
|
|
68
|
+
* the two certificates the server *should* be sending (fetched from the
|
|
69
|
+
* leaf's own Authority Information Access URLs), supplied here so Node can
|
|
70
|
+
* still build a full, properly verified chain up to a root it already
|
|
71
|
+
* trusts (ISRG Root X1). If Let's Encrypt rotates this intermediate, this
|
|
72
|
+
* stops working and every Kubbealtı call fails closed to `null` — same
|
|
73
|
+
* fail-closed contract as the rest of this file's fragile integrations.
|
|
74
|
+
*/
|
|
75
|
+
static KUBBEALTI_EXTRA_CA = [
|
|
76
|
+
`-----BEGIN CERTIFICATE-----
|
|
77
|
+
MIIE2jCCAsKgAwIBAgIQTr0klH4k05SALYSlL9WzGTANBgkqhkiG9w0BAQsFADAu
|
|
78
|
+
MQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZUjAe
|
|
79
|
+
Fw0yNTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYw
|
|
80
|
+
FAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZUjIwggEiMA0GCSqGSIb3
|
|
81
|
+
DQEBAQUAA4IBDwAwggEKAoIBAQDZ0LxwBppqh84luqMerV/eeL/fXQ7mLQQv1Lnp
|
|
82
|
+
WKZbyvGpx6wh6AfnslAnF6ewTkcHA+gSOoBvm3Dfm06AuGiF+KRut4fAcowqnAQQ
|
|
83
|
+
CW98+QPP/eOv/wug7Iyk4NkOxf2I6g2f55T6nJoOTLFcukeRq80JGQEYan+dPFr9
|
|
84
|
+
OGUgQK2hGKgNkW87pappsOAuUJcroYhRt5uUis4qaZireiseu32gzDJNBAiKtsvd
|
|
85
|
+
6HX4v25bpkRNcS/B/Gtc9kVbUpD+2PLPxdei3Tim55k4tfAEXwD2qyiPTxrTNq6l
|
|
86
|
+
N+AMr5g2c1dNqkOTwjxeV6L5lpP1rGiYvLnRaPlOqyZRPW+5AgMBAAGjge4wgesw
|
|
87
|
+
DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMBIGA1UdEwEB/wQI
|
|
88
|
+
MAYBAf8CAQAwHQYDVR0OBBYEFEAVLSZ57TIgnt+ach3WMh+BDIEMMB8GA1UdIwQY
|
|
89
|
+
MBaAFN7nW2DQIm1AKH0/DQH+pLVStFGUMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEF
|
|
90
|
+
BQcwAoYWaHR0cDovL3lyLmkubGVuY3Iub3JnLzATBgNVHSAEDDAKMAgGBmeBDAEC
|
|
91
|
+
ATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veXIuYy5sZW5jci5vcmcvMA0GCSqG
|
|
92
|
+
SIb3DQEBCwUAA4ICAQB0ZUQWZ9/Yn9COEpo+JfecMnB0h0vwDm/M66IqXqw3LoaL
|
|
93
|
+
mx9lZvRTeDIS67PUeI3yCA2W6PKRD0/FE/G57lOmS+Xy5AaaL00ICGOqjNcCaMWW
|
|
94
|
+
8o8nevHOd4i4lqgtznE/28QwlcdJyF8yBiWHpnyjhEpmNWJURgOCOg2xpwRMBCsj
|
|
95
|
+
MScqYPtOhBeuYQvSwAEeTML2Ukh6uGuX4E14q65Ja8cdjF5bAldnP1eE4FBaAwsZ
|
|
96
|
+
G2fOqqrKV03Y85Nw2btedP1AtliQuJZs/Jo/gXxXdc7LrH3McgnpnbTiAncX7yES
|
|
97
|
+
hP6kzQejllqMCIt52HOjxDGWafS7Xw+DKwqmH+Eqy8dcbOuag/1AYlQoKNVK3F5q
|
|
98
|
+
Hh6tEDiMqQcLIibGKteE6iHo4A/bIScbzrhXUYuism42ZYzmc48FMVIH3qy4L84E
|
|
99
|
+
TdAH2gtxw0PAhvRVXp8HP7wfngpzsN/8xOTpeRSbM4+Qbc56G6+Bifmv6sk1ieQb
|
|
100
|
+
NA3wJdl4DDUuQSV8hBgx6zoI1ZSGORprDFux7c6rhc77QZMSRrEgomBeklervEve
|
|
101
|
+
86ylWmZ3WWHV6RLMi8xNvjd71r4EPIGgY7BZU/VPBkq+uA7Gb6mbJnFgV43uh3xy
|
|
102
|
+
LRFgxIAphIukwTGSMZZR+AI+Qnp0BYTWovHXozOf3H8r6hozEoT02JHn0AeTfA==
|
|
103
|
+
-----END CERTIFICATE-----`,
|
|
104
|
+
`-----BEGIN CERTIFICATE-----
|
|
105
|
+
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
|
|
137
|
+
-----END CERTIFICATE-----`
|
|
138
|
+
];
|
|
61
139
|
// Cache Mechanism
|
|
62
140
|
static isCacheEnabled = false;
|
|
63
141
|
static wordCache = /* @__PURE__ */ new Map();
|
|
@@ -567,7 +645,166 @@ var TDK = class {
|
|
|
567
645
|
}
|
|
568
646
|
}
|
|
569
647
|
static htmlToPlainText(html) {
|
|
570
|
-
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/&
|
|
648
|
+
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();
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* GETs a JSON path from Kubbealtı Lugatı's data API (`eski.lugatim.com`),
|
|
652
|
+
* supplying `KUBBEALTI_EXTRA_CA` to work around that host's incomplete
|
|
653
|
+
* certificate chain (see the constant's doc comment). Fails closed to
|
|
654
|
+
* `null` on any error — network, TLS, HTTP, or JSON parse.
|
|
655
|
+
*/
|
|
656
|
+
static fetchKubbealtiJson(path2) {
|
|
657
|
+
return new Promise((resolve) => {
|
|
658
|
+
const req = https.request(
|
|
659
|
+
{
|
|
660
|
+
hostname: this.KUBBEALTI_HOST,
|
|
661
|
+
path: path2,
|
|
662
|
+
method: "GET",
|
|
663
|
+
ca: [...tls.rootCertificates, ...this.KUBBEALTI_EXTRA_CA],
|
|
664
|
+
headers: {
|
|
665
|
+
"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"
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
(res) => {
|
|
669
|
+
if (res.statusCode !== 200) {
|
|
670
|
+
res.resume();
|
|
671
|
+
resolve(null);
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
let body = "";
|
|
675
|
+
res.on("data", (chunk) => body += chunk);
|
|
676
|
+
res.on("end", () => {
|
|
677
|
+
try {
|
|
678
|
+
resolve(JSON.parse(body));
|
|
679
|
+
} catch {
|
|
680
|
+
resolve(null);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
req.on("error", () => resolve(null));
|
|
686
|
+
req.end();
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Returns Kubbealtı Lugatı ("Misalli Büyük Türkçe Sözlük") entries for a
|
|
691
|
+
* word, scraped from the site's own data API — undocumented, and Kubbealtı
|
|
692
|
+
* Lugatı is a commercial dictionary product, unlike TDK's or Wiktionary's
|
|
693
|
+
* openly-published data, so use this in line with their terms. `anlam` is
|
|
694
|
+
* raw HTML (rich typography markup); use `getKubbealtiMeanings()` for
|
|
695
|
+
* plain text. Returns `null` on any fetch/parse failure, `[]` if the word
|
|
696
|
+
* isn't found.
|
|
697
|
+
*/
|
|
698
|
+
static async getKubbealti(word2) {
|
|
699
|
+
if (!word2 || word2.trim() === "")
|
|
700
|
+
return null;
|
|
701
|
+
const data = await this.fetchKubbealtiJson(`/rest/s/${encodeURIComponent(word2.trim())}/`);
|
|
702
|
+
if (!data || !Array.isArray(data.content))
|
|
703
|
+
return null;
|
|
704
|
+
return data.content.map((entry) => ({ kelime: entry.kelime, anlam: entry.anlam }));
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Same as `getKubbealti()` but with each entry's `anlam` HTML stripped to
|
|
708
|
+
* plain text via `htmlToPlainText()`.
|
|
709
|
+
*/
|
|
710
|
+
static async getKubbealtiMeanings(word2) {
|
|
711
|
+
const entries = await this.getKubbealti(word2);
|
|
712
|
+
if (!entries)
|
|
713
|
+
return null;
|
|
714
|
+
return entries.map((e) => this.htmlToPlainText(e.anlam));
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Autocomplete suggestions from Kubbealtı Lugatı's own typeahead endpoint
|
|
718
|
+
* (separate from `getSuggestions()`, which uses TDK's data).
|
|
719
|
+
*/
|
|
720
|
+
static async getKubbealtiSuggestions(prefix) {
|
|
721
|
+
if (!prefix || prefix.trim() === "")
|
|
722
|
+
return [];
|
|
723
|
+
const data = await this.fetchKubbealtiJson(`/rest/word-search/${encodeURIComponent(prefix.trim())}`);
|
|
724
|
+
if (!Array.isArray(data))
|
|
725
|
+
return [];
|
|
726
|
+
return data.map((item) => item.display).filter(Boolean);
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Returns the etymology paragraph for a word from Nişanyan Sözlük, scraped
|
|
730
|
+
* from that page's server-rendered `<meta name="description">` tag (the
|
|
731
|
+
* page already puts the full etymology text there for SEO, so no need to
|
|
732
|
+
* parse the site's internal SvelteKit data format). Returns `null` if the
|
|
733
|
+
* word isn't found (the page falls back to a generic site tagline in that
|
|
734
|
+
* case) or the request fails.
|
|
735
|
+
*/
|
|
736
|
+
static async getNisanyan(word2) {
|
|
737
|
+
if (!word2 || word2.trim() === "")
|
|
738
|
+
return null;
|
|
739
|
+
try {
|
|
740
|
+
const response = await fetch(
|
|
741
|
+
`https://www.nisanyansozluk.com/kelime/${encodeURIComponent(word2.trim().toLocaleLowerCase("tr-TR"))}`,
|
|
742
|
+
{ headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" } }
|
|
743
|
+
);
|
|
744
|
+
if (!response.ok)
|
|
745
|
+
return null;
|
|
746
|
+
const html = await response.text();
|
|
747
|
+
const match = html.match(/<meta name="description" content="([^"]*)"/);
|
|
748
|
+
if (!match)
|
|
749
|
+
return null;
|
|
750
|
+
const description = this.htmlToPlainText(match[1]);
|
|
751
|
+
if (description === "\xC7a\u011Fda\u015F T\xFCrk\xE7enin Etimolojisi")
|
|
752
|
+
return null;
|
|
753
|
+
return description;
|
|
754
|
+
} catch {
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Returns the Turkish Wiktionary (`tr.wiktionary.org`) entry for a word,
|
|
760
|
+
* via MediaWiki's official Action API (`action=query&prop=extracts`) — no
|
|
761
|
+
* scraping involved, this is a stable, documented public API. `sections`
|
|
762
|
+
* splits the plain-text extract on its `== Heading ==`/`=== Heading ===`
|
|
763
|
+
* markers (e.g. "Köken", "Söyleniş", "Ad") for convenience; `raw` has the
|
|
764
|
+
* unsplit text. Returns `null` if the page doesn't exist or the request
|
|
765
|
+
* fails.
|
|
766
|
+
*/
|
|
767
|
+
static async getWiktionary(word2) {
|
|
768
|
+
if (!word2 || word2.trim() === "")
|
|
769
|
+
return null;
|
|
770
|
+
try {
|
|
771
|
+
const url = `https://tr.wiktionary.org/w/api.php?action=query&prop=extracts&titles=${encodeURIComponent(
|
|
772
|
+
word2.trim()
|
|
773
|
+
)}&format=json&explaintext=1&formatversion=2`;
|
|
774
|
+
const response = await fetch(url, { headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" } });
|
|
775
|
+
if (!response.ok)
|
|
776
|
+
return null;
|
|
777
|
+
const data = await response.json();
|
|
778
|
+
const page = data?.query?.pages?.[0];
|
|
779
|
+
if (!page || page.missing || !page.extract)
|
|
780
|
+
return null;
|
|
781
|
+
const raw = page.extract;
|
|
782
|
+
const sections = {};
|
|
783
|
+
const parts = raw.split(/\n(={2,4})\s*(.+?)\s*\1\n/);
|
|
784
|
+
for (let i = 1; i < parts.length; i += 3) {
|
|
785
|
+
const title = parts[i + 1]?.trim();
|
|
786
|
+
const content = parts[i + 2]?.trim();
|
|
787
|
+
if (title)
|
|
788
|
+
sections[title] = content ?? "";
|
|
789
|
+
}
|
|
790
|
+
return { raw, sections };
|
|
791
|
+
} catch {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Convenience filter over `getWiktionary()`: returns just one section's
|
|
797
|
+
* text (e.g. `getWiktionarySection(word, "Köken")` for etymology), matched
|
|
798
|
+
* case-insensitively. Returns `null` if the word or the section isn't found.
|
|
799
|
+
*/
|
|
800
|
+
static async getWiktionarySection(word2, sectionName) {
|
|
801
|
+
const entry = await this.getWiktionary(word2);
|
|
802
|
+
if (!entry)
|
|
803
|
+
return null;
|
|
804
|
+
const key = Object.keys(entry.sections).find(
|
|
805
|
+
(k) => k.toLocaleLowerCase("tr-TR") === sectionName.trim().toLocaleLowerCase("tr-TR")
|
|
806
|
+
);
|
|
807
|
+
return key ? entry.sections[key] : null;
|
|
571
808
|
}
|
|
572
809
|
/**
|
|
573
810
|
* Returns compound words that contain this word.
|
|
@@ -818,7 +1055,10 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
818
1055
|
"kural",
|
|
819
1056
|
"karsilastir",
|
|
820
1057
|
"analiz",
|
|
821
|
-
"oneri"
|
|
1058
|
+
"oneri",
|
|
1059
|
+
"kubbealti",
|
|
1060
|
+
"nisanyan",
|
|
1061
|
+
"viki"
|
|
822
1062
|
]);
|
|
823
1063
|
var command = args[0];
|
|
824
1064
|
var word = args.slice(1).join(" ");
|
|
@@ -844,7 +1084,7 @@ async function run() {
|
|
|
844
1084
|
if (!command || command === "--help" || command === "-h") {
|
|
845
1085
|
console.log("Kullan\u0131m: tdk [komut] <kelime> [--json]");
|
|
846
1086
|
console.log(
|
|
847
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri"
|
|
1087
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, kubbealti, nisanyan, viki"
|
|
848
1088
|
);
|
|
849
1089
|
console.log("Not: Komut belirtilmezse do\u011Frudan kelime anlam\u0131 aran\u0131r (\xF6rn: tdk selam)");
|
|
850
1090
|
process.exit(command ? 0 : 1);
|
|
@@ -1047,6 +1287,46 @@ async function run() {
|
|
|
1047
1287
|
});
|
|
1048
1288
|
break;
|
|
1049
1289
|
}
|
|
1290
|
+
case "kubbealti": {
|
|
1291
|
+
if (!word)
|
|
1292
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
1293
|
+
const meanings = await TDK.getKubbealtiMeanings(word);
|
|
1294
|
+
printResult(meanings, () => {
|
|
1295
|
+
if (!meanings) {
|
|
1296
|
+
console.log("Kubbealt\u0131 Lugat\u0131'na ula\u015F\u0131lamad\u0131.");
|
|
1297
|
+
} else if (meanings.length === 0) {
|
|
1298
|
+
console.log("Sonu\xE7 bulunamad\u0131.");
|
|
1299
|
+
} else {
|
|
1300
|
+
meanings.forEach((m, i) => console.log(`${i + 1}. ${m}`));
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
case "nisanyan": {
|
|
1306
|
+
if (!word)
|
|
1307
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
1308
|
+
const origin = await TDK.getNisanyan(word);
|
|
1309
|
+
printResult(origin, () => console.log(origin ?? "Sonu\xE7 bulunamad\u0131."));
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
case "viki": {
|
|
1313
|
+
if (!word)
|
|
1314
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
1315
|
+
const entry = await TDK.getWiktionary(word);
|
|
1316
|
+
printResult(entry, () => {
|
|
1317
|
+
if (!entry) {
|
|
1318
|
+
console.log("Sonu\xE7 bulunamad\u0131.");
|
|
1319
|
+
} else {
|
|
1320
|
+
for (const [title, content] of Object.entries(entry.sections)) {
|
|
1321
|
+
if (content)
|
|
1322
|
+
console.log(`-- ${title} --
|
|
1323
|
+
${content}
|
|
1324
|
+
`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
break;
|
|
1329
|
+
}
|
|
1050
1330
|
default:
|
|
1051
1331
|
printError("Bilinmeyen komut.");
|
|
1052
1332
|
}
|
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-SLNXKZKR.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
var rawArgs = process.argv.slice(2);
|
|
@@ -24,7 +24,10 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
24
24
|
"kural",
|
|
25
25
|
"karsilastir",
|
|
26
26
|
"analiz",
|
|
27
|
-
"oneri"
|
|
27
|
+
"oneri",
|
|
28
|
+
"kubbealti",
|
|
29
|
+
"nisanyan",
|
|
30
|
+
"viki"
|
|
28
31
|
]);
|
|
29
32
|
var command = args[0];
|
|
30
33
|
var word = args.slice(1).join(" ");
|
|
@@ -50,7 +53,7 @@ async function run() {
|
|
|
50
53
|
if (!command || command === "--help" || command === "-h") {
|
|
51
54
|
console.log("Kullan\u0131m: tdk [komut] <kelime> [--json]");
|
|
52
55
|
console.log(
|
|
53
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri"
|
|
56
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, kubbealti, nisanyan, viki"
|
|
54
57
|
);
|
|
55
58
|
console.log("Not: Komut belirtilmezse do\u011Frudan kelime anlam\u0131 aran\u0131r (\xF6rn: tdk selam)");
|
|
56
59
|
process.exit(command ? 0 : 1);
|
|
@@ -253,6 +256,46 @@ async function run() {
|
|
|
253
256
|
});
|
|
254
257
|
break;
|
|
255
258
|
}
|
|
259
|
+
case "kubbealti": {
|
|
260
|
+
if (!word)
|
|
261
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
262
|
+
const meanings = await TDK.getKubbealtiMeanings(word);
|
|
263
|
+
printResult(meanings, () => {
|
|
264
|
+
if (!meanings) {
|
|
265
|
+
console.log("Kubbealt\u0131 Lugat\u0131'na ula\u015F\u0131lamad\u0131.");
|
|
266
|
+
} else if (meanings.length === 0) {
|
|
267
|
+
console.log("Sonu\xE7 bulunamad\u0131.");
|
|
268
|
+
} else {
|
|
269
|
+
meanings.forEach((m, i) => console.log(`${i + 1}. ${m}`));
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
case "nisanyan": {
|
|
275
|
+
if (!word)
|
|
276
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
277
|
+
const origin = await TDK.getNisanyan(word);
|
|
278
|
+
printResult(origin, () => console.log(origin ?? "Sonu\xE7 bulunamad\u0131."));
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case "viki": {
|
|
282
|
+
if (!word)
|
|
283
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
284
|
+
const entry = await TDK.getWiktionary(word);
|
|
285
|
+
printResult(entry, () => {
|
|
286
|
+
if (!entry) {
|
|
287
|
+
console.log("Sonu\xE7 bulunamad\u0131.");
|
|
288
|
+
} else {
|
|
289
|
+
for (const [title, content] of Object.entries(entry.sections)) {
|
|
290
|
+
if (content)
|
|
291
|
+
console.log(`-- ${title} --
|
|
292
|
+
${content}
|
|
293
|
+
`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
256
299
|
default:
|
|
257
300
|
printError("Bilinmeyen komut.");
|
|
258
301
|
}
|