function fuzzyScoreItem(targetStr, searchStr) {
      if (!targetStr) {
        return null;
      }

      // empty search string is technically a match of nothing
      if (!searchStr) {
        return {
          score: 0,
          ranges: [],
        };
      }

      // if user enters a quoted search then only perform substring match
      // e.g "la matches [{La}s Vegas] but not [Los Angeles]
      // NOTE: ending quote is optional so user can get incremental matching as they type.
      const isQuotedSearchStr = searchStr[0] === '"';
      if (isQuotedSearchStr) {
        searchStr = searchStr.slice(1, searchStr.endsWith(`"`) ? -1 : searchStr.length);
      }

      // try substring search first
      // js engine uses boyer moore algo which is very fast O(m/n)
      const lCaseTargetStr = targetStr.toLowerCase();
      const matchIdx = lCaseTargetStr.indexOf(searchStr);
      const searchLen = searchStr.length;

      if (matchIdx >= 0) {
        const isWordPrefix = matchIdx > 0 && !_isCodeAlphaNum(targetStr.charCodeAt(matchIdx - 1));
        return {
          score: _getMatchScore(matchIdx, searchLen, isWordPrefix),
          ranges: [matchIdx, searchLen],
        };
      }

      // if we didn't match a single character as a substr, we won't fuzzy match it either, exit early.
      // if quoted search, exit after substring search as well, since user doesn't want fuzzy search.
      if (searchLen === 1 || isQuotedSearchStr) {
        return null;
      }

      // fall back to fuzzy matching which matches word prefixes or punctuations
      // because we've precomputed targetSkips, its O(m+n) for avg case
      // the skip array helps us make faster alignments, rather than letter by letter
      const targetSkips = _getTargetSkips(targetStr);

      for (let skipIdx = 0, skipLen = targetSkips.length - 1; skipIdx < skipLen; ++skipIdx) {
        if (lCaseTargetStr[targetSkips[skipIdx]] === searchStr[0]) {
          // possible alignment, perform prefix match
          const ranges = _fuzzyPrefixMatch(skipIdx, searchStr, lCaseTargetStr, targetSkips);
          if (ranges) {
            let score = 0;
            for (let i = 0, len = ranges.length; i < len; i += 2) {
              score += _getMatchScore(ranges[i], ranges[i + 1], true /*isWordPrefix*/);
            }
            return {score, ranges};
          }
        }
      }

      return null;
    }
fuzzyScoreItem is called by fuzzyMatch, it's a slightly lower level call
If perf is of importance and you want to avoid lowercase + trim + highlighting on every item
Use this and only call highlightsFromRanges for only the items that are displayed
@param {string} targetStr - lowercased trimmed target string to search on
@param {string} searchStr - lowercased trimmed search string
@returns {{score: number, ranges: number[]} | null} - null if no match