tdk-api-wrapper 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-2TA5PMVZ.mjs → chunk-6BTOGV2M.mjs} +37 -24
- package/dist/cli.js +37 -24
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +16 -6
- package/dist/index.d.ts +16 -6
- package/dist/index.js +37 -24
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/tdk.ts +44 -31
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Aşağıdaki metotlar `TDK` sınıfı üzerinden statik olarak erişilebilir dur
|
|
|
68
68
|
- **`TDK.syllabicate(word)`**: Kelimeyi Türkçe heceleme kurallarına göre doğru hecelerine ayırır (Örn: `['mu', 'vaf', 'fa', 'ki', 'yet']`). API isteği atmaz, çok hızlıdır.
|
|
69
69
|
- **`TDK.checkVowelHarmony(word)`**: Kelimenin büyük ünlü uyumuna uyup uymadığını (boolean) kontrol eder.
|
|
70
70
|
- **`TDK.getPartOfSpeech(word)`**: Kelimenin sözcük türünü (isim, sıfat, zarf vb.) döndürür.
|
|
71
|
-
- **`TDK.checkSpelling(word)`**:
|
|
71
|
+
- **`TDK.checkSpelling(word)`**: Kelimenin doğru yazılıp yazılmadığını kontrol eder. Önce TDK'nin "sık yapılan yanlışlar" listesinde tam eşleşme arar; bulamazsa TDK'nin ~81 bin kelimelik tam madde listesi üzerinde edit-distance (Levenshtein) ile en yakın kelimeyi önerir (örn. `herkez` → `herkes`, `mektub` → `mektup`).
|
|
72
72
|
- **`TDK.getCompoundWords(word)`**: Aranan kelime ile oluşturulmuş birleşik kelimeleri (Örn: dolma kalem) listeler.
|
|
73
73
|
|
|
74
74
|
### 3. Edebi ve Kültürel Analiz
|
|
@@ -390,29 +390,37 @@ var TDK = class {
|
|
|
390
390
|
if (mixMatch) {
|
|
391
391
|
return { isCorrect: false, word, suggestion: mixMatch.dogru };
|
|
392
392
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
393
|
+
}
|
|
394
|
+
if (this.autocompleteCache.length === 0) {
|
|
395
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
396
|
+
}
|
|
397
|
+
const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
|
|
398
|
+
let best = null;
|
|
399
|
+
for (const candidate of this.autocompleteCache) {
|
|
400
|
+
if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR"))
|
|
401
|
+
continue;
|
|
402
|
+
const distance = this.levenshtein(cleanWord, candidate);
|
|
403
|
+
if (distance > 0 && (!best || distance < best.distance)) {
|
|
404
|
+
best = { candidate, distance };
|
|
405
|
+
if (distance === 1)
|
|
406
|
+
break;
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
|
+
if (best && best.distance <= 2) {
|
|
410
|
+
return { isCorrect: false, word, suggestion: best.candidate };
|
|
411
|
+
}
|
|
409
412
|
return { isCorrect: false, word };
|
|
410
413
|
}
|
|
411
414
|
/**
|
|
412
415
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
416
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
417
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
418
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
419
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
420
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
413
421
|
*/
|
|
414
|
-
static async getDailyContent() {
|
|
415
|
-
if (this.isCacheEnabled && this.dailyContentCache)
|
|
422
|
+
static async getDailyContent(bypassCache = false) {
|
|
423
|
+
if (!bypassCache && this.isCacheEnabled && this.dailyContentCache)
|
|
416
424
|
return this.dailyContentCache;
|
|
417
425
|
try {
|
|
418
426
|
const response = await fetch(`${this.BASE_URL}/icerik`, {
|
|
@@ -420,7 +428,7 @@ var TDK = class {
|
|
|
420
428
|
});
|
|
421
429
|
if (response.ok) {
|
|
422
430
|
const data = await response.json();
|
|
423
|
-
if (this.isCacheEnabled)
|
|
431
|
+
if (!bypassCache && this.isCacheEnabled)
|
|
424
432
|
this.dailyContentCache = data;
|
|
425
433
|
return data;
|
|
426
434
|
}
|
|
@@ -461,10 +469,12 @@ var TDK = class {
|
|
|
461
469
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
462
470
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
463
471
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
464
|
-
* calls a second apart can return entirely different rules.
|
|
472
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
473
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
474
|
+
* draw even when `enableCache(true)` is on.
|
|
465
475
|
*/
|
|
466
|
-
static async getKurallar() {
|
|
467
|
-
const daily = await this.getDailyContent();
|
|
476
|
+
static async getKurallar(bypassCache = false) {
|
|
477
|
+
const daily = await this.getDailyContent(bypassCache);
|
|
468
478
|
return daily?.kural ?? [];
|
|
469
479
|
}
|
|
470
480
|
/**
|
|
@@ -473,16 +483,19 @@ var TDK = class {
|
|
|
473
483
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
474
484
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
475
485
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
476
|
-
* short delay) until it finds a match or gives up.
|
|
477
|
-
*
|
|
478
|
-
*
|
|
486
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
487
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
488
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
489
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
490
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
491
|
+
* matched page can't be parsed.
|
|
479
492
|
*/
|
|
480
493
|
static async getRule(name) {
|
|
481
494
|
if (!name || name.trim() === "")
|
|
482
495
|
return null;
|
|
483
496
|
const target = name.trim().toLocaleLowerCase("tr-TR");
|
|
484
497
|
for (let attempt = 0; attempt < 25; attempt++) {
|
|
485
|
-
const rules = await this.getKurallar();
|
|
498
|
+
const rules = await this.getKurallar(true);
|
|
486
499
|
const match = rules.find((r) => r.adi.toLocaleLowerCase("tr-TR").includes(target));
|
|
487
500
|
if (match)
|
|
488
501
|
return this.fetchRuleText(match.url);
|
package/dist/cli.js
CHANGED
|
@@ -415,29 +415,37 @@ var TDK = class {
|
|
|
415
415
|
if (mixMatch) {
|
|
416
416
|
return { isCorrect: false, word: word2, suggestion: mixMatch.dogru };
|
|
417
417
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
418
|
+
}
|
|
419
|
+
if (this.autocompleteCache.length === 0) {
|
|
420
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
421
|
+
}
|
|
422
|
+
const cleanWord = word2.trim().toLocaleLowerCase("tr-TR");
|
|
423
|
+
let best = null;
|
|
424
|
+
for (const candidate of this.autocompleteCache) {
|
|
425
|
+
if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR"))
|
|
426
|
+
continue;
|
|
427
|
+
const distance = this.levenshtein(cleanWord, candidate);
|
|
428
|
+
if (distance > 0 && (!best || distance < best.distance)) {
|
|
429
|
+
best = { candidate, distance };
|
|
430
|
+
if (distance === 1)
|
|
431
|
+
break;
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
|
+
if (best && best.distance <= 2) {
|
|
435
|
+
return { isCorrect: false, word: word2, suggestion: best.candidate };
|
|
436
|
+
}
|
|
434
437
|
return { isCorrect: false, word: word2 };
|
|
435
438
|
}
|
|
436
439
|
/**
|
|
437
440
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
441
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
442
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
443
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
444
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
445
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
438
446
|
*/
|
|
439
|
-
static async getDailyContent() {
|
|
440
|
-
if (this.isCacheEnabled && this.dailyContentCache)
|
|
447
|
+
static async getDailyContent(bypassCache = false) {
|
|
448
|
+
if (!bypassCache && this.isCacheEnabled && this.dailyContentCache)
|
|
441
449
|
return this.dailyContentCache;
|
|
442
450
|
try {
|
|
443
451
|
const response = await fetch(`${this.BASE_URL}/icerik`, {
|
|
@@ -445,7 +453,7 @@ var TDK = class {
|
|
|
445
453
|
});
|
|
446
454
|
if (response.ok) {
|
|
447
455
|
const data = await response.json();
|
|
448
|
-
if (this.isCacheEnabled)
|
|
456
|
+
if (!bypassCache && this.isCacheEnabled)
|
|
449
457
|
this.dailyContentCache = data;
|
|
450
458
|
return data;
|
|
451
459
|
}
|
|
@@ -486,10 +494,12 @@ var TDK = class {
|
|
|
486
494
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
487
495
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
488
496
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
489
|
-
* calls a second apart can return entirely different rules.
|
|
497
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
498
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
499
|
+
* draw even when `enableCache(true)` is on.
|
|
490
500
|
*/
|
|
491
|
-
static async getKurallar() {
|
|
492
|
-
const daily = await this.getDailyContent();
|
|
501
|
+
static async getKurallar(bypassCache = false) {
|
|
502
|
+
const daily = await this.getDailyContent(bypassCache);
|
|
493
503
|
return daily?.kural ?? [];
|
|
494
504
|
}
|
|
495
505
|
/**
|
|
@@ -498,16 +508,19 @@ var TDK = class {
|
|
|
498
508
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
499
509
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
500
510
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
501
|
-
* short delay) until it finds a match or gives up.
|
|
502
|
-
*
|
|
503
|
-
*
|
|
511
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
512
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
513
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
514
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
515
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
516
|
+
* matched page can't be parsed.
|
|
504
517
|
*/
|
|
505
518
|
static async getRule(name) {
|
|
506
519
|
if (!name || name.trim() === "")
|
|
507
520
|
return null;
|
|
508
521
|
const target = name.trim().toLocaleLowerCase("tr-TR");
|
|
509
522
|
for (let attempt = 0; attempt < 25; attempt++) {
|
|
510
|
-
const rules = await this.getKurallar();
|
|
523
|
+
const rules = await this.getKurallar(true);
|
|
511
524
|
const match = rules.find((r) => r.adi.toLocaleLowerCase("tr-TR").includes(target));
|
|
512
525
|
if (match)
|
|
513
526
|
return this.fetchRuleText(match.url);
|
package/dist/cli.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -244,8 +244,13 @@ declare class TDK {
|
|
|
244
244
|
static checkSpelling(word: string): Promise<SpellCheckResult>;
|
|
245
245
|
/**
|
|
246
246
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
247
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
248
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
249
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
250
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
251
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
247
252
|
*/
|
|
248
|
-
static getDailyContent(): Promise<DailyContent | null>;
|
|
253
|
+
static getDailyContent(bypassCache?: boolean): Promise<DailyContent | null>;
|
|
249
254
|
/**
|
|
250
255
|
* Returns today's word of the day along with all of its listed meanings.
|
|
251
256
|
*/
|
|
@@ -260,18 +265,23 @@ declare class TDK {
|
|
|
260
265
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
261
266
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
262
267
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
263
|
-
* calls a second apart can return entirely different rules.
|
|
268
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
269
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
270
|
+
* draw even when `enableCache(true)` is on.
|
|
264
271
|
*/
|
|
265
|
-
static getKurallar(): Promise<TDKRule[]>;
|
|
272
|
+
static getKurallar(bypassCache?: boolean): Promise<TDKRule[]>;
|
|
266
273
|
/**
|
|
267
274
|
* Fetches the full plain-text content of a named spelling rule (matched
|
|
268
275
|
* case-insensitively, substring match) from `tdk.gov.tr`. Since `/icerik`
|
|
269
276
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
270
277
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
271
278
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
272
|
-
* short delay) until it finds a match or gives up.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
279
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
280
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
281
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
282
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
283
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
284
|
+
* matched page can't be parsed.
|
|
275
285
|
*/
|
|
276
286
|
static getRule(name: string): Promise<string | null>;
|
|
277
287
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -244,8 +244,13 @@ declare class TDK {
|
|
|
244
244
|
static checkSpelling(word: string): Promise<SpellCheckResult>;
|
|
245
245
|
/**
|
|
246
246
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
247
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
248
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
249
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
250
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
251
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
247
252
|
*/
|
|
248
|
-
static getDailyContent(): Promise<DailyContent | null>;
|
|
253
|
+
static getDailyContent(bypassCache?: boolean): Promise<DailyContent | null>;
|
|
249
254
|
/**
|
|
250
255
|
* Returns today's word of the day along with all of its listed meanings.
|
|
251
256
|
*/
|
|
@@ -260,18 +265,23 @@ declare class TDK {
|
|
|
260
265
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
261
266
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
262
267
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
263
|
-
* calls a second apart can return entirely different rules.
|
|
268
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
269
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
270
|
+
* draw even when `enableCache(true)` is on.
|
|
264
271
|
*/
|
|
265
|
-
static getKurallar(): Promise<TDKRule[]>;
|
|
272
|
+
static getKurallar(bypassCache?: boolean): Promise<TDKRule[]>;
|
|
266
273
|
/**
|
|
267
274
|
* Fetches the full plain-text content of a named spelling rule (matched
|
|
268
275
|
* case-insensitively, substring match) from `tdk.gov.tr`. Since `/icerik`
|
|
269
276
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
270
277
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
271
278
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
272
|
-
* short delay) until it finds a match or gives up.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
279
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
280
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
281
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
282
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
283
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
284
|
+
* matched page can't be parsed.
|
|
275
285
|
*/
|
|
276
286
|
static getRule(name: string): Promise<string | null>;
|
|
277
287
|
/**
|
package/dist/index.js
CHANGED
|
@@ -429,29 +429,37 @@ var TDK = class {
|
|
|
429
429
|
if (mixMatch) {
|
|
430
430
|
return { isCorrect: false, word, suggestion: mixMatch.dogru };
|
|
431
431
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
432
|
+
}
|
|
433
|
+
if (this.autocompleteCache.length === 0) {
|
|
434
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
435
|
+
}
|
|
436
|
+
const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
|
|
437
|
+
let best = null;
|
|
438
|
+
for (const candidate of this.autocompleteCache) {
|
|
439
|
+
if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR"))
|
|
440
|
+
continue;
|
|
441
|
+
const distance = this.levenshtein(cleanWord, candidate);
|
|
442
|
+
if (distance > 0 && (!best || distance < best.distance)) {
|
|
443
|
+
best = { candidate, distance };
|
|
444
|
+
if (distance === 1)
|
|
445
|
+
break;
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
|
+
if (best && best.distance <= 2) {
|
|
449
|
+
return { isCorrect: false, word, suggestion: best.candidate };
|
|
450
|
+
}
|
|
448
451
|
return { isCorrect: false, word };
|
|
449
452
|
}
|
|
450
453
|
/**
|
|
451
454
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
455
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
456
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
457
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
458
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
459
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
452
460
|
*/
|
|
453
|
-
static async getDailyContent() {
|
|
454
|
-
if (this.isCacheEnabled && this.dailyContentCache)
|
|
461
|
+
static async getDailyContent(bypassCache = false) {
|
|
462
|
+
if (!bypassCache && this.isCacheEnabled && this.dailyContentCache)
|
|
455
463
|
return this.dailyContentCache;
|
|
456
464
|
try {
|
|
457
465
|
const response = await fetch(`${this.BASE_URL}/icerik`, {
|
|
@@ -459,7 +467,7 @@ var TDK = class {
|
|
|
459
467
|
});
|
|
460
468
|
if (response.ok) {
|
|
461
469
|
const data = await response.json();
|
|
462
|
-
if (this.isCacheEnabled)
|
|
470
|
+
if (!bypassCache && this.isCacheEnabled)
|
|
463
471
|
this.dailyContentCache = data;
|
|
464
472
|
return data;
|
|
465
473
|
}
|
|
@@ -500,10 +508,12 @@ var TDK = class {
|
|
|
500
508
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
501
509
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
502
510
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
503
|
-
* calls a second apart can return entirely different rules.
|
|
511
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
512
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
513
|
+
* draw even when `enableCache(true)` is on.
|
|
504
514
|
*/
|
|
505
|
-
static async getKurallar() {
|
|
506
|
-
const daily = await this.getDailyContent();
|
|
515
|
+
static async getKurallar(bypassCache = false) {
|
|
516
|
+
const daily = await this.getDailyContent(bypassCache);
|
|
507
517
|
return daily?.kural ?? [];
|
|
508
518
|
}
|
|
509
519
|
/**
|
|
@@ -512,16 +522,19 @@ var TDK = class {
|
|
|
512
522
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
513
523
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
514
524
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
515
|
-
* short delay) until it finds a match or gives up.
|
|
516
|
-
*
|
|
517
|
-
*
|
|
525
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
526
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
527
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
528
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
529
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
530
|
+
* matched page can't be parsed.
|
|
518
531
|
*/
|
|
519
532
|
static async getRule(name) {
|
|
520
533
|
if (!name || name.trim() === "")
|
|
521
534
|
return null;
|
|
522
535
|
const target = name.trim().toLocaleLowerCase("tr-TR");
|
|
523
536
|
for (let attempt = 0; attempt < 25; attempt++) {
|
|
524
|
-
const rules = await this.getKurallar();
|
|
537
|
+
const rules = await this.getKurallar(true);
|
|
525
538
|
const match = rules.find((r) => r.adi.toLocaleLowerCase("tr-TR").includes(target));
|
|
526
539
|
if (match)
|
|
527
540
|
return this.fetchRuleText(match.url);
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
package/src/tdk.ts
CHANGED
|
@@ -389,8 +389,10 @@ export class TDK {
|
|
|
389
389
|
if (results.length > 0) {
|
|
390
390
|
return { isCorrect: true, word };
|
|
391
391
|
}
|
|
392
|
-
|
|
393
|
-
// 2. If not, check "sıkça yapılan yanlışlar" from DailyContent
|
|
392
|
+
|
|
393
|
+
// 2. If not, check "sıkça yapılan yanlışlar" from DailyContent — an exact
|
|
394
|
+
// match here is TDK explicitly saying "X is often confused with Y", so
|
|
395
|
+
// it's authoritative when it hits (but only 2-3 rotating entries per call).
|
|
394
396
|
const daily = await this.getDailyContent();
|
|
395
397
|
if (daily) {
|
|
396
398
|
const syydMatch = daily.syyd.find(s => s.yanliskelime.toLocaleLowerCase("tr-TR") === word.toLocaleLowerCase("tr-TR"));
|
|
@@ -401,43 +403,49 @@ export class TDK {
|
|
|
401
403
|
if (mixMatch) {
|
|
402
404
|
return { isCorrect: false, word, suggestion: mixMatch.dogru };
|
|
403
405
|
}
|
|
406
|
+
}
|
|
404
407
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
if (best && best.distance <= 2) {
|
|
422
|
-
return { isCorrect: false, word, suggestion: best.candidate };
|
|
408
|
+
// 3. No exact match in TDK's curated lists: fall back to the closest
|
|
409
|
+
// headword (by edit distance) across TDK's full ~81k-word list (the same
|
|
410
|
+
// data `getSuggestions()` uses). Restricted to single-token, lowercase
|
|
411
|
+
// headwords so it doesn't suggest compounds/phrases or proper nouns.
|
|
412
|
+
if (this.autocompleteCache.length === 0) {
|
|
413
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
414
|
+
}
|
|
415
|
+
const cleanWord = word.trim().toLocaleLowerCase("tr-TR");
|
|
416
|
+
let best: { candidate: string; distance: number } | null = null;
|
|
417
|
+
for (const candidate of this.autocompleteCache) {
|
|
418
|
+
if (candidate.includes(" ") || candidate !== candidate.toLocaleLowerCase("tr-TR")) continue;
|
|
419
|
+
const distance = this.levenshtein(cleanWord, candidate);
|
|
420
|
+
if (distance > 0 && (!best || distance < best.distance)) {
|
|
421
|
+
best = { candidate, distance };
|
|
422
|
+
if (distance === 1) break;
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
|
+
if (best && best.distance <= 2) {
|
|
426
|
+
return { isCorrect: false, word, suggestion: best.candidate };
|
|
427
|
+
}
|
|
425
428
|
return { isCorrect: false, word };
|
|
426
429
|
}
|
|
427
430
|
|
|
428
431
|
/**
|
|
429
432
|
* Fetches daily content (word of the day, proverbs, rules, etc).
|
|
433
|
+
* `bypassCache` skips both reading and writing `dailyContentCache` even
|
|
434
|
+
* when `enableCache(true)` is on — used by `getRule()`'s retry loop, which
|
|
435
|
+
* needs a fresh random `/icerik` draw on every attempt; without it, once
|
|
436
|
+
* caching is enabled the loop would just re-read the same cached response
|
|
437
|
+
* 25 times and could never find a rule outside that first random draw.
|
|
430
438
|
*/
|
|
431
|
-
public static async getDailyContent(): Promise<DailyContent | null> {
|
|
432
|
-
if (this.isCacheEnabled && this.dailyContentCache) return this.dailyContentCache;
|
|
433
|
-
|
|
439
|
+
public static async getDailyContent(bypassCache = false): Promise<DailyContent | null> {
|
|
440
|
+
if (!bypassCache && this.isCacheEnabled && this.dailyContentCache) return this.dailyContentCache;
|
|
441
|
+
|
|
434
442
|
try {
|
|
435
443
|
const response = await fetch(`${this.BASE_URL}/icerik`, {
|
|
436
444
|
headers: { "User-Agent": "TDK-API-Nodejs-Wrapper/1.0" },
|
|
437
445
|
});
|
|
438
446
|
if (response.ok) {
|
|
439
447
|
const data = await response.json() as DailyContent;
|
|
440
|
-
if (this.isCacheEnabled) this.dailyContentCache = data;
|
|
448
|
+
if (!bypassCache && this.isCacheEnabled) this.dailyContentCache = data;
|
|
441
449
|
return data;
|
|
442
450
|
}
|
|
443
451
|
} catch {
|
|
@@ -480,10 +488,12 @@ export class TDK {
|
|
|
480
488
|
* `/icerik` daily-content feed, e.g. `{ adi: "Kısaltmalar", url: "https://..." }`.
|
|
481
489
|
* Note: like `getRandomWord()`, this is NOT a fixed catalog — `/icerik`
|
|
482
490
|
* appears to hand back a single randomly-rotated rule per request, so two
|
|
483
|
-
* calls a second apart can return entirely different rules.
|
|
491
|
+
* calls a second apart can return entirely different rules. `bypassCache`
|
|
492
|
+
* (used internally by `getRule()`'s retry loop) forces a fresh `/icerik`
|
|
493
|
+
* draw even when `enableCache(true)` is on.
|
|
484
494
|
*/
|
|
485
|
-
public static async getKurallar(): Promise<TDKRule[]> {
|
|
486
|
-
const daily = await this.getDailyContent();
|
|
495
|
+
public static async getKurallar(bypassCache = false): Promise<TDKRule[]> {
|
|
496
|
+
const daily = await this.getDailyContent(bypassCache);
|
|
487
497
|
return daily?.kural ?? [];
|
|
488
498
|
}
|
|
489
499
|
|
|
@@ -493,16 +503,19 @@ export class TDK {
|
|
|
493
503
|
* hands back a single randomly-rotated rule per request (out of a pool of
|
|
494
504
|
* roughly twenty) rather than a fixed catalog, a single `getKurallar()`
|
|
495
505
|
* draw would rarely match a given name — this re-draws (bounded, with a
|
|
496
|
-
* short delay) until it finds a match or gives up.
|
|
497
|
-
*
|
|
498
|
-
*
|
|
506
|
+
* short delay) until it finds a match or gives up. Every attempt bypasses
|
|
507
|
+
* `dailyContentCache` — without that, once `enableCache(true)` is on, all
|
|
508
|
+
* 25 attempts would just re-read the same cached `/icerik` response and
|
|
509
|
+
* could never find a rule outside whatever the first draw happened to be.
|
|
510
|
+
* Returns `null` if no match turns up within the attempt budget or the
|
|
511
|
+
* matched page can't be parsed.
|
|
499
512
|
*/
|
|
500
513
|
public static async getRule(name: string): Promise<string | null> {
|
|
501
514
|
if (!name || name.trim() === "") return null;
|
|
502
515
|
const target = name.trim().toLocaleLowerCase("tr-TR");
|
|
503
516
|
|
|
504
517
|
for (let attempt = 0; attempt < 25; attempt++) {
|
|
505
|
-
const rules = await this.getKurallar();
|
|
518
|
+
const rules = await this.getKurallar(true);
|
|
506
519
|
const match = rules.find((r) => r.adi.toLocaleLowerCase("tr-TR").includes(target));
|
|
507
520
|
if (match) return this.fetchRuleText(match.url);
|
|
508
521
|
await this.delay(100);
|