All files SynonymResolver.ts

95.29% Statements 81/85
92.85% Branches 26/28
100% Functions 12/12
95.29% Lines 81/85

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229                                1x   2x     2x     2x     2x     2x 2x 2x             2x 53x 2x 2x 53x 53x                                     2x 23x     23x 1x 1x 1x     23x 1x 1x 1x   21x       21x   21x   21x   23x 1x   1x 1x 1x     18x       18x 18x 18x   18x     18x   18x 23x 2x     2x 2x 2x 23x                   2x 35x 35x 35x             2x 6x 6x                                   2x 3x 3x     3x 1x 1x     2x 2x 3x                               2x 4x 4x 4x                               2x 1x     1x 1x   1x 1x   2x 5x     5x       5x 5x 2x 1x 1x   2x
/**
 * SynonymResolver provides synonym matching capabilities using the DataMuse API.
 * 
 * This is the basic version that includes:
 * - Singleton pattern
 * - Basic synonym fetching from DataMuse API
 * - Simple in-memory caching
 * - Error handling
 * 
 * @example
 * ```ts
 * const resolver = SynonymResolver.getInstance();
 * const synonyms = await resolver.getSynonyms('jump');
 * // Returns: ['leap', 'hop', 'spring', 'bound', ...]
 * ```
 */
export class SynonymResolver {
  /** Cache to store synonym results and avoid repeated API calls */
  private synonymCache: Map<string, string[]>;
 
  /** The single global instance of SynonymResolver */
  private static instance: SynonymResolver;
 
  /** Base URL for the DataMuse API */
  private readonly API_URL = 'https://api.datamuse.com/words';
 
  /** Maximum number of synonyms to fetch per word */
  private MAX_RESULTS = 3;
 
  /** Private constructor prevents direct instantiation - use getInstance() */
  private constructor() {
    this.synonymCache = new Map<string, string[]>();
  }
 
  /**
   * Returns the singleton instance of SynonymResolver.
   * 
   * @returns {SynonymResolver} The single shared instance
   */
  public static getInstance(): SynonymResolver {
    if (!SynonymResolver.instance) {
      SynonymResolver.instance = new SynonymResolver();
    }
    return SynonymResolver.instance;
  }
 
  /**
   * Fetches synonyms for a given word from the DataMuse API.
   * Results are cached in memory to avoid repeated API calls for the same word.
   * 
   * The DataMuse API parameter 'ml' means "means like" which returns synonyms.
   * API is free and doesn't require authentication.
   * 
   * @param {string} word - The word to find synonyms for
   * @returns {Promise<string[]>} Array of synonym words (lowercase), empty array on error
   * 
   * @example
   * ```ts
   * const resolver = SynonymResolver.getInstance();
   * const synonyms = await resolver.getSynonyms('jump');
   * console.log(synonyms); // ['leap', 'hop', 'spring', 'bound', ...]
   * ```
   */
  public async getSynonyms(word: string): Promise<string[]> {
    const normalized = word.toLowerCase().trim();
 
    // Return empty array for invalid input
    if (!normalized) {
      console.warn('Cannot fetch synonyms for empty word');
      return [];
    }
 
    // Check cache first to avoid unnecessary API calls
    if (this.synonymCache.has(normalized)) {
      console.log(`Cache hit for "${normalized}"`);
      return this.synonymCache.get(normalized)!;
    }
 
    try {
      // Build API URL with query parameters
      // ml= means "means like" (synonyms)
      // max= limits the number of results
      const url = `${this.API_URL}?ml=${encodeURIComponent(normalized)}&max=${this.MAX_RESULTS}`;
      
      console.log(`Fetching synonyms for "${normalized}" from DataMuse API...`);
      
      const response = await fetch(url);
 
      if (!response.ok) {
        console.warn(`DataMuse API returned status ${response.status} for "${word}"`);
        // Cache empty array to avoid repeated failed requests
        this.synonymCache.set(normalized, []);
        return [];
      }
 
      // Parse JSON response
      const data = await response.json();
      
      // DataMuse returns array of objects like: [{word: "leap", score: 1234}, ...]
      // Extract just the words and normalize to lowercase
      const synonyms = data
        .map((item: { word: string }) => item.word.toLowerCase().trim())
        .filter((synonym: string) => synonym !== normalized); // Exclude the original word
 
      console.log(`Found ${synonyms.length} synonyms for "${normalized}"`);
 
      // Cache the results for future use
      this.synonymCache.set(normalized, synonyms);
 
      return synonyms;
    } catch (error) {
      console.error(`Error fetching synonyms for "${word}":`, error);
      
      // Cache empty array to prevent repeated failures
      this.synonymCache.set(normalized, []);
      return [];
    }
  }
 
  
 
