tdk-api-wrapper 1.3.1 → 1.4.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 +13 -5
- package/dist/{chunk-ACMGCL7T.mjs → chunk-5TYJDVHK.mjs} +620 -6
- package/dist/cli.js +653 -9
- package/dist/cli.mjs +41 -4
- package/dist/index.d.mts +88 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +629 -8
- package/dist/index.mjs +17 -3
- package/package.json +3 -2
- package/src/cli.ts +40 -3
- package/src/index.ts +1 -0
- package/src/morphology.ts +214 -0
- package/src/tdk.ts +135 -6
- package/src/types.ts +11 -0
- package/test/morphology.test.js +105 -0
package/dist/cli.js
CHANGED
|
@@ -50,6 +50,503 @@ var TDKNetworkError = class extends TDKError {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
// src/morphology.ts
|
|
54
|
+
var TURKISH_VOWELS = "ae\u0131io\xF6u\xFC";
|
|
55
|
+
function isVowel(ch) {
|
|
56
|
+
return TURKISH_VOWELS.includes(ch);
|
|
57
|
+
}
|
|
58
|
+
var TURKISH_SUFFIXES = [
|
|
59
|
+
// 9-letter composite suffixes
|
|
60
|
+
"lerimizden",
|
|
61
|
+
"lar\u0131m\u0131zdan",
|
|
62
|
+
"lerinizden",
|
|
63
|
+
"lar\u0131n\u0131zdan",
|
|
64
|
+
// 8-letter composite suffixes
|
|
65
|
+
"lerinin",
|
|
66
|
+
"lar\u0131n\u0131n",
|
|
67
|
+
"lerinde",
|
|
68
|
+
"lar\u0131nda",
|
|
69
|
+
"lerinden",
|
|
70
|
+
"lar\u0131ndan",
|
|
71
|
+
"leriyle",
|
|
72
|
+
"lar\u0131yla",
|
|
73
|
+
"lerini",
|
|
74
|
+
"lar\u0131n\u0131",
|
|
75
|
+
"lerimize",
|
|
76
|
+
"lar\u0131m\u0131za",
|
|
77
|
+
"lerimizle",
|
|
78
|
+
"lar\u0131m\u0131zla",
|
|
79
|
+
"lerinizin",
|
|
80
|
+
"lar\u0131n\u0131z\u0131n",
|
|
81
|
+
"lerinizde",
|
|
82
|
+
"lar\u0131n\u0131zda",
|
|
83
|
+
"d\u0131\u011F\u0131ndan",
|
|
84
|
+
"di\u011Finden",
|
|
85
|
+
"du\u011Fundan",
|
|
86
|
+
"d\xFC\u011F\xFCnden",
|
|
87
|
+
"t\u0131\u011F\u0131ndan",
|
|
88
|
+
"ti\u011Finden",
|
|
89
|
+
"tu\u011Fundan",
|
|
90
|
+
"t\xFC\u011F\xFCnden",
|
|
91
|
+
// 7-letter composite suffixes
|
|
92
|
+
"ecektir",
|
|
93
|
+
"acakt\u0131r",
|
|
94
|
+
"ece\u011Fim",
|
|
95
|
+
"aca\u011F\u0131m",
|
|
96
|
+
"eceksin",
|
|
97
|
+
"acaks\u0131n",
|
|
98
|
+
"ece\u011Fiz",
|
|
99
|
+
"aca\u011F\u0131z",
|
|
100
|
+
"lerimiz",
|
|
101
|
+
"lar\u0131m\u0131z",
|
|
102
|
+
"leriniz",
|
|
103
|
+
"lar\u0131n\u0131z",
|
|
104
|
+
"umuzdan",
|
|
105
|
+
"\xFCm\xFCzden",
|
|
106
|
+
"inizden",
|
|
107
|
+
"\u0131n\u0131zdan",
|
|
108
|
+
"\xFCn\xFCzden",
|
|
109
|
+
"d\u0131\u011F\u0131nda",
|
|
110
|
+
"di\u011Finde",
|
|
111
|
+
"du\u011Funda",
|
|
112
|
+
"d\xFC\u011F\xFCnde",
|
|
113
|
+
"t\u0131\u011F\u0131nda",
|
|
114
|
+
"ti\u011Finde",
|
|
115
|
+
"tu\u011Funda",
|
|
116
|
+
"t\xFC\u011F\xFCnde",
|
|
117
|
+
"mas\u0131na",
|
|
118
|
+
"mesine",
|
|
119
|
+
"\u0131yorsunuz",
|
|
120
|
+
"iyorsunuz",
|
|
121
|
+
"uyorsunuz",
|
|
122
|
+
"\xFCyorsunuz",
|
|
123
|
+
"yorsunuz",
|
|
124
|
+
// 6-letter composite suffixes
|
|
125
|
+
"iyorsa",
|
|
126
|
+
"iyorduk",
|
|
127
|
+
"iyordu",
|
|
128
|
+
"iyormu\u015F",
|
|
129
|
+
"\u0131yorsa",
|
|
130
|
+
"\u0131yorduk",
|
|
131
|
+
"\u0131yordu",
|
|
132
|
+
"\u0131yormu\u015F",
|
|
133
|
+
"uyorsa",
|
|
134
|
+
"uyorduk",
|
|
135
|
+
"uyordu",
|
|
136
|
+
"uyormu\u015F",
|
|
137
|
+
"\xFCyorsa",
|
|
138
|
+
"\xFCyorduk",
|
|
139
|
+
"\xFCyordu",
|
|
140
|
+
"\xFCyormu\u015F",
|
|
141
|
+
"\u0131yorsun",
|
|
142
|
+
"iyorsun",
|
|
143
|
+
"uyorsun",
|
|
144
|
+
"\xFCyorsun",
|
|
145
|
+
"\u0131yorlar",
|
|
146
|
+
"iyorlar",
|
|
147
|
+
"uyorlar",
|
|
148
|
+
"\xFCyorlar",
|
|
149
|
+
"iyoruz",
|
|
150
|
+
"\u0131yoruz",
|
|
151
|
+
"uyoruz",
|
|
152
|
+
"\xFCyoruz",
|
|
153
|
+
"imizin",
|
|
154
|
+
"\u0131m\u0131z\u0131n",
|
|
155
|
+
"umuzun",
|
|
156
|
+
"\xFCm\xFCz\xFCn",
|
|
157
|
+
"imizde",
|
|
158
|
+
"\u0131m\u0131zda",
|
|
159
|
+
"umuzda",
|
|
160
|
+
"\xFCm\xFCzde",
|
|
161
|
+
"imizden",
|
|
162
|
+
"\u0131m\u0131zdan",
|
|
163
|
+
"imizle",
|
|
164
|
+
"\u0131m\u0131zla",
|
|
165
|
+
"umuzla",
|
|
166
|
+
"\xFCm\xFCzle",
|
|
167
|
+
"lerdir",
|
|
168
|
+
"lard\u0131r",
|
|
169
|
+
"mu\u015Ftur",
|
|
170
|
+
"mi\u015Ftir",
|
|
171
|
+
"mu\u015Ftur",
|
|
172
|
+
"m\xFC\u015Ft\xFCr",
|
|
173
|
+
"lerden",
|
|
174
|
+
"lardan",
|
|
175
|
+
"lerine",
|
|
176
|
+
"lar\u0131na",
|
|
177
|
+
"leriyle",
|
|
178
|
+
"lar\u0131yla",
|
|
179
|
+
"seniz",
|
|
180
|
+
"san\u0131z",
|
|
181
|
+
"diniz",
|
|
182
|
+
"d\u0131n\u0131z",
|
|
183
|
+
"dunuz",
|
|
184
|
+
"d\xFCn\xFCz",
|
|
185
|
+
"tiniz",
|
|
186
|
+
"t\u0131n\u0131z",
|
|
187
|
+
"tunuz",
|
|
188
|
+
"t\xFCn\xFCz",
|
|
189
|
+
"siniz",
|
|
190
|
+
"s\u0131n\u0131z",
|
|
191
|
+
"sunuz",
|
|
192
|
+
"s\xFCn\xFCz",
|
|
193
|
+
"d\u0131k\xE7a",
|
|
194
|
+
"dik\xE7e",
|
|
195
|
+
"duk\xE7a",
|
|
196
|
+
"d\xFCk\xE7e",
|
|
197
|
+
"t\u0131k\xE7a",
|
|
198
|
+
"tik\xE7e",
|
|
199
|
+
"tuk\xE7a",
|
|
200
|
+
"t\xFCk\xE7e",
|
|
201
|
+
"\u0131rken",
|
|
202
|
+
"irken",
|
|
203
|
+
"urken",
|
|
204
|
+
"\xFCrken",
|
|
205
|
+
"arken",
|
|
206
|
+
"erken",
|
|
207
|
+
// 5-letter suffixes
|
|
208
|
+
"lerde",
|
|
209
|
+
"larda",
|
|
210
|
+
"lerle",
|
|
211
|
+
"larla",
|
|
212
|
+
"lerin",
|
|
213
|
+
"lar\u0131n",
|
|
214
|
+
"lerim",
|
|
215
|
+
"lar\u0131m",
|
|
216
|
+
"dirler",
|
|
217
|
+
"d\u0131rlar",
|
|
218
|
+
"d\xFCrler",
|
|
219
|
+
"durlar",
|
|
220
|
+
"tirler",
|
|
221
|
+
"t\u0131rlar",
|
|
222
|
+
"t\xFCrler",
|
|
223
|
+
"turlar",
|
|
224
|
+
"siniz",
|
|
225
|
+
"s\u0131n\u0131z",
|
|
226
|
+
"sunuz",
|
|
227
|
+
"s\xFCn\xFCz",
|
|
228
|
+
"yorum",
|
|
229
|
+
"yorsun",
|
|
230
|
+
"uyoruz",
|
|
231
|
+
"yorsunuz",
|
|
232
|
+
"yorlar",
|
|
233
|
+
"eceks",
|
|
234
|
+
"acaks",
|
|
235
|
+
"eyim",
|
|
236
|
+
"ay\u0131m",
|
|
237
|
+
"indik",
|
|
238
|
+
"\u0131nd\u0131k",
|
|
239
|
+
"unduk",
|
|
240
|
+
"\xFCnd\xFCk",
|
|
241
|
+
"ildik",
|
|
242
|
+
"\u0131ld\u0131k",
|
|
243
|
+
"ulduk",
|
|
244
|
+
"\xFCld\xFCk",
|
|
245
|
+
"meden",
|
|
246
|
+
"madan",
|
|
247
|
+
"y\u0131n\u0131z",
|
|
248
|
+
"yiniz",
|
|
249
|
+
"yunuz",
|
|
250
|
+
"y\xFCn\xFCz",
|
|
251
|
+
// 4-letter suffixes
|
|
252
|
+
"imiz",
|
|
253
|
+
"\u0131m\u0131z",
|
|
254
|
+
"umuz",
|
|
255
|
+
"\xFCm\xFCz",
|
|
256
|
+
"iniz",
|
|
257
|
+
"\u0131n\u0131z",
|
|
258
|
+
"unuz",
|
|
259
|
+
"\xFCn\xFCz",
|
|
260
|
+
"leri",
|
|
261
|
+
"lar\u0131",
|
|
262
|
+
"idir",
|
|
263
|
+
"\u0131d\u0131r",
|
|
264
|
+
"udur",
|
|
265
|
+
"\xFCd\xFCr",
|
|
266
|
+
"ecek",
|
|
267
|
+
"acak",
|
|
268
|
+
"erek",
|
|
269
|
+
"arak",
|
|
270
|
+
"ince",
|
|
271
|
+
"\u0131nca",
|
|
272
|
+
"unca",
|
|
273
|
+
"\xFCnce",
|
|
274
|
+
"ken",
|
|
275
|
+
"meli",
|
|
276
|
+
"mal\u0131",
|
|
277
|
+
"iyor",
|
|
278
|
+
"\u0131yor",
|
|
279
|
+
"uyor",
|
|
280
|
+
"\xFCyor",
|
|
281
|
+
"mi\u015Fti",
|
|
282
|
+
"m\u0131\u015Ft\u0131",
|
|
283
|
+
"mu\u015Ftu",
|
|
284
|
+
"m\xFC\u015Ft\xFC",
|
|
285
|
+
"seydi",
|
|
286
|
+
"sayd\u0131",
|
|
287
|
+
"ydim",
|
|
288
|
+
"yd\u0131m",
|
|
289
|
+
"ydum",
|
|
290
|
+
"yd\xFCm",
|
|
291
|
+
"tiler",
|
|
292
|
+
"t\u0131lar",
|
|
293
|
+
"diler",
|
|
294
|
+
"d\u0131lar",
|
|
295
|
+
"ikten",
|
|
296
|
+
"\u0131ktan",
|
|
297
|
+
"uktan",
|
|
298
|
+
"\xFCkten",
|
|
299
|
+
// 3-letter suffixes
|
|
300
|
+
"ler",
|
|
301
|
+
"lar",
|
|
302
|
+
"den",
|
|
303
|
+
"dan",
|
|
304
|
+
"ten",
|
|
305
|
+
"tan",
|
|
306
|
+
"dir",
|
|
307
|
+
"d\u0131r",
|
|
308
|
+
"dur",
|
|
309
|
+
"d\xFCr",
|
|
310
|
+
"tir",
|
|
311
|
+
"t\u0131r",
|
|
312
|
+
"tur",
|
|
313
|
+
"t\xFCr",
|
|
314
|
+
"nin",
|
|
315
|
+
"n\u0131n",
|
|
316
|
+
"nun",
|
|
317
|
+
"n\xFCn",
|
|
318
|
+
"yle",
|
|
319
|
+
"yla",
|
|
320
|
+
"mi\u015F",
|
|
321
|
+
"m\u0131\u015F",
|
|
322
|
+
"mu\u015F",
|
|
323
|
+
"m\xFC\u015F",
|
|
324
|
+
"dim",
|
|
325
|
+
"d\u0131m",
|
|
326
|
+
"dum",
|
|
327
|
+
"d\xFCm",
|
|
328
|
+
"tim",
|
|
329
|
+
"t\u0131m",
|
|
330
|
+
"tum",
|
|
331
|
+
"t\xFCm",
|
|
332
|
+
"din",
|
|
333
|
+
"d\u0131n",
|
|
334
|
+
"dun",
|
|
335
|
+
"d\xFCn",
|
|
336
|
+
"tin",
|
|
337
|
+
"t\u0131n",
|
|
338
|
+
"tun",
|
|
339
|
+
"t\xFCn",
|
|
340
|
+
"dik",
|
|
341
|
+
"d\u0131k",
|
|
342
|
+
"duk",
|
|
343
|
+
"d\xFCk",
|
|
344
|
+
"tik",
|
|
345
|
+
"t\u0131k",
|
|
346
|
+
"tuk",
|
|
347
|
+
"t\xFCk",
|
|
348
|
+
"ydi",
|
|
349
|
+
"yd\u0131",
|
|
350
|
+
"ydu",
|
|
351
|
+
"yd\xFC",
|
|
352
|
+
"yim",
|
|
353
|
+
"y\u0131m",
|
|
354
|
+
"yum",
|
|
355
|
+
"y\xFCm",
|
|
356
|
+
"sin",
|
|
357
|
+
"s\u0131n",
|
|
358
|
+
"sun",
|
|
359
|
+
"s\xFCn",
|
|
360
|
+
"siz",
|
|
361
|
+
"s\u0131z",
|
|
362
|
+
"suz",
|
|
363
|
+
"s\xFCz",
|
|
364
|
+
"lik",
|
|
365
|
+
"l\u0131k",
|
|
366
|
+
"luk",
|
|
367
|
+
"l\xFCk",
|
|
368
|
+
"ici",
|
|
369
|
+
"\u0131c\u0131",
|
|
370
|
+
"ucu",
|
|
371
|
+
"\xFCc\xFC",
|
|
372
|
+
"gen",
|
|
373
|
+
"gan",
|
|
374
|
+
"ken",
|
|
375
|
+
"kan",
|
|
376
|
+
"len",
|
|
377
|
+
"lan",
|
|
378
|
+
"le\u015F",
|
|
379
|
+
"la\u015F",
|
|
380
|
+
"mek",
|
|
381
|
+
"mak",
|
|
382
|
+
"yor",
|
|
383
|
+
// 2-letter suffixes
|
|
384
|
+
"de",
|
|
385
|
+
"da",
|
|
386
|
+
"te",
|
|
387
|
+
"ta",
|
|
388
|
+
"im",
|
|
389
|
+
"\u0131m",
|
|
390
|
+
"um",
|
|
391
|
+
"\xFCm",
|
|
392
|
+
"in",
|
|
393
|
+
"\u0131n",
|
|
394
|
+
"un",
|
|
395
|
+
"\xFCn",
|
|
396
|
+
"iz",
|
|
397
|
+
"\u0131z",
|
|
398
|
+
"uz",
|
|
399
|
+
"\xFCz",
|
|
400
|
+
"si",
|
|
401
|
+
"s\u0131",
|
|
402
|
+
"su",
|
|
403
|
+
"s\xFC",
|
|
404
|
+
"ye",
|
|
405
|
+
"ya",
|
|
406
|
+
"le",
|
|
407
|
+
"la",
|
|
408
|
+
"di",
|
|
409
|
+
"d\u0131",
|
|
410
|
+
"du",
|
|
411
|
+
"d\xFC",
|
|
412
|
+
"ti",
|
|
413
|
+
"t\u0131",
|
|
414
|
+
"tu",
|
|
415
|
+
"t\xFC",
|
|
416
|
+
"se",
|
|
417
|
+
"sa",
|
|
418
|
+
"ce",
|
|
419
|
+
"ca",
|
|
420
|
+
"\xE7e",
|
|
421
|
+
"\xE7a",
|
|
422
|
+
"me",
|
|
423
|
+
"ma",
|
|
424
|
+
"ip",
|
|
425
|
+
"\u0131p",
|
|
426
|
+
"up",
|
|
427
|
+
"\xFCp",
|
|
428
|
+
"en",
|
|
429
|
+
"an",
|
|
430
|
+
"i\u015F",
|
|
431
|
+
"\u0131\u015F",
|
|
432
|
+
"u\u015F",
|
|
433
|
+
"\xFC\u015F",
|
|
434
|
+
"li",
|
|
435
|
+
"l\u0131",
|
|
436
|
+
"lu",
|
|
437
|
+
"l\xFC",
|
|
438
|
+
"ci",
|
|
439
|
+
"c\u0131",
|
|
440
|
+
"cu",
|
|
441
|
+
"c\xFC",
|
|
442
|
+
"\xE7i",
|
|
443
|
+
"\xE7\u0131",
|
|
444
|
+
"\xE7u",
|
|
445
|
+
"\xE7\xFC",
|
|
446
|
+
// 1-letter suffixes (vowels / basic case endings)
|
|
447
|
+
"e",
|
|
448
|
+
"a",
|
|
449
|
+
"i",
|
|
450
|
+
"\u0131",
|
|
451
|
+
"u",
|
|
452
|
+
"\xFC"
|
|
453
|
+
];
|
|
454
|
+
function restoreConsonantSoftening(stem) {
|
|
455
|
+
if (stem.length < 2)
|
|
456
|
+
return [];
|
|
457
|
+
const last = stem.slice(-1);
|
|
458
|
+
const base = stem.slice(0, -1);
|
|
459
|
+
switch (last) {
|
|
460
|
+
case "b":
|
|
461
|
+
return [base + "p"];
|
|
462
|
+
case "c":
|
|
463
|
+
return [base + "\xE7"];
|
|
464
|
+
case "d":
|
|
465
|
+
return [base + "t"];
|
|
466
|
+
case "\u011F":
|
|
467
|
+
return [base + "k"];
|
|
468
|
+
case "g":
|
|
469
|
+
return [base + "k"];
|
|
470
|
+
default:
|
|
471
|
+
return [];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function restoreVowelDrop(stem) {
|
|
475
|
+
if (stem.length < 3)
|
|
476
|
+
return [];
|
|
477
|
+
const c1 = stem[stem.length - 2];
|
|
478
|
+
const c2 = stem[stem.length - 1];
|
|
479
|
+
if (!isVowel(c1) && !isVowel(c2)) {
|
|
480
|
+
const vowelsInBase = stem.slice(0, -2).split("").filter(isVowel);
|
|
481
|
+
if (vowelsInBase.length > 0) {
|
|
482
|
+
const lastVowel = vowelsInBase[vowelsInBase.length - 1];
|
|
483
|
+
let inserted = "i";
|
|
484
|
+
if ("a\u0131".includes(lastVowel))
|
|
485
|
+
inserted = "\u0131";
|
|
486
|
+
else if ("ei".includes(lastVowel))
|
|
487
|
+
inserted = "i";
|
|
488
|
+
else if ("ou".includes(lastVowel))
|
|
489
|
+
inserted = "u";
|
|
490
|
+
else if ("\xF6\xFC".includes(lastVowel))
|
|
491
|
+
inserted = "\xFC";
|
|
492
|
+
return [stem.slice(0, -1) + inserted + c2];
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
function restoreInfinitive(stem) {
|
|
498
|
+
if (stem.length < 2)
|
|
499
|
+
return [];
|
|
500
|
+
const vowelsInBase = stem.split("").filter(isVowel);
|
|
501
|
+
if (vowelsInBase.length === 0)
|
|
502
|
+
return [];
|
|
503
|
+
const lastVowel = vowelsInBase[vowelsInBase.length - 1];
|
|
504
|
+
return "a\u0131ou".includes(lastVowel) ? [stem + "mak"] : [stem + "mek"];
|
|
505
|
+
}
|
|
506
|
+
function getStemCandidates(word2, minStemLength = 2, maxDepth = 4) {
|
|
507
|
+
if (!word2 || word2.trim().length === 0)
|
|
508
|
+
return [];
|
|
509
|
+
const raw = word2.trim();
|
|
510
|
+
const normalized = raw.toLocaleLowerCase("tr-TR");
|
|
511
|
+
const candidatesWithWeight = [];
|
|
512
|
+
const seen = /* @__PURE__ */ new Set();
|
|
513
|
+
if (raw.includes("'") || raw.includes("\u2019")) {
|
|
514
|
+
const apostropheStem = normalized.split(/['’]/)[0];
|
|
515
|
+
if (apostropheStem.length >= minStemLength) {
|
|
516
|
+
candidatesWithWeight.push({ candidate: apostropheStem, baseLength: apostropheStem.length + 10 });
|
|
517
|
+
seen.add(apostropheStem);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
let frontier = [normalized];
|
|
521
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
522
|
+
const nextFrontier = [];
|
|
523
|
+
for (const current of frontier) {
|
|
524
|
+
for (const suffix of TURKISH_SUFFIXES) {
|
|
525
|
+
if (current.length - suffix.length >= minStemLength && current.endsWith(suffix)) {
|
|
526
|
+
const stem = current.slice(0, -suffix.length);
|
|
527
|
+
const hardened = restoreConsonantSoftening(stem);
|
|
528
|
+
const vowelDropped = restoreVowelDrop(stem);
|
|
529
|
+
const verbalBases = [stem, ...hardened];
|
|
530
|
+
const infinitives = verbalBases.flatMap((v) => restoreInfinitive(v));
|
|
531
|
+
const variants = [stem, ...hardened, ...vowelDropped, ...infinitives];
|
|
532
|
+
for (const variant of variants) {
|
|
533
|
+
if (!seen.has(variant) && variant !== normalized) {
|
|
534
|
+
seen.add(variant);
|
|
535
|
+
nextFrontier.push(variant);
|
|
536
|
+
candidatesWithWeight.push({ candidate: variant, baseLength: stem.length });
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (nextFrontier.length === 0)
|
|
543
|
+
break;
|
|
544
|
+
frontier = nextFrontier;
|
|
545
|
+
}
|
|
546
|
+
candidatesWithWeight.sort((a, b) => b.baseLength - a.baseLength);
|
|
547
|
+
return [...new Set(candidatesWithWeight.map((c) => c.candidate))];
|
|
548
|
+
}
|
|
549
|
+
|
|
53
550
|
// src/tdk.ts
|
|
54
551
|
var fs = __toESM(require("fs"));
|
|
55
552
|
var path = __toESM(require("path"));
|
|
@@ -141,6 +638,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
141
638
|
static wordCache = /* @__PURE__ */ new Map();
|
|
142
639
|
static dailyContentCache = null;
|
|
143
640
|
static autocompleteCache = [];
|
|
641
|
+
static autocompleteSet = /* @__PURE__ */ new Set();
|
|
642
|
+
static stemCache = /* @__PURE__ */ new Map();
|
|
144
643
|
/**
|
|
145
644
|
* Enables or disables in-memory caching for API requests.
|
|
146
645
|
*/
|
|
@@ -157,6 +656,8 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
157
656
|
this.wordCache.clear();
|
|
158
657
|
this.dailyContentCache = null;
|
|
159
658
|
this.autocompleteCache = [];
|
|
659
|
+
this.autocompleteSet.clear();
|
|
660
|
+
this.stemCache.clear();
|
|
160
661
|
}
|
|
161
662
|
static delay(ms) {
|
|
162
663
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -263,6 +764,17 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
263
764
|
return [];
|
|
264
765
|
}
|
|
265
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Ensures TDK's ~81k headword list is loaded in memory for fast O(1) set operations.
|
|
769
|
+
*/
|
|
770
|
+
static async ensureAutocompleteLoaded() {
|
|
771
|
+
if (this.autocompleteCache.length === 0) {
|
|
772
|
+
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
773
|
+
this.autocompleteSet = new Set(
|
|
774
|
+
this.autocompleteCache.map((w) => w.toLocaleLowerCase("tr-TR"))
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
266
778
|
/**
|
|
267
779
|
* Returns autocomplete suggestions for a given prefix, searched over TDK's
|
|
268
780
|
* full headword list (see `fetchAutocompleteData`). The list is fetched
|
|
@@ -272,12 +784,81 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
272
784
|
static async getSuggestions(prefix) {
|
|
273
785
|
if (!prefix || prefix.trim() === "")
|
|
274
786
|
return [];
|
|
275
|
-
|
|
276
|
-
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
277
|
-
}
|
|
787
|
+
await this.ensureAutocompleteLoaded();
|
|
278
788
|
const cleanPrefix = prefix.trim().toLocaleLowerCase("tr-TR");
|
|
279
789
|
return this.autocompleteCache.filter((w) => w.toLocaleLowerCase("tr-TR").startsWith(cleanPrefix)).slice(0, 10);
|
|
280
790
|
}
|
|
791
|
+
/**
|
|
792
|
+
* Checks whether a word exists as a known headword in TDK dictionary.
|
|
793
|
+
* Checks in-memory autocompleteSet (81k headwords) if loaded, or queries TDK API.
|
|
794
|
+
*/
|
|
795
|
+
static async isHeadword(word2) {
|
|
796
|
+
if (!word2 || word2.trim() === "")
|
|
797
|
+
return false;
|
|
798
|
+
const clean = word2.trim().toLocaleLowerCase("tr-TR");
|
|
799
|
+
await this.ensureAutocompleteLoaded();
|
|
800
|
+
if (this.autocompleteSet.size > 0) {
|
|
801
|
+
return this.autocompleteSet.has(clean);
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
const results = await this.getWord(clean);
|
|
805
|
+
return results.length > 0;
|
|
806
|
+
} catch {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Generates candidate roots for a given Turkish word using progressive BFS suffix stripping,
|
|
812
|
+
* consonant mutation restoration, and vowel drop restoration.
|
|
813
|
+
*/
|
|
814
|
+
static getStemCandidates(word2) {
|
|
815
|
+
return getStemCandidates(word2);
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Finds the dictionary root (headword) of a word by checking direct existence
|
|
819
|
+
* and evaluating candidate stems generated by morphological analysis.
|
|
820
|
+
* Returns the root headword string if found, or null if no match in TDK.
|
|
821
|
+
*/
|
|
822
|
+
static async findRoot(word2) {
|
|
823
|
+
if (!word2 || word2.trim() === "")
|
|
824
|
+
return null;
|
|
825
|
+
const clean = word2.trim().toLocaleLowerCase("tr-TR");
|
|
826
|
+
if (this.stemCache.has(clean)) {
|
|
827
|
+
return this.stemCache.get(clean);
|
|
828
|
+
}
|
|
829
|
+
if (await this.isHeadword(clean)) {
|
|
830
|
+
this.stemCache.set(clean, clean);
|
|
831
|
+
return clean;
|
|
832
|
+
}
|
|
833
|
+
const candidates = getStemCandidates(clean);
|
|
834
|
+
for (const candidate of candidates) {
|
|
835
|
+
if (await this.isHeadword(candidate)) {
|
|
836
|
+
this.stemCache.set(clean, candidate);
|
|
837
|
+
return candidate;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
this.stemCache.set(clean, null);
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Performs morphological stemming on a Turkish word.
|
|
845
|
+
* Returns a StemResult containing the original word, resolved root, and whether it is inflected.
|
|
846
|
+
*/
|
|
847
|
+
static async stem(word2) {
|
|
848
|
+
if (!word2 || word2.trim() === "")
|
|
849
|
+
return null;
|
|
850
|
+
const clean = word2.trim().toLocaleLowerCase("tr-TR");
|
|
851
|
+
const root = await this.findRoot(word2);
|
|
852
|
+
if (!root) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
word: word2,
|
|
857
|
+
root,
|
|
858
|
+
isInflected: root !== clean,
|
|
859
|
+
candidates: getStemCandidates(word2)
|
|
860
|
+
};
|
|
861
|
+
}
|
|
281
862
|
/**
|
|
282
863
|
* Returns a list of proverbs and idioms containing the word.
|
|
283
864
|
*/
|
|
@@ -494,6 +1075,17 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
494
1075
|
return { isCorrect: false, word: word2, suggestion: mixMatch.dogru };
|
|
495
1076
|
}
|
|
496
1077
|
}
|
|
1078
|
+
const root = await this.findRoot(word2);
|
|
1079
|
+
if (root) {
|
|
1080
|
+
const cleanWord2 = word2.trim().toLocaleLowerCase("tr-TR");
|
|
1081
|
+
const isInflected = root !== cleanWord2;
|
|
1082
|
+
return {
|
|
1083
|
+
isCorrect: true,
|
|
1084
|
+
word: word2,
|
|
1085
|
+
isInflected,
|
|
1086
|
+
root
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
497
1089
|
if (this.autocompleteCache.length === 0) {
|
|
498
1090
|
this.autocompleteCache = await this.fetchAutocompleteData();
|
|
499
1091
|
}
|
|
@@ -990,13 +1582,28 @@ yDFx8r7i9vIJU5HS3moZLkYWAOilMaV9N56A9Bgb6dNcHkvg3NoaYA==
|
|
|
990
1582
|
const unique = [...new Set(words)];
|
|
991
1583
|
const analyses = [];
|
|
992
1584
|
for (const word2 of unique) {
|
|
993
|
-
|
|
994
|
-
|
|
1585
|
+
let results = await this.getWord(word2);
|
|
1586
|
+
let found = results.length > 0;
|
|
1587
|
+
let root;
|
|
1588
|
+
let isInflected;
|
|
1589
|
+
if (!found) {
|
|
1590
|
+
const resolvedRoot = await this.findRoot(word2);
|
|
1591
|
+
if (resolvedRoot) {
|
|
1592
|
+
results = await this.getWord(resolvedRoot);
|
|
1593
|
+
if (results.length > 0) {
|
|
1594
|
+
found = true;
|
|
1595
|
+
root = resolvedRoot;
|
|
1596
|
+
isInflected = true;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
995
1600
|
analyses.push({
|
|
996
1601
|
word: word2,
|
|
997
1602
|
found,
|
|
998
1603
|
meaning: found ? this.firstMeaning(results) : null,
|
|
999
|
-
origin: found ? results[0].lisan || "T\xFCrk\xE7e" : null
|
|
1604
|
+
origin: found ? results[0].lisan || "T\xFCrk\xE7e" : null,
|
|
1605
|
+
root,
|
|
1606
|
+
isInflected
|
|
1000
1607
|
});
|
|
1001
1608
|
await this.delay(200);
|
|
1002
1609
|
}
|
|
@@ -1102,6 +1709,9 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
1102
1709
|
"hece",
|
|
1103
1710
|
"uyum",
|
|
1104
1711
|
"yazim",
|
|
1712
|
+
"kok",
|
|
1713
|
+
"stem",
|
|
1714
|
+
"deyim",
|
|
1105
1715
|
"gunun",
|
|
1106
1716
|
"rastgele",
|
|
1107
1717
|
"esanlam",
|
|
@@ -1140,7 +1750,7 @@ async function run() {
|
|
|
1140
1750
|
if (!command || command === "--help" || command === "-h") {
|
|
1141
1751
|
console.log("Kullan\u0131m: tdk [komut] <kelime> [--json]");
|
|
1142
1752
|
console.log(
|
|
1143
|
-
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, kubbealti, nisanyan, viki"
|
|
1753
|
+
"Komutlar: ara, anlam, koken, ornek, hece, uyum, yazim, kok, deyim, gunun, rastgele, esanlam, karsit, yabanci, kurallar, kural, karsilastir, analiz, oneri, kubbealti, nisanyan, viki"
|
|
1144
1754
|
);
|
|
1145
1755
|
console.log("Not: Komut belirtilmezse do\u011Frudan kelime anlam\u0131 aran\u0131r (\xF6rn: tdk selam)");
|
|
1146
1756
|
process.exit(command ? 0 : 1);
|
|
@@ -1208,13 +1818,46 @@ async function run() {
|
|
|
1208
1818
|
const spellResult = await TDK.checkSpelling(word);
|
|
1209
1819
|
printResult(spellResult, () => {
|
|
1210
1820
|
if (spellResult.isCorrect) {
|
|
1211
|
-
|
|
1821
|
+
if (spellResult.isInflected && spellResult.root) {
|
|
1822
|
+
console.log(`Do\u011Fru yaz\u0131m (\xE7ekimli bi\xE7im, k\xF6k: ${spellResult.root}).`);
|
|
1823
|
+
} else {
|
|
1824
|
+
console.log("Do\u011Fru yaz\u0131m.");
|
|
1825
|
+
}
|
|
1212
1826
|
} else {
|
|
1213
1827
|
console.log(`Yanl\u0131\u015F yaz\u0131m.${spellResult.suggestion ? " Do\u011Frusu: " + spellResult.suggestion : ""}`);
|
|
1214
1828
|
}
|
|
1215
1829
|
});
|
|
1216
1830
|
break;
|
|
1217
1831
|
}
|
|
1832
|
+
case "kok":
|
|
1833
|
+
case "stem": {
|
|
1834
|
+
if (!word)
|
|
1835
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
1836
|
+
const stemResult = await TDK.stem(word);
|
|
1837
|
+
printResult(stemResult, () => {
|
|
1838
|
+
if (!stemResult) {
|
|
1839
|
+
console.log("K\xF6k bulunamad\u0131.");
|
|
1840
|
+
} else if (stemResult.isInflected) {
|
|
1841
|
+
console.log(`K\xF6k: ${stemResult.root} (\xE7ekimli bi\xE7im)`);
|
|
1842
|
+
} else {
|
|
1843
|
+
console.log(`K\xF6k: ${stemResult.root} (yal\u0131n bi\xE7im)`);
|
|
1844
|
+
}
|
|
1845
|
+
});
|
|
1846
|
+
break;
|
|
1847
|
+
}
|
|
1848
|
+
case "deyim": {
|
|
1849
|
+
if (!word)
|
|
1850
|
+
throw new Error("Kelime belirtmelisiniz.");
|
|
1851
|
+
const proverbs = await TDK.getProverbs(word);
|
|
1852
|
+
printResult(proverbs, () => {
|
|
1853
|
+
if (proverbs.length === 0) {
|
|
1854
|
+
console.log("Atas\xF6z\xFC/deyim bulunamad\u0131.");
|
|
1855
|
+
} else {
|
|
1856
|
+
proverbs.forEach((p, i) => console.log(`${i + 1}. ${p}`));
|
|
1857
|
+
}
|
|
1858
|
+
});
|
|
1859
|
+
break;
|
|
1860
|
+
}
|
|
1218
1861
|
case "gunun": {
|
|
1219
1862
|
const wotd = await TDK.getWordOfTheDay();
|
|
1220
1863
|
printResult(wotd, () => {
|
|
@@ -1321,7 +1964,8 @@ async function run() {
|
|
|
1321
1964
|
} else {
|
|
1322
1965
|
analysis.forEach((a) => {
|
|
1323
1966
|
if (a.found) {
|
|
1324
|
-
|
|
1967
|
+
const rootLabel = a.isInflected && a.root ? ` (k\xF6k: ${a.root})` : "";
|
|
1968
|
+
console.log(`${a.word}${rootLabel}: ${a.meaning ?? "-"} (${a.origin})`);
|
|
1325
1969
|
} else {
|
|
1326
1970
|
console.log(`${a.word}: bulunamad\u0131`);
|
|
1327
1971
|
}
|