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 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(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&quot;/gi, '"').replace(/&#39;|&rsquo;/gi, "'").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
623
+ 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();
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(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&quot;/gi, '"').replace(/&#39;|&rsquo;/gi, "'").replace(/[ \t]+/g, " ").replace(/[ \t]*\n[ \t]*/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
648
+ 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();
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-SNY3KUCF.mjs";
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
  }