  /**
   * Clears the entire synonym cache.
   * Useful for testing or if you want to force fresh API calls.
   * 
   * @returns {void}
   */
  public clearCache(): void {
    this.synonymCache.clear();
    console.log('Synonym cache cleared');
  }
 
  /**
   * Returns the number of words currently cached.
   * 
   * @returns {number} Number of words in the cache
   */
  public getCacheSize(): number {
    return this.synonymCache.size;
  }
 
  /**
   * Checks if two words are synonyms of each other.
   * 
   * This method checks if word2 appears in word1's synonym list.
   * 
   * @param {string} word1 - First word to compare
   * @param {string} word2 - Second word to compare
   * @returns {Promise<boolean>} True if the words are synonyms, false otherwise
   * 
   * @example
   * ```ts
   * const resolver = SynonymResolver.getInstance();
   * const areSynonyms = await resolver.areSynonyms('jump', 'leap');
   * console.log(areSynonyms); // true
   * ```
   */
  public async areSynonyms(word1: string, word2: string): Promise<boolean> {
    const normalized1 = word1.toLowerCase().trim();
    const normalized2 = word2.toLowerCase().trim();
 
    // Same word is obviously a match
    if (normalized1 === normalized2) {
      return true;
    }
 
    // Check if word2 is in word1's synonyms
    const synonyms1 = await this.getSynonyms(normalized1);
    return synonyms1.includes(normalized2);
  }
 
  /**
   * Checks if synonyms for a specific word are already cached.
   * 
   * @param {string} word - The word to check
   * @returns {boolean} True if synonyms are cached, false otherwise
   * 
   * @example
   * ```ts
   * const resolver = SynonymResolver.getInstance();
   * if (resolver.isCached('jump')) {
   *   console.log('Synonyms for "jump" are already cached');
   * }
   * ```
   */
  public isCached(word: string): boolean {
    const normalized = word.toLowerCase().trim();
    return this.synonymCache.has(normalized);
  }
 
  /**
   * Pre-fetches and caches synonyms for multiple words at once.
   * Useful for initializing the cache with commonly used words.
   * 
   * @param {string[]} words - Array of words to pre-fetch synonyms for
   * @returns {Promise<void>} Resolves when all synonyms are fetched
   * 
   * @example
   * ```ts
   * const resolver = SynonymResolver.getInstance();
   * await resolver.prefetchSynonyms(['jump', 'run', 'walk']);
   * // All synonyms are now cached and ready for instant lookup
   * ```
   */
  public async prefetchSynonyms(words: string[]): Promise<void> {
    console.log(`Pre-fetching synonyms for ${words.length} words...`);
    
    // Fetch all synonyms in parallel for better performance
    const promises = words.map(word => this.getSynonyms(word));
    await Promise.all(promises);
    
    console.log(`Pre-fetch complete. Cache now contains ${this.getCacheSize()} words`);
  }
 
public setMAX_RESULTS(numberOfSynonyms: number):void{
  if(!numberOfSynonyms){
    numberOfSynonyms = 3;
  }
  if(numberOfSynonyms <1){
    numberOfSynonyms = 1;
  }
 
  this.MAX_RESULTS = numberOfSynonyms;
}
public getMax_Results():number{
  return this.MAX_RESULTS;
}
 
}