@apollo/federation-internals
Version:
Apollo Federation internal utilities
53 lines (44 loc) • 1.7 kB
text/typescript
import levenshtein from 'js-levenshtein';
import { mapKeys } from './utils';
/**
* Given an invalid input string and a list of valid options, returns a filtered
* list of valid options sorted based on their similarity with the input.
*/
export function suggestionList(input: string, options: readonly string[]): string[] {
const optionsByDistance = new Map<string, number>();
const threshold = Math.floor(input.length * 0.4) + 1;
const inputLowerCase = input.toLowerCase();
for (const option of options) {
// Special casing so that if the only mismatch is in uppper/lower-case, then the
// option is always shown.
const distance = inputLowerCase === option.toLowerCase()
? 1
: levenshtein(input, option);
if (distance <= threshold) {
optionsByDistance.set(option, distance);
}
}
return mapKeys(optionsByDistance).sort((a, b) => {
const distanceDiff = optionsByDistance.get(a)! - optionsByDistance.get(b)!;
return distanceDiff !== 0 ? distanceDiff : a.localeCompare(b);
});
}
const MAX_SUGGESTIONS = 5;
/**
* Given [ A, B, C ] return ' Did you mean A, B, or C?'.
*/
export function didYouMean(suggestions: readonly string[]): string {
const message = ' Did you mean ';
const quotedSuggestions = suggestions.map((x) => `"${x}"`);
switch (suggestions.length) {
case 0:
return '';
case 1:
return message + quotedSuggestions[0] + '?';
case 2:
return message + quotedSuggestions[0] + ' or ' + quotedSuggestions[1] + '?';
}
const selected = quotedSuggestions.slice(0, MAX_SUGGESTIONS);
const lastItem = selected.pop();
return message + selected.join(', ') + ', or ' + lastItem + '?';
}