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 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)`**: Kelimenin sadece ilk birkaç harfini girdiğinizde otomatik tamamlama önerilerini çeker.
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- try {
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- try {
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-P3GX7I53.mjs";
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- try {
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
@@ -3,7 +3,7 @@ import {
3
3
  TDKError,
4
4
  TDKNetworkError,
5
5
  TDKValidationError
6
- } from "./chunk-P3GX7I53.mjs";
6
+ } from "./chunk-2TA5PMVZ.mjs";
7
7
  export {
8
8
  TDK,
9
9
  TDKError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tdk-api-wrapper",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "TDK (Türk Dil Kurumu) unofficial live data API wrapper for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
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
- * Returns suggestions (autocomplete) for a given prefix.
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
- try {
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);