@cachly-dev/mcp-server 0.5.50 → 0.5.70
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/dist/index.js +523 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +451 -40
package/src/index.ts
CHANGED
|
@@ -55,7 +55,7 @@ import { Redis } from 'ioredis';
|
|
|
55
55
|
const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
|
|
56
56
|
const JWT = process.env.CACHLY_JWT ?? '';
|
|
57
57
|
const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
|
|
58
|
-
const CURRENT_VERSION = '0.5.
|
|
58
|
+
const CURRENT_VERSION = '0.5.70';
|
|
59
59
|
/**
|
|
60
60
|
* ┌──────────────────────────────────────────────────────────────────────┐
|
|
61
61
|
* │ EMBEDDING PROVIDER — pluggable, client-side first │
|
|
@@ -434,6 +434,40 @@ const STOPWORDS = new Set([
|
|
|
434
434
|
'मैं', 'तुम', 'उन', 'इन', 'ने', 'बहुत', 'सब', 'कुछ', 'फिर',
|
|
435
435
|
'अब', 'तो', 'ही', 'तरह', 'जैसे', 'करना', 'होना', 'रहा', 'रही',
|
|
436
436
|
'रहे', 'गया', 'गई', 'गए', 'किया', 'किए', 'कर', 'हुआ', 'हुई',
|
|
437
|
+
// ── Russian — common prepositions, pronouns, auxiliary verbs ───────────────
|
|
438
|
+
'в', 'на', 'не', 'с', 'и', 'а', 'но', 'по', 'за', 'из', 'от', 'до',
|
|
439
|
+
'к', 'у', 'о', 'об', 'во', 'при', 'под', 'над', 'без', 'для',
|
|
440
|
+
'что', 'как', 'это', 'все', 'так', 'уже', 'или', 'же', 'ли',
|
|
441
|
+
'если', 'то', 'да', 'нет', 'был', 'была', 'были', 'быть', 'есть',
|
|
442
|
+
'его', 'её', 'их', 'этот', 'эта', 'эти', 'который', 'которая',
|
|
443
|
+
'которые', 'который', 'мой', 'моя', 'мои', 'ваш', 'ваша', 'ваши',
|
|
444
|
+
'он', 'она', 'они', 'мы', 'вы', 'я', 'ты', 'тот', 'та', 'те',
|
|
445
|
+
'очень', 'тоже', 'также', 'когда', 'где', 'как', 'почему', 'зачем',
|
|
446
|
+
'сейчас', 'здесь', 'там', 'потому', 'поэтому', 'чтобы', 'тут',
|
|
447
|
+
// ── Turkish — common particles, postpositions, copulas ─────────────────────
|
|
448
|
+
'bir', 'bu', 'şu', 'o', 've', 'ile', 'de', 'da', 'için', 'gibi',
|
|
449
|
+
'ama', 'fakat', 'ancak', 'ya', 'ne', 'ki', 'mi', 'mı', 'mu', 'mü',
|
|
450
|
+
'var', 'yok', 'olan', 'oldu', 'olur', 'olarak', 'ise', 'daha',
|
|
451
|
+
'çok', 'en', 'her', 'hiç', 'bazı', 'tüm', 'bütün', 'hem', 'veya',
|
|
452
|
+
'bu', 'şu', 'ben', 'sen', 'biz', 'siz', 'onlar', 'benim', 'senin',
|
|
453
|
+
'nasıl', 'neden', 'niçin', 'nerede', 'ne', 'hangi', 'kaç',
|
|
454
|
+
// ── Polish — common prepositions, pronouns, particles ─────────────────────
|
|
455
|
+
'w', 'na', 'z', 'do', 'się', 'że', 'to', 'jest', 'i', 'a', 'nie',
|
|
456
|
+
'tak', 'jak', 'czy', 'po', 'o', 'ale', 'go', 'mu', 'jej', 'ich',
|
|
457
|
+
'tego', 'tej', 'te', 'ten', 'ta', 'są', 'był', 'była', 'było', 'byli',
|
|
458
|
+
'będzie', 'będą', 'ma', 'mam', 'masz', 'mają', 'ze', 'co', 'już',
|
|
459
|
+
'przez', 'przy', 'za', 'bez', 'nad', 'pod', 'przed', 'po', 'między',
|
|
460
|
+
'kiedy', 'gdzie', 'dlaczego', 'który', 'która', 'które', 'tylko', 'też',
|
|
461
|
+
// ── Czech — common words ──────────────────────────────────────────────────
|
|
462
|
+
'v', 'na', 'z', 'do', 'se', 'že', 'to', 'je', 'a', 'ne', 'pro',
|
|
463
|
+
'ale', 'jak', 'by', 'byl', 'být', 'jsem', 'jsou', 'má', 'mám',
|
|
464
|
+
'ten', 'ta', 'ty', 'tato', 'jeho', 'její', 'jejich', 'také', 'jen',
|
|
465
|
+
'kde', 'kdy', 'proč', 'který', 'která', 'které', 'co', 'při', 'bez',
|
|
466
|
+
// ── Bengali — common particles, pronouns, verbs ───────────────────────────
|
|
467
|
+
'এই', 'এটি', 'এটা', 'এর', 'তার', 'তারা', 'আমি', 'তুমি', 'সে', 'আমরা',
|
|
468
|
+
'এবং', 'কিন্তু', 'বা', 'না', 'হ্যাঁ', 'যে', 'কি', 'কে', 'কী', 'থেকে',
|
|
469
|
+
'দিয়ে', 'জন্য', 'সাথে', 'মধ্যে', 'উপর', 'নিচে', 'আছে', 'ছিল', 'হয়',
|
|
470
|
+
'করা', 'করে', 'করেছে', 'হবে', 'হয়েছে', 'একটি', 'একটা', 'অনেক', 'সব',
|
|
437
471
|
]);
|
|
438
472
|
|
|
439
473
|
|
|
@@ -450,6 +484,12 @@ const ARABIC_CHAR_RE = /[\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb50-\ufdff\u
|
|
|
450
484
|
const FARSI_CHAR_RE = /[\u067e\u0686\u0698\u06af\u06cc]/;
|
|
451
485
|
// Devanagari (Hindi, Sanskrit, Marathi …) — U+0900–U+097F + extended
|
|
452
486
|
const DEVANAGARI_RE = /[\u0900-\u097f]/;
|
|
487
|
+
// Cyrillic (Russian, Bulgarian, Ukrainian, Serbian …) — U+0400–U+04FF
|
|
488
|
+
const CYRILLIC_RE = /[\u0400-\u04ff]/;
|
|
489
|
+
// Turkish uses Latin alphabet but has unique chars (ğ, ş, ı, ö, ü, ç) — detected by these
|
|
490
|
+
const TURKISH_CHAR_RE = /[\u011f\u015f\u0131\u0130\u00e7]/; // ğ ş ı İ ç
|
|
491
|
+
// Bengali (Bangla) — U+0980–U+09FF
|
|
492
|
+
const BENGALI_RE = /[\u0980-\u09ff]/;
|
|
453
493
|
|
|
454
494
|
// ── Katakana → Romaji conversion table (Hepburn system) ──────────────────────
|
|
455
495
|
// Digraphs must be listed before single chars so they match first.
|
|
@@ -611,10 +651,155 @@ function hindiLightStem(word: string): string {
|
|
|
611
651
|
if (result.endsWith('\u0915\u0930') && result.length > 4) return result.slice(0, -2); // \u0915\u0930 (kar) conjunctive
|
|
612
652
|
if (result.endsWith('\u0913\u0902') && result.length > 4) return result.slice(0, -2); // \u0913\u0902 (oṃ) oblique plural
|
|
613
653
|
if (result.endsWith('\u0907\u092f\u093e\u0902') && result.length > 5) return result.slice(0, -4); // \u0907\u092f\u093e\u0902 (iyāṃ) f plural
|
|
614
|
-
if (result.endsWith('\u0940\u092f\u093e\u0902') && result.length > 5) return result.slice(0, -4); // \u0940\u092f\u093e\
|
|
654
|
+
if (result.endsWith('\u0940\u092f\u093e\u0902') && result.length > 5) return result.slice(0, -4); // \u0940\u092f\u093e\u0102 (īyāṃ)
|
|
615
655
|
return result;
|
|
616
656
|
}
|
|
617
657
|
|
|
658
|
+
/**
|
|
659
|
+
* Russian light stemmer — strips common inflectional suffixes.
|
|
660
|
+
* Handles the most frequent verb/noun/adjective endings to improve recall.
|
|
661
|
+
* Only strips when result is still ≥ 3 chars.
|
|
662
|
+
*
|
|
663
|
+
* Covers:
|
|
664
|
+
* ошибки/ошибка → ошибк (noun plural/genitive)
|
|
665
|
+
* развёртывание → развёртыва (gerund → stem)
|
|
666
|
+
* установить → установ (infinitive)
|
|
667
|
+
* настройки → настройк (genitive plural)
|
|
668
|
+
*/
|
|
669
|
+
function russianLightStem(word: string): string {
|
|
670
|
+
let w = word;
|
|
671
|
+
// Longest first to avoid partial stripping
|
|
672
|
+
// Verb infinitives / gerunds
|
|
673
|
+
if (w.endsWith('ывание') && w.length > 7) return w.slice(0, -6);
|
|
674
|
+
if (w.endsWith('ивание') && w.length > 7) return w.slice(0, -6);
|
|
675
|
+
if (w.endsWith('ование') && w.length > 7) return w.slice(0, -6);
|
|
676
|
+
if (w.endsWith('вание') && w.length > 6) return w.slice(0, -5);
|
|
677
|
+
if (w.endsWith('ение') && w.length > 5) return w.slice(0, -4);
|
|
678
|
+
if (w.endsWith('ание') && w.length > 5) return w.slice(0, -4);
|
|
679
|
+
if (w.endsWith('ить') && w.length > 4) return w.slice(0, -3);
|
|
680
|
+
if (w.endsWith('ать') && w.length > 4) return w.slice(0, -3);
|
|
681
|
+
if (w.endsWith('еть') && w.length > 4) return w.slice(0, -3);
|
|
682
|
+
if (w.endsWith('уть') && w.length > 4) return w.slice(0, -3);
|
|
683
|
+
// Noun plural/genitive
|
|
684
|
+
if (w.endsWith('ки') && w.length > 4) return w.slice(0, -2);
|
|
685
|
+
if (w.endsWith('ги') && w.length > 4) return w.slice(0, -2);
|
|
686
|
+
if (w.endsWith('ов') && w.length > 4) return w.slice(0, -2);
|
|
687
|
+
if (w.endsWith('ев') && w.length > 4) return w.slice(0, -2);
|
|
688
|
+
if (w.endsWith('ей') && w.length > 4) return w.slice(0, -2);
|
|
689
|
+
if (w.endsWith('ий') && w.length > 4) return w.slice(0, -2);
|
|
690
|
+
if (w.endsWith('ый') && w.length > 4) return w.slice(0, -2);
|
|
691
|
+
if (w.endsWith('ая') && w.length > 4) return w.slice(0, -2);
|
|
692
|
+
if (w.endsWith('ое') && w.length > 4) return w.slice(0, -2);
|
|
693
|
+
if (w.endsWith('ую') && w.length > 4) return w.slice(0, -2);
|
|
694
|
+
// Verb present tense
|
|
695
|
+
if (w.endsWith('ет') && w.length > 4) return w.slice(0, -2);
|
|
696
|
+
if (w.endsWith('ют') && w.length > 4) return w.slice(0, -2);
|
|
697
|
+
if (w.endsWith('ут') && w.length > 4) return w.slice(0, -2);
|
|
698
|
+
if (w.endsWith('ят') && w.length > 4) return w.slice(0, -2);
|
|
699
|
+
return w;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Turkish light stemmer — strips common agglutinative suffixes.
|
|
704
|
+
* Turkish is highly agglutinative; this covers the most common inflectional ends.
|
|
705
|
+
*
|
|
706
|
+
* Covers:
|
|
707
|
+
* hatalar → hata (-lar/-ler plural)
|
|
708
|
+
* sunucuda → sunucu (-da/-de locative)
|
|
709
|
+
* dağıtımı → dağıtım (-ı/-i/-u/-ü accusative)
|
|
710
|
+
* yüklemek → yükle (-mek/-mak infinitive)
|
|
711
|
+
*/
|
|
712
|
+
function turkishLightStem(word: string): string {
|
|
713
|
+
let w = word;
|
|
714
|
+
// Suffixes longest-first
|
|
715
|
+
if (w.endsWith('lardaki') && w.length > 7) return w.slice(0, -7);
|
|
716
|
+
if (w.endsWith('lerdeki') && w.length > 7) return w.slice(0, -7);
|
|
717
|
+
if (w.endsWith('ların') && w.length > 6) return w.slice(0, -5);
|
|
718
|
+
if (w.endsWith('lerin') && w.length > 6) return w.slice(0, -5);
|
|
719
|
+
if (w.endsWith('larda') && w.length > 6) return w.slice(0, -5);
|
|
720
|
+
if (w.endsWith('lerde') && w.length > 6) return w.slice(0, -5);
|
|
721
|
+
if (w.endsWith('ları') && w.length > 5) return w.slice(0, -4);
|
|
722
|
+
if (w.endsWith('leri') && w.length > 5) return w.slice(0, -4);
|
|
723
|
+
if (w.endsWith('mek') && w.length > 4) return w.slice(0, -3);
|
|
724
|
+
if (w.endsWith('mak') && w.length > 4) return w.slice(0, -3);
|
|
725
|
+
if (w.endsWith('ler') && w.length > 4) return w.slice(0, -3);
|
|
726
|
+
if (w.endsWith('lar') && w.length > 4) return w.slice(0, -3);
|
|
727
|
+
if (w.endsWith('nın') && w.length > 4) return w.slice(0, -3);
|
|
728
|
+
if (w.endsWith('nin') && w.length > 4) return w.slice(0, -3);
|
|
729
|
+
if (w.endsWith('nun') && w.length > 4) return w.slice(0, -3);
|
|
730
|
+
if (w.endsWith('nün') && w.length > 4) return w.slice(0, -3);
|
|
731
|
+
if (w.endsWith('da') && w.length > 4) return w.slice(0, -2);
|
|
732
|
+
if (w.endsWith('de') && w.length > 4) return w.slice(0, -2);
|
|
733
|
+
if (w.endsWith('ta') && w.length > 4) return w.slice(0, -2);
|
|
734
|
+
if (w.endsWith('te') && w.length > 4) return w.slice(0, -2);
|
|
735
|
+
if (w.endsWith('dan') && w.length > 4) return w.slice(0, -3);
|
|
736
|
+
if (w.endsWith('den') && w.length > 4) return w.slice(0, -3);
|
|
737
|
+
if (w.endsWith('tan') && w.length > 4) return w.slice(0, -3);
|
|
738
|
+
if (w.endsWith('ten') && w.length > 4) return w.slice(0, -3);
|
|
739
|
+
if (w.endsWith('ın') && w.length > 4) return w.slice(0, -2);
|
|
740
|
+
if (w.endsWith('in') && w.length > 4) return w.slice(0, -2);
|
|
741
|
+
if (w.endsWith('un') && w.length > 4) return w.slice(0, -2);
|
|
742
|
+
if (w.endsWith('ün') && w.length > 4) return w.slice(0, -2);
|
|
743
|
+
if (w.endsWith('ı') && w.length > 4) return w.slice(0, -1);
|
|
744
|
+
if (w.endsWith('i') && w.length > 4) return w.slice(0, -1);
|
|
745
|
+
if (w.endsWith('u') && w.length > 4) return w.slice(0, -1);
|
|
746
|
+
if (w.endsWith('ü') && w.length > 4) return w.slice(0, -1);
|
|
747
|
+
return w;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Bengali (Bangla) light stemmer — strips common verbal and nominal suffixes.
|
|
752
|
+
* Bengali is an Indo-Aryan language with moderate morphological complexity.
|
|
753
|
+
* Only strips when result is still ≥ 2 chars.
|
|
754
|
+
*
|
|
755
|
+
* Covers:
|
|
756
|
+
* করছে/করেছে → কর (progressive/perfect aspect)
|
|
757
|
+
* সার্ভারগুলো → সার্ভার (-গুলো plural)
|
|
758
|
+
* সমস্যাটি → সমস্যা (-টি singular marker)
|
|
759
|
+
*/
|
|
760
|
+
function bengaliLightStem(word: string): string {
|
|
761
|
+
let w = word;
|
|
762
|
+
// Plural / collective markers (longest first)
|
|
763
|
+
if (w.endsWith('\u0997\u09c1\u09b2\u09cb') && w.length > 5) return w.slice(0, -4); // গুলো (-gulo plural)
|
|
764
|
+
if (w.endsWith('\u0997\u09c1\u09b2\u09bf') && w.length > 5) return w.slice(0, -4); // গুলি (-guli plural)
|
|
765
|
+
if (w.endsWith('\u09a6\u09c7\u09b0') && w.length > 4) return w.slice(0, -3); // দের (genitive plural)
|
|
766
|
+
if (w.endsWith('\u09a6\u09bf\u0997\u09c7') && w.length > 5) return w.slice(0, -4); // দিগে
|
|
767
|
+
// Definiteness / case markers
|
|
768
|
+
if (w.endsWith('\u099f\u09bf') && w.length > 3) return w.slice(0, -2); // টি (singular definite)
|
|
769
|
+
if (w.endsWith('\u099f\u09be') && w.length > 3) return w.slice(0, -2); // টা (singular definite)
|
|
770
|
+
if (w.endsWith('\u0996\u09be\u09a8\u09be') && w.length > 5) return w.slice(0, -4); // খানা
|
|
771
|
+
// Verbal suffixes
|
|
772
|
+
if (w.endsWith('\u099b\u09c7') && w.length > 3) return w.slice(0, -2); // ছে (progressive)
|
|
773
|
+
if (w.endsWith('\u099b\u09bf\u09b2') && w.length > 4) return w.slice(0, -3); // ছিল (past progressive)
|
|
774
|
+
if (w.endsWith('\u09af\u09be\u09ac\u09c7') && w.length > 5) return w.slice(0, -4); // যাবে (future)
|
|
775
|
+
if (w.endsWith('\u0995\u09b0\u09be') && w.length > 4) return w.slice(0, -3); // করা (infinitive)
|
|
776
|
+
if (w.endsWith('\u0995\u09b0\u09c7') && w.length > 4) return w.slice(0, -3); // করে (present)
|
|
777
|
+
if (w.endsWith('\u09b9\u09df') && w.length > 3) return w.slice(0, -2); // হয় (is/becomes)
|
|
778
|
+
if (w.endsWith('\u09b9\u09ac\u09c7') && w.length > 4) return w.slice(0, -3); // হবে (will be)
|
|
779
|
+
return w;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Pre-process text BEFORE tokenization to split code identifiers.
|
|
784
|
+
* Handles camelCase, PascalCase, snake_case, and kebab-case.
|
|
785
|
+
*
|
|
786
|
+
* Examples:
|
|
787
|
+
* deployServer → deploy Server (then lowercased → deploy server)
|
|
788
|
+
* HTMLParser → HTML Parser
|
|
789
|
+
* my_api_key → my api key
|
|
790
|
+
* get-user-by-id → get user by id (hyphens preserved in regex, spaces also fine)
|
|
791
|
+
* BackendServiceImpl → Backend Service Impl
|
|
792
|
+
*/
|
|
793
|
+
function preprocessText(text: string): string {
|
|
794
|
+
return text
|
|
795
|
+
// camelCase boundary: lowercase/digit → uppercase
|
|
796
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
797
|
+
// Consecutive uppercase → uppercase+lowercase boundary: HTMLParser → HTML Parser
|
|
798
|
+
.replace(/([A-Z]{2,})([A-Z][a-z])/g, '$1 $2')
|
|
799
|
+
// snake_case: replace underscores with spaces
|
|
800
|
+
.replace(/_/g, ' ');
|
|
801
|
+
}
|
|
802
|
+
|
|
618
803
|
|
|
619
804
|
// Maps tokens (English, Japanese katakana, Chinese, Korean, Arabic, Hebrew)
|
|
620
805
|
// to their equivalents in other languages. Used to expand tokens at search time
|
|
@@ -622,46 +807,104 @@ function hindiLightStem(word: string): string {
|
|
|
622
807
|
//
|
|
623
808
|
// Keys must be lowercase. CJK values will be bigram-expanded at use time.
|
|
624
809
|
const CROSS_LINGUAL_MAP = new Map<string, string[]>([
|
|
810
|
+
|
|
811
|
+
// ── Tech / Ecosystem synonyms (language-agnostic) ─────────────────────────
|
|
812
|
+
['javascript', ['js', 'node', 'nodejs', 'ecmascript']],
|
|
813
|
+
['js', ['javascript', 'node', 'nodejs']],
|
|
814
|
+
['typescript', ['ts', 'javascript', 'js', 'tsc']],
|
|
815
|
+
['ts', ['typescript', 'javascript', 'js']],
|
|
816
|
+
['python', ['py', 'pip', 'django', 'flask', 'fastapi']],
|
|
817
|
+
['py', ['python', 'pip']],
|
|
818
|
+
['golang', ['go', 'gopher', 'goroutine']],
|
|
819
|
+
['go', ['golang', 'goroutine']],
|
|
820
|
+
['rust', ['cargo', 'rustlang', 'crates']],
|
|
821
|
+
['cargo', ['rust', 'rustlang']],
|
|
822
|
+
['java', ['jvm', 'maven', 'gradle', 'spring']],
|
|
823
|
+
['kotlin', ['jvm', 'android', 'coroutine']],
|
|
824
|
+
['dotnet', ['csharp', 'asp', 'nuget', 'aspnet']],
|
|
825
|
+
['postgresql', ['postgres', 'pg', 'psql']],
|
|
826
|
+
['postgres', ['postgresql', 'pg', 'psql']],
|
|
827
|
+
['mysql', ['mariadb', 'sql']],
|
|
828
|
+
['mongodb', ['mongo', 'nosql', 'bson', 'mongoose']],
|
|
829
|
+
['mongo', ['mongodb', 'nosql', 'bson']],
|
|
830
|
+
['elasticsearch', ['elastic', 'opensearch', 'lucene', 'kibana']],
|
|
831
|
+
['elastic', ['elasticsearch', 'opensearch']],
|
|
832
|
+
['kubernetes', ['k8s', 'kube', 'kubectl', 'helm', 'k3s']],
|
|
833
|
+
['k8s', ['kubernetes', 'kube', 'kubectl', 'helm']],
|
|
834
|
+
['helm', ['kubernetes', 'k8s', 'chart']],
|
|
835
|
+
['terraform', ['tf', 'hcl', 'iac', 'opentofu']],
|
|
836
|
+
['ansible', ['playbook', 'automation']],
|
|
837
|
+
['grafana', ['dashboard', 'visualization', 'metrics']],
|
|
838
|
+
['prometheus', ['metrics', 'alert', 'scrape', 'grafana']],
|
|
839
|
+
['nginx', ['proxy', 'webserver', 'ingress']],
|
|
840
|
+
['traefik', ['proxy', 'ingress', 'router']],
|
|
841
|
+
['rabbitmq', ['amqp', 'queue', 'broker', 'messaging']],
|
|
842
|
+
['kafka', ['messaging', 'stream', 'broker']],
|
|
843
|
+
['grpc', ['protobuf', 'proto', 'rpc']],
|
|
844
|
+
['graphql', ['gql', 'resolver', 'schema']],
|
|
845
|
+
['react', ['jsx', 'hooks', 'component', 'redux']],
|
|
846
|
+
['nextjs', ['next', 'react', 'ssr', 'vercel']],
|
|
847
|
+
['vue', ['vuejs', 'vite', 'nuxt']],
|
|
848
|
+
['angular', ['ng', 'rxjs', 'typescript']],
|
|
849
|
+
['aws', ['amazon', 'ec2', 's3', 'lambda', 'cloudwatch', 'ecs', 'eks']],
|
|
850
|
+
['gcp', ['google cloud', 'gke', 'bigquery']],
|
|
851
|
+
['azure', ['microsoft cloud', 'aks', 'devops']],
|
|
852
|
+
['s3', ['bucket', 'object storage', 'aws']],
|
|
853
|
+
['lambda', ['serverless', 'function', 'faas', 'aws']],
|
|
854
|
+
['jwt', ['token', 'bearer', 'oauth', 'auth']],
|
|
855
|
+
['oauth', ['oidc', 'auth', 'token', 'sso']],
|
|
856
|
+
['webhook', ['callback', 'event', 'trigger']],
|
|
857
|
+
['cron', ['schedule', 'job', 'timer']],
|
|
858
|
+
['yaml', ['yml', 'config', 'manifest']],
|
|
859
|
+
['dotenv', ['env', 'environment', 'envfile']],
|
|
860
|
+
['github', ['git', 'actions', 'repo', 'ci']],
|
|
861
|
+
['gitlab', ['git', 'pipeline', 'runner']],
|
|
862
|
+
['wireguard', ['vpn', 'tunnel', 'wg']],
|
|
863
|
+
['prisma', ['orm', 'database', 'schema', 'migration']],
|
|
864
|
+
['npm', ['node', 'package', 'registry', 'yarn', 'pnpm']],
|
|
865
|
+
['yarn', ['npm', 'package', 'node']],
|
|
866
|
+
['pip', ['python', 'package', 'pypi']],
|
|
867
|
+
['brew', ['homebrew', 'macos', 'package']],
|
|
625
868
|
// English → all
|
|
626
|
-
['deploy', ['デプロイ', '部署', '배포', 'deployment', 'deploying', 'نشر', 'פריסה', 'استقرار', 'तैनाती', 'bereitstellen', 'bereitstellung', 'déployer', 'déploiement', 'desplegar', 'despliegue', 'distribuire', 'distribuzione', 'implantar', 'implantação']],
|
|
627
|
-
['deployment', ['デプロイ', '部署', '배포', 'deploy', 'نشر', 'פריסה', 'استقرار', 'तैनाती', 'bereitstellung', 'déploiement', 'despliegue', 'distribuzione', 'implantação']],
|
|
869
|
+
['deploy', ['デプロイ', '部署', '배포', 'deployment', 'deploying', 'نشر', 'פריסה', 'استقرار', 'तैनाती', 'bereitstellen', 'bereitstellung', 'déployer', 'déploiement', 'desplegar', 'despliegue', 'distribuire', 'distribuzione', 'implantar', 'implantação', 'развёртывание', 'развертывание', 'dağıtım', 'dağıtmak']],
|
|
870
|
+
['deployment', ['デプロイ', '部署', '배포', 'deploy', 'نشر', 'פריסה', 'استقرار', 'तैनाती', 'bereitstellung', 'déploiement', 'despliegue', 'distribuzione', 'implantação', 'развёртывание', 'dağıtım']],
|
|
628
871
|
['container', ['コンテナ', '容器', '컨테이너', 'docker']],
|
|
629
|
-
['server', ['サーバー', 'サーバ', '服务器', '서버', 'سيرفر', 'שרת', 'سرور', 'सर्वर']],
|
|
872
|
+
['server', ['サーバー', 'サーバ', '服务器', '서버', 'سيرفر', 'שרת', 'سرور', 'सर्वर', 'сервер', 'sunucu']],
|
|
630
873
|
['database', ['データベース', '数据库', '데이터베이스', 'db', 'قاعدة البيانات', 'datenbank', 'پایگاه داده', 'डेटाबेس', 'base de données', 'base de datos', 'banco de dados']],
|
|
631
|
-
['cache', ['キャッシュ', '缓存', '캐시', 'كاش', 'מטמון', 'کش', 'कैश']],
|
|
632
|
-
['error', ['エラー', '错误', '오류', 'خطأ', '
|
|
874
|
+
['cache', ['キャッシュ', '缓存', '캐시', 'كاش', 'מטמון', 'کش', 'कैश', 'кэш', 'önbellek']],
|
|
875
|
+
['error', ['エラー', '错误', '오류', 'خطأ', 'שגיאה', 'خطا', 'त्रुटि', 'गलती', 'exception', 'err', 'fehler', 'erreur', 'fallo', 'errore', 'erro', 'ошибка', 'hata', 'hatalı']],
|
|
633
876
|
['bug', ['バグ', '缺陷', '버그', 'issue', 'defect', 'خلل', 'اشکال', 'बग']],
|
|
634
|
-
['fix', ['修正', '修复', '수정', 'bugfix', 'patch', 'hotfix', 'إصلاح', 'תיקון', 'رفع', 'सुधार', 'beheben', 'behoben', 'réparer', 'correction', 'arreglar', 'corrección', 'correggere', 'corrigir']],
|
|
635
|
-
['build', ['ビルド', '构建', '빌드', 'بناء', 'בנייה', 'ساخت', 'निर्माण', 'bauen', 'construire', 'construir', 'costruire']],
|
|
636
|
-
['test', ['テスト', '测试', '테스트', 'اختبار', 'בדיקה', 'آزمایش', 'परीक्षण', 'testen', 'tester', 'probar', 'testare', 'testar']],
|
|
637
|
-
['auth', ['認証', '认证', '인증', 'authentication', 'login', 'oauth', 'مصادقة', 'אימות', 'احراز هویت', 'प्रमाणीकरण', 'authentifizierung', 'authentification', 'autenticación', 'autenticazione', 'autenticação']],
|
|
638
|
-
['authentication', ['auth', 'مصادقة', 'אימות', 'احراز هویت', 'प्रमाणीकरण', '認証', '认证', '인증', 'login', 'oauth']],
|
|
639
|
-
['environment', ['環境', '环境', '환경', 'env', 'بيئة', 'סביבה', 'umgebung', 'environnement', 'entorno', 'ambiente']],
|
|
640
|
-
['secret', ['key', 'token', 'password', 'jwt', 'مفتاح', 'מפתח', 'geheimnis', 'schlüssel', 'clave', 'clé', 'chiave', 'chave']],
|
|
641
|
-
['problem', ['error', 'issue', 'bug', 'failure', 'مشكلة', '
|
|
877
|
+
['fix', ['修正', '修复', '수정', 'bugfix', 'patch', 'hotfix', 'إصلاح', 'תיקון', 'رفع', 'सुधार', 'beheben', 'behoben', 'réparer', 'correction', 'arreglar', 'corrección', 'correggere', 'corrigir', 'исправить', 'исправление', 'düzeltmek', 'düzeltme']],
|
|
878
|
+
['build', ['ビルド', '构建', '빌드', 'بناء', 'בנייה', 'ساخت', 'निर्माण', 'bauen', 'construire', 'construir', 'costruire', 'сборка', 'собрать', 'derleme', 'derlemek']],
|
|
879
|
+
['test', ['テスト', '测试', '테스트', 'اختبار', 'בדיקה', 'آزمایش', 'परीक्षण', 'testen', 'tester', 'probar', 'testare', 'testar', 'тест', 'тестирование', 'test', 'testlemek']],
|
|
880
|
+
['auth', ['認証', '认证', '인증', 'authentication', 'login', 'oauth', 'مصادقة', 'אימות', 'احراز هویت', 'प्रमाणीकरण', 'authentifizierung', 'authentification', 'autenticación', 'autenticazione', 'autenticação', 'аутентификация', 'авторизация', 'kimlik doğrulama', 'yetkilendirme']],
|
|
881
|
+
['authentication', ['auth', 'مصادقة', 'אימות', 'احراز هویت', 'प्रमाणीकरण', '認証', '认证', '인증', 'login', 'oauth', 'аутентификация', 'авторизация', 'kimlik doğrulama']],
|
|
882
|
+
['environment', ['環境', '环境', '환경', 'env', 'بيئة', 'סביבה', 'umgebung', 'environnement', 'entorno', 'ambiente', 'среда', 'окружение', 'ortam']],
|
|
883
|
+
['secret', ['key', 'token', 'password', 'jwt', 'مفتاح', 'מפתח', 'geheimnis', 'schlüssel', 'clave', 'clé', 'chiave', 'chave', 'секрет', 'ключ', 'gizli', 'anahtar']],
|
|
884
|
+
['problem', ['error', 'issue', 'bug', 'failure', 'مشكلة', '問題', '오류', 'fehler', 'problème', 'problema', 'проблема', 'ошибка', 'sorun', 'problem']],
|
|
642
885
|
['kubernetes', ['クベルネテス', 'k8s', 'kube']],
|
|
643
|
-
['network', ['ネットワーク', '网络', '네트워크', 'شبكة', 'רשת', 'شبکه', 'नेटवर्क', 'netzwerk', 'réseau', 'red', 'rete', 'rede']],
|
|
886
|
+
['network', ['ネットワーク', '网络', '네트워크', 'شبكة', 'רשת', 'شبکه', 'नेटवर्क', 'netzwerk', 'réseau', 'red', 'rete', 'rede', 'сеть', 'ağ']],
|
|
644
887
|
['timeout', ['タイムアウト', '超时', '타임아웃', 'مهلة']],
|
|
645
|
-
['memory', ['メモリ', '内存', '메모리', 'ram', 'ذاكرة', 'זיכרון', 'حافظه', 'मेमोरी', 'speicher', 'mémoire', 'memoria', 'memória']],
|
|
646
|
-
['config', ['設定', '配置', '설정', 'configuration', 'settings', 'conf', 'إعدادات', 'הגדרות', 'پیکربندی', 'कॉन्फ़िग', 'konfiguration', 'einstellungen', 'configuración', 'configurazione', 'configuração']],
|
|
647
|
-
['install', ['インストール', '安装', '설치', 'تثبيت', 'התקנה', 'نصب', 'इंस्टॉल', 'installieren', 'installer', 'instalar', 'installare']],
|
|
648
|
-
['update', ['アップデート', '更新', '업데이트', 'upgrade', 'تحديث', 'עדכון', 'بهروزرسانی', 'अपडेट', 'aktualisieren', 'aktualisierung', 'actualizar', 'aggiornare', 'atualizar']],
|
|
649
|
-
['log', ['ログ', '日志', '로그', 'logging', 'سجل', 'יומן', 'ثبت', 'लॉग', 'protokoll', 'protokolle', 'journal', 'registro']],
|
|
888
|
+
['memory', ['メモリ', '内存', '메모리', 'ram', 'ذاكرة', 'זיכרון', 'حافظه', 'मेमोरी', 'speicher', 'mémoire', 'memoria', 'memória', 'память', 'bellek', 'hafıza']],
|
|
889
|
+
['config', ['設定', '配置', '설정', 'configuration', 'settings', 'conf', 'إعدادات', 'הגדרות', 'پیکربندی', 'कॉन्फ़िग', 'konfiguration', 'einstellungen', 'configuración', 'configurazione', 'configuração', 'конфигурация', 'настройки', 'yapılandırma', 'ayarlar']],
|
|
890
|
+
['install', ['インストール', '安装', '설치', 'تثبيت', 'התקנה', 'نصب', 'इंस्टॉल', 'installieren', 'installer', 'instalar', 'installare', 'установить', 'установка', 'yüklemek', 'kurulum']],
|
|
891
|
+
['update', ['アップデート', '更新', '업데이트', 'upgrade', 'تحديث', 'עדכון', 'بهروزرسانی', 'अपडेट', 'aktualisieren', 'aktualisierung', 'actualizar', 'aggiornare', 'atualizar', 'обновление', 'обновить', 'güncelleme']],
|
|
892
|
+
['log', ['ログ', '日志', '로그', 'logging', 'سجل', 'יומן', 'ثبت', 'लॉग', 'protokoll', 'protokolle', 'journal', 'registro', 'журнал', 'лог', 'günlük']],
|
|
650
893
|
['port', ['ポート', '端口', '포트', 'منفذ', 'פורט']],
|
|
651
894
|
['file', ['ファイル', '文件', '파일', 'ملف', 'קובץ']],
|
|
652
895
|
['image', ['イメージ', '镜像', '이미지', 'صورة', 'תמונה', 'abbild', 'imagen', 'imagem']],
|
|
653
896
|
['volume', ['ボリューム', '卷', '볼륨']],
|
|
654
897
|
['cluster', ['クラスター', '集群', '클러스터']],
|
|
655
898
|
['node', ['ノード', '节点', '노드']],
|
|
656
|
-
['service', ['サービス', '服务', '서비스', 'svc', 'خدمة', 'שירות', 'dienst', 'servicio', 'servizio', 'serviço']],
|
|
899
|
+
['service', ['サービス', '服务', '서비스', 'svc', 'خدمة', 'שירות', 'dienst', 'servicio', 'servizio', 'serviço', 'сервис', 'служба', 'servis', 'hizmet']],
|
|
657
900
|
['certificate', ['証明書', '证书', '인증서', 'cert', 'ssl', 'tls', 'شهادة', 'תעודה']],
|
|
658
901
|
['password', ['パスワード', '密码', '비밀번호', 'passwd', 'pwd', 'كلمة المرور', 'סיסמה']],
|
|
659
902
|
['token', ['トークン', '令牌', '토큰', 'jwt', 'secret', 'رمز', 'אסימון']],
|
|
660
903
|
['health', ['ヘルス', '健康', '헬스', 'healthcheck', 'probe', 'صحة']],
|
|
661
904
|
['migration', ['マイグレーション', '迁移', '마이그레이션', 'migrate', 'هجرة']],
|
|
662
|
-
['backup', ['バックアップ', '备份', '백업', 'نسخ احتياطي', 'גיבוי', 'پشتیبانگیری', 'बैकअप', 'sicherung', 'sauvegarde', 'respaldo', 'cópia de segurança']],
|
|
663
|
-
['monitor', ['モニター', '监控', '모니터링', 'monitoring', 'metrics', 'alert', 'مراقبة', 'ניטור', 'نظارت', 'निगरानी', 'überwachung', 'surveiller', 'monitoreo', 'monitorare', 'monitorar']],
|
|
664
|
-
['performance', ['パフォーマンス', '性能', '성능', 'latency', 'throughput', 'عملکرد', 'प्रदर्शन', 'leistung', 'performances', 'rendimiento', 'prestazioni', 'desempenho']],
|
|
905
|
+
['backup', ['バックアップ', '备份', '백업', 'نسخ احتياطي', 'גיבוי', 'پشتیبانگیری', 'बैकअप', 'sicherung', 'sauvegarde', 'respaldo', 'cópia de segurança', 'резервная копия', 'резерв', 'yedekleme']],
|
|
906
|
+
['monitor', ['モニター', '监控', '모니터링', 'monitoring', 'metrics', 'alert', 'مراقبة', 'ניטור', 'نظارت', 'निगरानी', 'überwachung', 'surveiller', 'monitoreo', 'monitorare', 'monitorar', 'мониторинг', 'наблюдение', 'izleme']],
|
|
907
|
+
['performance', ['パフォーマンス', '性能', '성능', 'latency', 'throughput', 'عملکرد', 'प्रदर्शन', 'leistung', 'performances', 'rendimiento', 'prestazioni', 'desempenho', 'производительность', 'быстродействие', 'performans']],
|
|
665
908
|
['connection', ['接続', '连接', '연결', 'conn', 'socket', 'اتصال', 'חיבור', 'اتصال', 'कनेक्शन', 'verbindung', 'connexion', 'conexión', 'connessione', 'conexão']],
|
|
666
909
|
['queue', ['キュー', '队列', '큐', 'طابور', 'warteschlange', 'file d\'attente', 'cola', 'coda', 'fila']],
|
|
667
910
|
['redis', ['レディス', '레디스']],
|
|
@@ -676,10 +919,10 @@ const CROSS_LINGUAL_MAP = new Map<string, string[]>([
|
|
|
676
919
|
['git', ['version control', 'repo', 'repository', 'commit', 'push', 'pull']],
|
|
677
920
|
['ci', ['pipeline', 'github actions', 'gitlab', 'jenkins', 'build']],
|
|
678
921
|
['cd', ['deploy', 'deployment', 'release']],
|
|
679
|
-
['debug', ['デバッグ', '调试', '디버그', 'debugging', 'breakpoint', 'تصحيح', 'איתור באגים', 'اشکالزدایی', 'डीबग', 'debuggen', 'déboguer', 'depurar']],
|
|
680
|
-
['crash', ['クラッシュ', '崩溃', '크래시', 'panic', 'segfault', 'انهيار', 'קריסה', 'خرابی', 'क्रैश', 'absturz', 'panne', 'plantage', 'caída', 'falha']],
|
|
922
|
+
['debug', ['デバッグ', '调试', '디버그', 'debugging', 'breakpoint', 'تصحيح', 'איתור באגים', 'اشکالزدایی', 'डीबग', 'debuggen', 'déboguer', 'depurar', 'отладка', 'дебаг', 'hata ayıklama']],
|
|
923
|
+
['crash', ['クラッシュ', '崩溃', '크래시', 'panic', 'segfault', 'انهيار', 'קריסה', 'خرابی', 'क्रैश', 'absturz', 'panne', 'plantage', 'caída', 'falha', 'сбой', 'крэш', 'çökme', 'çöküş']],
|
|
681
924
|
['restart', ['再起動', '重启', '재시작', 'reboot', 'إعادة تشغيل', 'הפעלה מחדש', 'راهاندازی مجدد', 'पुنरारंभ', 'neustart', 'redémarrer', 'reiniciar', 'riavviare']],
|
|
682
|
-
['permission', ['権限', '权限', '권한', 'access', 'acl', 'chmod', 'صلاحية', 'הרשאה', 'مجوز', 'अनुमति', 'berechtigung', 'berechtigungen', 'permiso', 'permesso', 'permissão']],
|
|
925
|
+
['permission', ['権限', '权限', '권한', 'access', 'acl', 'chmod', 'صلاحية', 'הרשאה', 'مجوز', 'अनुमति', 'berechtigung', 'berechtigungen', 'permiso', 'permesso', 'permissão', 'разрешение', 'доступ', 'izin', 'yetki']],
|
|
683
926
|
// Japanese katakana → English
|
|
684
927
|
['デプロイ', ['deploy', 'deployment']],
|
|
685
928
|
['コンテナ', ['container', 'docker']],
|
|
@@ -1114,6 +1357,105 @@ const CROSS_LINGUAL_MAP = new Map<string, string[]>([
|
|
|
1114
1357
|
['ambiente', ['environment', 'env']],
|
|
1115
1358
|
['fila', ['queue']],
|
|
1116
1359
|
['imagem', ['image']],
|
|
1360
|
+
|
|
1361
|
+
// ── Russian (Cyrillic) → English ──────────────────────────────────────────
|
|
1362
|
+
['ошибка', ['error', 'err']],
|
|
1363
|
+
['ошибки', ['error', 'err']],
|
|
1364
|
+
['сбой', ['crash', 'failure', 'error']],
|
|
1365
|
+
['крэш', ['crash', 'panic']],
|
|
1366
|
+
['баг', ['bug', 'issue']],
|
|
1367
|
+
['исправить', ['fix', 'bugfix']],
|
|
1368
|
+
['исправление', ['fix', 'patch']],
|
|
1369
|
+
['сборка', ['build']],
|
|
1370
|
+
['собрать', ['build']],
|
|
1371
|
+
['тест', ['test']],
|
|
1372
|
+
['тестирование', ['test', 'testing']],
|
|
1373
|
+
['аутентификация', ['auth', 'authentication']],
|
|
1374
|
+
['авторизация', ['auth', 'authorization']],
|
|
1375
|
+
['конфигурация', ['config', 'configuration']],
|
|
1376
|
+
['настройки', ['config', 'settings']],
|
|
1377
|
+
['установить', ['install']],
|
|
1378
|
+
['установка', ['install']],
|
|
1379
|
+
['обновление', ['update', 'upgrade']],
|
|
1380
|
+
['обновить', ['update', 'upgrade']],
|
|
1381
|
+
['журнал', ['log']],
|
|
1382
|
+
['лог', ['log', 'logs']],
|
|
1383
|
+
['сервер', ['server']],
|
|
1384
|
+
['база данных', ['database', 'db']],
|
|
1385
|
+
['сеть', ['network']],
|
|
1386
|
+
['память', ['memory', 'ram']],
|
|
1387
|
+
['кэш', ['cache']],
|
|
1388
|
+
['отладка', ['debug', 'debugging']],
|
|
1389
|
+
['перезапуск', ['restart', 'reboot']],
|
|
1390
|
+
['перезагрузка', ['restart', 'reboot']],
|
|
1391
|
+
['разрешение', ['permission', 'access']],
|
|
1392
|
+
['доступ', ['permission', 'access']],
|
|
1393
|
+
['резерв', ['backup']],
|
|
1394
|
+
['производительность', ['performance']],
|
|
1395
|
+
['проблема', ['problem', 'issue', 'error']],
|
|
1396
|
+
['сервис', ['service']],
|
|
1397
|
+
['служба', ['service']],
|
|
1398
|
+
['подключение', ['connection', 'conn']],
|
|
1399
|
+
['соединение', ['connection', 'conn']],
|
|
1400
|
+
['развёртывание', ['deploy', 'deployment']],
|
|
1401
|
+
['развертывание', ['deploy', 'deployment']],
|
|
1402
|
+
['контейнер', ['container', 'docker']],
|
|
1403
|
+
['образ', ['image']],
|
|
1404
|
+
['кластер', ['cluster']],
|
|
1405
|
+
['узел', ['node']],
|
|
1406
|
+
['секрет', ['secret', 'key', 'token']],
|
|
1407
|
+
['ключ', ['key', 'token', 'secret']],
|
|
1408
|
+
['пароль', ['password']],
|
|
1409
|
+
['токен', ['token', 'secret']],
|
|
1410
|
+
['мониторинг', ['monitor', 'monitoring']],
|
|
1411
|
+
['наблюдение', ['monitor', 'monitoring']],
|
|
1412
|
+
['мигрировать', ['migrate', 'migration']],
|
|
1413
|
+
['миграция', ['migrate', 'migration']],
|
|
1414
|
+
|
|
1415
|
+
// ── Turkish (Latin + special chars ğ/ş/ı) → English ─────────────────────
|
|
1416
|
+
['hata', ['error', 'err']],
|
|
1417
|
+
['hatalı', ['error', 'err']],
|
|
1418
|
+
['çökme', ['crash', 'panic']],
|
|
1419
|
+
['çöküş', ['crash', 'failure']],
|
|
1420
|
+
['düzeltmek', ['fix', 'bugfix']],
|
|
1421
|
+
['düzeltme', ['fix', 'patch']],
|
|
1422
|
+
['derleme', ['build']],
|
|
1423
|
+
['derlemek', ['build']],
|
|
1424
|
+
['test', ['test']],
|
|
1425
|
+
['testlemek', ['test', 'testing']],
|
|
1426
|
+
['kimlik doğrulama',['auth', 'authentication']],
|
|
1427
|
+
['yetkilendirme', ['auth', 'authorization']],
|
|
1428
|
+
['yapılandırma', ['config', 'configuration']],
|
|
1429
|
+
['ayarlar', ['config', 'settings']],
|
|
1430
|
+
['yüklemek', ['install']],
|
|
1431
|
+
['kurulum', ['install']],
|
|
1432
|
+
['güncelleme', ['update', 'upgrade']],
|
|
1433
|
+
['günlük', ['log']],
|
|
1434
|
+
['sunucu', ['server']],
|
|
1435
|
+
['veritabanı', ['database', 'db']],
|
|
1436
|
+
['ağ', ['network']],
|
|
1437
|
+
['bellek', ['memory', 'ram']],
|
|
1438
|
+
['önbellek', ['cache']],
|
|
1439
|
+
['hata ayıklama', ['debug', 'debugging']],
|
|
1440
|
+
['yeniden başlatma',['restart', 'reboot']],
|
|
1441
|
+
['izin', ['permission', 'access']],
|
|
1442
|
+
['yetki', ['permission', 'access', 'acl']],
|
|
1443
|
+
['yedekleme', ['backup']],
|
|
1444
|
+
['performans', ['performance']],
|
|
1445
|
+
['sorun', ['problem', 'issue', 'error']],
|
|
1446
|
+
['servis', ['service']],
|
|
1447
|
+
['hizmet', ['service']],
|
|
1448
|
+
['bağlantı', ['connection', 'conn']],
|
|
1449
|
+
['dağıtım', ['deploy', 'deployment']],
|
|
1450
|
+
['dağıtmak', ['deploy']],
|
|
1451
|
+
['konteyner', ['container', 'docker']],
|
|
1452
|
+
['küme', ['cluster']],
|
|
1453
|
+
['düğüm', ['node']],
|
|
1454
|
+
['gizli', ['secret', 'key']],
|
|
1455
|
+
['anahtar', ['key', 'token', 'secret']],
|
|
1456
|
+
['şifre', ['password']],
|
|
1457
|
+
['izleme', ['monitor', 'monitoring']],
|
|
1458
|
+
['göç', ['migrate', 'migration']],
|
|
1117
1459
|
]);
|
|
1118
1460
|
|
|
1119
1461
|
/**
|
|
@@ -1140,7 +1482,8 @@ function expandCrossLingual(token: string): string[] {
|
|
|
1140
1482
|
*/
|
|
1141
1483
|
function tokenize(text: string): string[] {
|
|
1142
1484
|
const tokens: string[] = [];
|
|
1143
|
-
|
|
1485
|
+
// Pre-process: split camelCase/PascalCase identifiers and snake_case before lowercasing
|
|
1486
|
+
const lower = preprocessText(text).toLowerCase();
|
|
1144
1487
|
|
|
1145
1488
|
for (const [seg] of lower.matchAll(SEGMENT_RE)) {
|
|
1146
1489
|
if (CJK_RE.test(seg)) {
|
|
@@ -1187,22 +1530,48 @@ function tokenize(text: string): string[] {
|
|
|
1187
1530
|
}
|
|
1188
1531
|
} else if (DEVANAGARI_RE.test(seg)) {
|
|
1189
1532
|
// Devanagari segment (Hindi / Marathi) — naturally space-separated
|
|
1190
|
-
// Words don't need bigram expansion; stems reduce inflected forms.
|
|
1191
1533
|
const words = seg.trim().split(/[\s\u200c\u200d]+/); // handle zero-width joiners
|
|
1192
1534
|
for (const w of words) {
|
|
1193
|
-
// Keep Devanagari chars + digits; discard pure Latin portions (e.g. embedded numbers)
|
|
1194
1535
|
const deva = w.replace(/[^\u0900-\u097f0-9]/g, '');
|
|
1195
1536
|
if (deva.length <= 1 || STOPWORDS.has(deva)) continue;
|
|
1196
1537
|
const stemmed = hindiLightStem(deva);
|
|
1197
1538
|
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) tokens.push(stemmed);
|
|
1198
1539
|
}
|
|
1540
|
+
} else if (BENGALI_RE.test(seg)) {
|
|
1541
|
+
// Bengali (Bangla) segment — space-separated words, Bengali script
|
|
1542
|
+
const words = seg.trim().split(/[\s\u200c\u200d]+/);
|
|
1543
|
+
for (const w of words) {
|
|
1544
|
+
const bn = w.replace(/[^\u0980-\u09ff0-9]/g, '');
|
|
1545
|
+
if (bn.length <= 1 || STOPWORDS.has(bn)) continue;
|
|
1546
|
+
const stemmed = bengaliLightStem(bn);
|
|
1547
|
+
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) tokens.push(stemmed);
|
|
1548
|
+
}
|
|
1549
|
+
} else if (CYRILLIC_RE.test(seg)) {
|
|
1550
|
+
// Cyrillic segment (Russian, Bulgarian, Ukrainian, Serbian …)
|
|
1551
|
+
const words = seg.trim().split(/\s+/);
|
|
1552
|
+
for (const w of words) {
|
|
1553
|
+
const cyr = w.replace(/[^\u0400-\u04ff]/g, '');
|
|
1554
|
+
if (cyr.length <= 1 || STOPWORDS.has(cyr)) continue;
|
|
1555
|
+
const stemmed = russianLightStem(cyr);
|
|
1556
|
+
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) tokens.push(stemmed);
|
|
1557
|
+
}
|
|
1199
1558
|
} else {
|
|
1200
|
-
// Latin / other —
|
|
1559
|
+
// Latin / other — includes Turkish (ğ/ş/ı), Polish (ą/ę/ł/ń/ó/ś/ź/ż/ć),
|
|
1560
|
+
// Czech (č/š/ž/ě/ů/ř), Hungarian (ő/ű), Romanian (ș/ț)
|
|
1201
1561
|
const words = seg
|
|
1202
|
-
.replace(/[^a-
|
|
1562
|
+
.replace(/[^a-záàâãäåæçéèêëíìîïñóòôõöúùûüýÿßœğşıİąćęłńśźżčšžěůřőűșț0-9:\-./]/g, ' ')
|
|
1203
1563
|
.split(/\s+/)
|
|
1204
1564
|
.filter(w => w.length > 1 && !STOPWORDS.has(w));
|
|
1205
|
-
|
|
1565
|
+
// Turkish: apply light stemmer to words with Turkish-specific chars
|
|
1566
|
+
for (const w of words) {
|
|
1567
|
+
if (TURKISH_CHAR_RE.test(w)) {
|
|
1568
|
+
const stemmed = turkishLightStem(w);
|
|
1569
|
+
if (stemmed !== w && stemmed.length > 1 && !STOPWORDS.has(stemmed)) {
|
|
1570
|
+
tokens.push(stemmed);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
tokens.push(w);
|
|
1574
|
+
}
|
|
1206
1575
|
}
|
|
1207
1576
|
}
|
|
1208
1577
|
|
|
@@ -1237,10 +1606,31 @@ function tokenize(text: string): string[] {
|
|
|
1237
1606
|
// Devanagari synonym (Hindi)
|
|
1238
1607
|
const stemmed = hindiLightStem(syn);
|
|
1239
1608
|
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) expansions.push(stemmed);
|
|
1609
|
+
} else if (BENGALI_RE.test(syn)) {
|
|
1610
|
+
// Bengali synonym
|
|
1611
|
+
const stemmed = bengaliLightStem(syn);
|
|
1612
|
+
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) expansions.push(stemmed);
|
|
1613
|
+
} else if (CYRILLIC_RE.test(syn)) {
|
|
1614
|
+
// Cyrillic synonym (Russian)
|
|
1615
|
+
const cyr = syn.replace(/[^\u0400-\u04ff]/g, '');
|
|
1616
|
+
const stemmed = russianLightStem(cyr);
|
|
1617
|
+
if (stemmed.length > 1 && !STOPWORDS.has(stemmed)) expansions.push(stemmed);
|
|
1240
1618
|
} else {
|
|
1241
|
-
// Latin synonym — split in case of multi-word
|
|
1242
|
-
const parts = syn.split(/\s+/).filter(w =>
|
|
1243
|
-
|
|
1619
|
+
// Latin synonym (includes Turkish) — split in case of multi-word
|
|
1620
|
+
const parts = syn.split(/\s+/).filter(w => {
|
|
1621
|
+
if (w.length <= 1 || STOPWORDS.has(w)) return false;
|
|
1622
|
+
return true;
|
|
1623
|
+
});
|
|
1624
|
+
for (const p of parts) {
|
|
1625
|
+
expansions.push(p);
|
|
1626
|
+
// Apply Turkish stemmer if it has Turkish-specific characters
|
|
1627
|
+
if (TURKISH_CHAR_RE.test(p)) {
|
|
1628
|
+
const stemmed = turkishLightStem(p);
|
|
1629
|
+
if (stemmed !== p && stemmed.length > 1 && !STOPWORDS.has(stemmed)) {
|
|
1630
|
+
expansions.push(stemmed);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1244
1634
|
}
|
|
1245
1635
|
}
|
|
1246
1636
|
}
|
|
@@ -1268,9 +1658,9 @@ function splitMultiQuery(query: string): string[] {
|
|
|
1268
1658
|
const semiParts = query.split(/[;\n]+/).filter(s => s.trim().length > 2);
|
|
1269
1659
|
if (semiParts.length >= 2) return semiParts.map(s => s.trim());
|
|
1270
1660
|
|
|
1271
|
-
// Conjunctions (EN + DE + FR + ES)
|
|
1661
|
+
// Conjunctions (EN + DE + FR + ES + IT + PT + RU + TR)
|
|
1272
1662
|
const conjParts = query.split(
|
|
1273
|
-
/\b(?:and also|also noch|außerdem|plus|additionally|furthermore|de plus|además|inoltre|além disso)\b/i
|
|
1663
|
+
/\b(?:and also|also noch|außerdem|plus|additionally|furthermore|de plus|además|inoltre|além disso|а также|и также|кроме того|плюс|ayrıca|bunun yanı sıra|همچنین|وأيضاً|وكذلك|وهمچنین|और भी|इसके अलावा)\b/i
|
|
1274
1664
|
).filter(s => s.trim().length > 2);
|
|
1275
1665
|
if (conjParts.length >= 2) return conjParts.map(s => s.trim());
|
|
1276
1666
|
|
|
@@ -1308,6 +1698,7 @@ interface DocEntry {
|
|
|
1308
1698
|
tokens: string[];
|
|
1309
1699
|
tokenFreq: Map<string, number>; // term → count in this doc
|
|
1310
1700
|
bigrams: Set<string>; // "term1|term2" adjacency pairs
|
|
1701
|
+
keyTokens: Set<string>; // tokens from key/title only (for title boost)
|
|
1311
1702
|
timestamp?: number; // epoch ms, extracted from content if present
|
|
1312
1703
|
}
|
|
1313
1704
|
|
|
@@ -1427,7 +1818,9 @@ async function keywordSearch(
|
|
|
1427
1818
|
}
|
|
1428
1819
|
|
|
1429
1820
|
const timestamp = extractTimestamp(content);
|
|
1430
|
-
|
|
1821
|
+
// Key tokens for title-boost: terms appearing in the Redis key get extra weight
|
|
1822
|
+
const keyTokens = new Set(tokenize(allKeys[i]));
|
|
1823
|
+
docs.push({ key: allKeys[i], content, tokens, tokenFreq, bigrams, keyTokens, timestamp });
|
|
1431
1824
|
totalTokens += tokens.length;
|
|
1432
1825
|
}
|
|
1433
1826
|
|
|
@@ -1535,6 +1928,24 @@ async function keywordSearch(
|
|
|
1535
1928
|
}
|
|
1536
1929
|
}
|
|
1537
1930
|
|
|
1931
|
+
// Phrase-match boost: if the raw (lowercased) query appears verbatim in content, 2× boost
|
|
1932
|
+
// This rewards docs where the exact phrase exists (vs scattered tokens).
|
|
1933
|
+
if (sq.length >= 4 && doc.content.toLowerCase().includes(sq.toLowerCase())) {
|
|
1934
|
+
score *= 2.0;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
// Key/title boost: if query terms appear in the Redis key (=title), 1.5× boost
|
|
1938
|
+
// Keys often encode the primary topic (e.g. "deploy:api:server"), so key hits are high-precision.
|
|
1939
|
+
if (score > 0) {
|
|
1940
|
+
let keyHits = 0;
|
|
1941
|
+
for (const qt of queryTokens) {
|
|
1942
|
+
if (doc.keyTokens.has(qt)) keyHits++;
|
|
1943
|
+
}
|
|
1944
|
+
if (keyHits > 0) {
|
|
1945
|
+
score *= 1 + 0.5 * (keyHits / queryTokens.length);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1538
1949
|
// Recency boost
|
|
1539
1950
|
score *= recencyBoost(doc.timestamp);
|
|
1540
1951
|
|