tdk-api-wrapper 1.1.0 → 1.2.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 +2 -1
- package/dist/{chunk-P3GX7I53.mjs → chunk-2TA5PMVZ.mjs} +50 -13
- package/dist/cli.js +66 -15
- package/dist/cli.mjs +17 -3
- package/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +50 -13
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/cli.ts +15 -1
- package/src/tdk.ts +51 -14
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ tdk kurallar
|
|
|
34
34
|
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
|
+
tdk oneri kale
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
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):
|
|
@@ -81,7 +82,7 @@ Aşağıdaki metotlar `TDK` sınıfı üzerinden statik olarak erişilebilir dur
|
|
|
81
82
|
- **`TDK.analyzeText(text)`**: Bir metindeki (Türkçe bağlaçlar/edatlar hariç) her benzersiz kelimeyi tek tek arayıp ilk anlamını ve kökenini döner.
|
|
82
83
|
|
|
83
84
|
### 4. Yardımcı Metotlar
|
|
84
|
-
- **`TDK.getSuggestions(prefix)`**:
|
|
85
|
+
- **`TDK.getSuggestions(prefix)`**: TDK'nin ~81 bin kelimelik tam madde listesi üzerinden önek bazlı otomatik tamamlama önerileri döner (ilk çağrıda listeyi indirip önbelleğe alır, sonraki çağrılar anlıktır).
|
|
85
86
|
- **`TDK.getAudioUrl(word)`**: TDK'nin bu kelime için gerçekten bir ses kaydı varsa doğrudan indirme URL'sini döner, yoksa `null`. `downloadAudio(word, destPath)` ile cihazınıza indirebilirsiniz.
|
|
86
87
|
- **`TDK.getDailyContent()`**: TDK anasayfasında yer alan "Günün Kelimesi, Atasözü ve Kuralı" içeriklerini çeker.
|
|
87
88
|
- **`TDK.getWordOfTheDay()`**: `getDailyContent()`'in üzerine ince bir katman; günün kelimesini ve tüm anlamlarını `{ word, meanings }` şeklinde döner.
|
|
@@ -119,23 +119,60 @@ var TDK = class {
|
|
|
119
119
|
return meanings;
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
123
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
124
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
125
|
+
* instead bundled directly into its main JS asset as a
|
|
126
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
127
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
128
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
129
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
130
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
131
|
+
*/
|
|
132
|
+
static async fetchAutocompleteData() {
|
|
133
|
+
try {
|
|
134
|
+
const homeResponse = await fetch(`${this.BASE_URL}/`, {
|
|
135
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
136
|
+
});
|
|
137
|
+
if (!homeResponse.ok)
|
|
138
|
+
return [];
|
|
139
|
+
const html = await homeResponse.text();
|
|
140
|
+
const scriptMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
|
|
141
|
+
if (!scriptMatch)
|
|
142
|
+
return [];
|
|
143
|
+
const bundleResponse = await fetch(`${this.BASE_URL}${scriptMatch[1]}`, {
|
|
144
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
145
|
+
});
|
|
146
|
+
if (!bundleResponse.ok)
|
|
147
|
+
return [];
|
|
148
|
+
const bundleJs = await bundleResponse.text();
|
|
149
|
+
const startMarker = 'JSON.parse(`[{"madde":';
|
|
150
|
+
const startIdx = bundleJs.indexOf(startMarker);
|
|
151
|
+
if (startIdx === -1)
|
|
152
|
+
return [];
|
|
153
|
+
const jsonStart = startIdx + "JSON.parse(".length + 1;
|
|
154
|
+
const jsonEnd = bundleJs.indexOf("`)", jsonStart);
|
|
155
|
+
if (jsonEnd === -1)
|
|
156
|
+
return [];
|
|
157
|
+
const data = JSON.parse(bundleJs.slice(jsonStart, jsonEnd));
|
|
158
|
+
return data.map((item) => item.madde).filter(Boolean);
|
|
159
|
+
} catch {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
165
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
166
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
167
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
123
168
|
*/
|
|
124
169
|
static async getSuggestions(prefix) {
|
|
170
|
+
if (!prefix || prefix.trim() === "")
|
|
171
|
+
return [];
|
|
125
172
|
if (this.autocompleteCache.length === 0) {
|
|
126
|
-
|
|
127
|
-
const response = await fetch(`${this.BASE_URL}/autocomplete.json`, {
|
|
128
|
-
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
129
|
-
});
|
|
130
|
-
if (response.ok) {
|
|
131
|
-
const data = await response.json();
|
|
132
|
-
this.autocompleteCache = data.map((item) => item.madde);
|
|
133
|
-
}
|
|
134
|
-
} catch (e) {
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
173
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
137
174
|
}
|
|
138
|
-
const cleanPrefix = prefix.toLocaleLowerCase("tr-TR");
|
|
175
|
+
const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
|
|
139
176
|
return this.autocompleteCache.filter((w) => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix)).slice(0, 10);
|
|
140
177
|
}
|
|
141
178
|
/**
|
package/dist/cli.js
CHANGED
|
@@ -144,23 +144,60 @@ var TDK = class {
|
|
|
144
144
|
return meanings;
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
148
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
149
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
150
|
+
* instead bundled directly into its main JS asset as a
|
|
151
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
152
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
153
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
154
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
155
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
156
|
+
*/
|
|
157
|
+
static async fetchAutocompleteData() {
|
|
158
|
+
try {
|
|
159
|
+
const homeResponse = await fetch(`${this.BASE_URL}/`, {
|
|
160
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
161
|
+
});
|
|
162
|
+
if (!homeResponse.ok)
|
|
163
|
+
return [];
|
|
164
|
+
const html = await homeResponse.text();
|
|
165
|
+
const scriptMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
|
|
166
|
+
if (!scriptMatch)
|
|
167
|
+
return [];
|
|
168
|
+
const bundleResponse = await fetch(`${this.BASE_URL}${scriptMatch[1]}`, {
|
|
169
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
170
|
+
});
|
|
171
|
+
if (!bundleResponse.ok)
|
|
172
|
+
return [];
|
|
173
|
+
const bundleJs = await bundleResponse.text();
|
|
174
|
+
const startMarker = 'JSON.parse(`[{"madde":';
|
|
175
|
+
const startIdx = bundleJs.indexOf(startMarker);
|
|
176
|
+
if (startIdx === -1)
|
|
177
|
+
return [];
|
|
178
|
+
const jsonStart = startIdx + "JSON.parse(".length + 1;
|
|
179
|
+
const jsonEnd = bundleJs.indexOf("`)", jsonStart);
|
|
180
|
+
if (jsonEnd === -1)
|
|
181
|
+
return [];
|
|
182
|
+
const data = JSON.parse(bundleJs.slice(jsonStart, jsonEnd));
|
|
183
|
+
return data.map((item) => item.madde).filter(Boolean);
|
|
184
|
+
} catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
190
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
191
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
192
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
148
193
|
*/
|
|
149
194
|
static async getSuggestions(prefix) {
|
|
195
|
+
if (!prefix || prefix.trim() === "")
|
|
196
|
+
return [];
|
|
150
197
|
if (this.autocompleteCache.length === 0) {
|
|
151
|
-
|
|
152
|
-
const response = await fetch(`${this.BASE_URL}/autocomplete.json`, {
|
|
153
|
-
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
154
|
-
});
|
|
155
|
-
if (response.ok) {
|
|
156
|
-
const data = await response.json();
|
|
157
|
-
this.autocompleteCache = data.map((item) => item.madde);
|
|
158
|
-
}
|
|
159
|
-
} catch (e) {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
198
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
162
199
|
}
|
|
163
|
-
const cleanPrefix = prefix.toLocaleLowerCase("tr-TR");
|
|
200
|
+
const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
|
|
164
201
|
return this.autocompleteCache.filter((w) => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix)).slice(0, 10);
|
|
165
202
|
}
|
|
166
203
|
/**
|
|
@@ -741,7 +778,8 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
741
778
|
"kurallar",
|
|
742
779
|
"kural",
|
|
743
780
|
"karsilastir",
|
|
744
|
-
"analiz"
|
|
781
|
+
"analiz",
|
|
782
|
+
"oneri"
|
|
745
783
|
]);
|
|
746
784
|
var command = args[0];
|
|
747
785
|
var word = args.slice(1).join(" ");
|
|
@@ -767,7 +805,7 @@ async function run() {
|
|
|
767
805
|
if (!command || command === "--help" || command === "-h") {
|
|
768
806
|
console.log("Kullan\u0131m: tdk [komut] <kelime> [--json]");
|
|
769
807
|
console.log(
|
|
770
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz"
|
|
808
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri"
|
|
771
809
|
);
|
|
772
810
|
console.log("Not: Komut belirtilmezse do\u011Frudan kelime anlam\u0131 aran\u0131r (\xF6rn: tdk selam)");
|
|
773
811
|
process.exit(command ? 0 : 1);
|
|
@@ -957,6 +995,19 @@ async function run() {
|
|
|
957
995
|
});
|
|
958
996
|
break;
|
|
959
997
|
}
|
|
998
|
+
case "oneri": {
|
|
999
|
+
if (!word)
|
|
1000
|
+
throw new Error("\xD6nek belirtmelisiniz.");
|
|
1001
|
+
const suggestions = await TDK.getSuggestions(word);
|
|
1002
|
+
printResult(suggestions, () => {
|
|
1003
|
+
if (suggestions.length === 0) {
|
|
1004
|
+
console.log("\xD6neri bulunamad\u0131.");
|
|
1005
|
+
} else {
|
|
1006
|
+
suggestions.forEach((s, i) => console.log(`${i + 1}. ${s}`));
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
960
1011
|
default:
|
|
961
1012
|
printError("Bilinmeyen komut.");
|
|
962
1013
|
}
|
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-2TA5PMVZ.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
var rawArgs = process.argv.slice(2);
|
|
@@ -23,7 +23,8 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
23
23
|
"kurallar",
|
|
24
24
|
"kural",
|
|
25
25
|
"karsilastir",
|
|
26
|
-
"analiz"
|
|
26
|
+
"analiz",
|
|
27
|
+
"oneri"
|
|
27
28
|
]);
|
|
28
29
|
var command = args[0];
|
|
29
30
|
var word = args.slice(1).join(" ");
|
|
@@ -49,7 +50,7 @@ async function run() {
|
|
|
49
50
|
if (!command || command === "--help" || command === "-h") {
|
|
50
51
|
console.log("Kullan\u0131m: tdk [komut] <kelime> [--json]");
|
|
51
52
|
console.log(
|
|
52
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz"
|
|
53
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri"
|
|
53
54
|
);
|
|
54
55
|
console.log("Not: Komut belirtilmezse do\u011Frudan kelime anlam\u0131 aran\u0131r (\xF6rn: tdk selam)");
|
|
55
56
|
process.exit(command ? 0 : 1);
|
|
@@ -239,6 +240,19 @@ async function run() {
|
|
|
239
240
|
});
|
|
240
241
|
break;
|
|
241
242
|
}
|
|
243
|
+
case "oneri": {
|
|
244
|
+
if (!word)
|
|
245
|
+
throw new Error("\xD6nek belirtmelisiniz.");
|
|
246
|
+
const suggestions = await TDK.getSuggestions(word);
|
|
247
|
+
printResult(suggestions, () => {
|
|
248
|
+
if (suggestions.length === 0) {
|
|
249
|
+
console.log("\xD6neri bulunamad\u0131.");
|
|
250
|
+
} else {
|
|
251
|
+
suggestions.forEach((s, i) => console.log(`${i + 1}. ${s}`));
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
242
256
|
default:
|
|
243
257
|
printError("Bilinmeyen komut.");
|
|
244
258
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -158,7 +158,22 @@ declare class TDK {
|
|
|
158
158
|
*/
|
|
159
159
|
static getMeanings(word: string): Promise<string[]>;
|
|
160
160
|
/**
|
|
161
|
-
*
|
|
161
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
162
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
163
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
164
|
+
* instead bundled directly into its main JS asset as a
|
|
165
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
166
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
167
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
168
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
169
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
170
|
+
*/
|
|
171
|
+
private static fetchAutocompleteData;
|
|
172
|
+
/**
|
|
173
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
174
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
175
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
176
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
162
177
|
*/
|
|
163
178
|
static getSuggestions(prefix: string): Promise<string[]>;
|
|
164
179
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -158,7 +158,22 @@ declare class TDK {
|
|
|
158
158
|
*/
|
|
159
159
|
static getMeanings(word: string): Promise<string[]>;
|
|
160
160
|
/**
|
|
161
|
-
*
|
|
161
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
162
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
163
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
164
|
+
* instead bundled directly into its main JS asset as a
|
|
165
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
166
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
167
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
168
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
169
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
170
|
+
*/
|
|
171
|
+
private static fetchAutocompleteData;
|
|
172
|
+
/**
|
|
173
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
174
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
175
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
176
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
162
177
|
*/
|
|
163
178
|
static getSuggestions(prefix: string): Promise<string[]>;
|
|
164
179
|
/**
|
package/dist/index.js
CHANGED
|
@@ -158,23 +158,60 @@ var TDK = class {
|
|
|
158
158
|
return meanings;
|
|
159
159
|
}
|
|
160
160
|
/**
|
|
161
|
-
*
|
|
161
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
162
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
163
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
164
|
+
* instead bundled directly into its main JS asset as a
|
|
165
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
166
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
167
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
168
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
169
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
170
|
+
*/
|
|
171
|
+
static async fetchAutocompleteData() {
|
|
172
|
+
try {
|
|
173
|
+
const homeResponse = await fetch(`${this.BASE_URL}/`, {
|
|
174
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
175
|
+
});
|
|
176
|
+
if (!homeResponse.ok)
|
|
177
|
+
return [];
|
|
178
|
+
const html = await homeResponse.text();
|
|
179
|
+
const scriptMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
|
|
180
|
+
if (!scriptMatch)
|
|
181
|
+
return [];
|
|
182
|
+
const bundleResponse = await fetch(`${this.BASE_URL}${scriptMatch[1]}`, {
|
|
183
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
184
|
+
});
|
|
185
|
+
if (!bundleResponse.ok)
|
|
186
|
+
return [];
|
|
187
|
+
const bundleJs = await bundleResponse.text();
|
|
188
|
+
const startMarker = 'JSON.parse(`[{"madde":';
|
|
189
|
+
const startIdx = bundleJs.indexOf(startMarker);
|
|
190
|
+
if (startIdx === -1)
|
|
191
|
+
return [];
|
|
192
|
+
const jsonStart = startIdx + "JSON.parse(".length + 1;
|
|
193
|
+
const jsonEnd = bundleJs.indexOf("`)", jsonStart);
|
|
194
|
+
if (jsonEnd === -1)
|
|
195
|
+
return [];
|
|
196
|
+
const data = JSON.parse(bundleJs.slice(jsonStart, jsonEnd));
|
|
197
|
+
return data.map((item) => item.madde).filter(Boolean);
|
|
198
|
+
} catch {
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
204
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
205
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
206
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
162
207
|
*/
|
|
163
208
|
static async getSuggestions(prefix) {
|
|
209
|
+
if (!prefix || prefix.trim() === "")
|
|
210
|
+
return [];
|
|
164
211
|
if (this.autocompleteCache.length === 0) {
|
|
165
|
-
|
|
166
|
-
const response = await fetch(`${this.BASE_URL}/autocomplete.json`, {
|
|
167
|
-
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" }
|
|
168
|
-
});
|
|
169
|
-
if (response.ok) {
|
|
170
|
-
const data = await response.json();
|
|
171
|
-
this.autocompleteCache = data.map((item) => item.madde);
|
|
172
|
-
}
|
|
173
|
-
} catch (e) {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
212
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
176
213
|
}
|
|
177
|
-
const cleanPrefix = prefix.toLocaleLowerCase("tr-TR");
|
|
214
|
+
const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
|
|
178
215
|
return this.autocompleteCache.filter((w) => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix)).slice(0, 10);
|
|
179
216
|
}
|
|
180
217
|
/**
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -21,6 +21,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
21
21
|
"kural",
|
|
22
22
|
"karsilastir",
|
|
23
23
|
"analiz",
|
|
24
|
+
"oneri",
|
|
24
25
|
]);
|
|
25
26
|
|
|
26
27
|
let command = args[0];
|
|
@@ -51,7 +52,7 @@ async function run() {
|
|
|
51
52
|
if (!command || command === "--help" || command === "-h") {
|
|
52
53
|
console.log("Kullanım: tdk [komut] <kelime> [--json]");
|
|
53
54
|
console.log(
|
|
54
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz"
|
|
55
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri"
|
|
55
56
|
);
|
|
56
57
|
console.log("Not: Komut belirtilmezse doğrudan kelime anlamı aranır (örn: tdk selam)");
|
|
57
58
|
process.exit(command ? 0 : 1);
|
|
@@ -245,6 +246,19 @@ async function run() {
|
|
|
245
246
|
break;
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
case "oneri": {
|
|
250
|
+
if (!word) throw new Error("Önek belirtmelisiniz.");
|
|
251
|
+
const suggestions = await TDK.getSuggestions(word);
|
|
252
|
+
printResult(suggestions, () => {
|
|
253
|
+
if (suggestions.length === 0) {
|
|
254
|
+
console.log("Öneri bulunamadı.");
|
|
255
|
+
} else {
|
|
256
|
+
suggestions.forEach((s, i) => console.log(`${i + 1}. ${s}`));
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
default:
|
|
249
263
|
printError("Bilinmeyen komut.");
|
|
250
264
|
}
|
package/src/tdk.ts
CHANGED
|
@@ -119,24 +119,61 @@ export class TDK {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* `sozluk.gov.tr`'s dedicated `/autocomplete.json` (and `/data/autocomplete.json`)
|
|
123
|
+
* routes no longer serve JSON — they fall through to the SPA's `index.html`.
|
|
124
|
+
* The full ~81k-word headword list the site's own autocomplete UI uses is
|
|
125
|
+
* instead bundled directly into its main JS asset as a
|
|
126
|
+
* `JSON.parse(\`[{"madde":"..."}]\`)` literal, so this fetches the home
|
|
127
|
+
* page to find that asset's current hashed filename, downloads it (a few
|
|
128
|
+
* MB, only once per process), and extracts the literal out of it. Fragile
|
|
129
|
+
* scraping of an implementation detail — if TDK's build stops embedding
|
|
130
|
+
* this, this fails closed to `[]` rather than throwing.
|
|
131
|
+
*/
|
|
132
|
+
private static async fetchAutocompleteData(): Promise<string[]> {
|
|
133
|
+
try {
|
|
134
|
+
const homeResponse = await fetch(`${this.BASE_URL}/`, {
|
|
135
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" },
|
|
136
|
+
});
|
|
137
|
+
if (!homeResponse.ok) return [];
|
|
138
|
+
const html = await homeResponse.text();
|
|
139
|
+
|
|
140
|
+
const scriptMatch = html.match(/src="(\/assets\/index-[^"]+\.js)"/);
|
|
141
|
+
if (!scriptMatch) return [];
|
|
142
|
+
|
|
143
|
+
const bundleResponse = await fetch(`${this.BASE_URL}${scriptMatch[1]}`, {
|
|
144
|
+
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" },
|
|
145
|
+
});
|
|
146
|
+
if (!bundleResponse.ok) return [];
|
|
147
|
+
const bundleJs = await bundleResponse.text();
|
|
148
|
+
|
|
149
|
+
const startMarker = 'JSON.parse(`[{"madde":';
|
|
150
|
+
const startIdx = bundleJs.indexOf(startMarker);
|
|
151
|
+
if (startIdx === -1) return [];
|
|
152
|
+
const jsonStart = startIdx + "JSON.parse(".length + 1;
|
|
153
|
+
const jsonEnd = bundleJs.indexOf("`)", jsonStart);
|
|
154
|
+
if (jsonEnd === -1) return [];
|
|
155
|
+
|
|
156
|
+
const data = JSON.parse(bundleJs.slice(jsonStart, jsonEnd)) as { madde: string }[];
|
|
157
|
+
return data.map((item) => item.madde).filter(Boolean);
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
165
|
+
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
166
|
+
* and cached once per process regardless of `enableCache()` — the same
|
|
167
|
+
* caching behavior as before — and only cleared by `clearCache()`.
|
|
123
168
|
*/
|
|
124
169
|
public static async getSuggestions(prefix: string): Promise<string[]> {
|
|
170
|
+
if (!prefix || prefix.trim() === "") return [];
|
|
171
|
+
|
|
125
172
|
if (this.autocompleteCache.length === 0) {
|
|
126
|
-
|
|
127
|
-
const response = await fetch(`${this.BASE_URL}/autocomplete.json`, {
|
|
128
|
-
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" },
|
|
129
|
-
});
|
|
130
|
-
if (response.ok) {
|
|
131
|
-
const data = await response.json() as { madde: string }[];
|
|
132
|
-
this.autocompleteCache = data.map(item => item.madde);
|
|
133
|
-
}
|
|
134
|
-
} catch (e) {
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
173
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
137
174
|
}
|
|
138
|
-
|
|
139
|
-
const cleanPrefix = prefix.toLocaleLowerCase("tr-TR");
|
|
175
|
+
|
|
176
|
+
const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
|
|
140
177
|
return this.autocompleteCache
|
|
141
178
|
.filter(w => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix))
|
|
142
179
|
.slice(0, 10);
|