npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aiquants/fuzzy-search

v1.5.1

Published

Advanced fuzzy search library with Levenshtein distance, n-gram indexing, and Web Worker support

Readme

@aiquants/fuzzy-search

Advanced fuzzy search library with Levenshtein distance, n-gram indexing, and Web Worker support.

Features

  • Advanced Fuzzy Search: Levenshtein distance-based fuzzy matching
  • Performance Optimized: Web Worker support for non-blocking search
  • Multiple Index Types: N-gram, word, and phonetic indexing
  • Learning Capabilities: Adapts based on user search patterns
  • React Integration: Ready-to-use hooks and components
  • Voice Search: Web Speech API integration
  • TypeScript First: Full type safety and IntelliSense support
  • Highly Configurable: Extensive options for customization

Installation

# Using pnpm (recommended)
pnpm add @aiquants/fuzzy-search

# Using npm
npm install @aiquants/fuzzy-search

# Using yarn
yarn add @aiquants/fuzzy-search

Note: This package is optimized for pnpm. The package manager is specified as [email protected] for better dependency management.

Quick Start

Basic Usage

import { quickSearch } from '@aiquants/fuzzy-search'

const items = [
  { name: 'Apple', category: 'Fruit' },
  { name: 'Banana', category: 'Fruit' },
  { name: 'Carrot', category: 'Vegetable' },
]

// Simple search
const results = await quickSearch('aple', items, ['name'], {
  threshold: 0.4,
  maxResults: 10
})

console.log(results) // [{ name: 'Apple', category: 'Fruit' }]

Advanced Usage

import { FuzzySearchManager } from '@aiquants/fuzzy-search'

const searchManager = new FuzzySearchManager({
  threshold: 0.4,
  maxResults: 50,
  enableNgramIndex: true,
  enablePhonetic: true,
  learningWeight: 1.2,
  debounceMs: 300,
  enableHighlighting: true,
})

// Perform search
const results = await searchManager.search('query', items, ['name', 'description'])

// Handle results
results.forEach(result => {
  console.log(`Item: ${result.item.name}, Score: ${result.score}`)
  if (result.highlighted) {
    console.log(`Highlighted: ${result.highlighted.name}`)
  }
})

// Clean up
searchManager.dispose()

Logging and Worker Configuration

You can configure logging levels and custom logger implementations. You can also specify a workerId to manage multiple independent worker instances.

Worker Isolation vs Sharing

The workerId option controls how Web Workers are managed:

  • Isolation (Different IDs): Using different workerIds creates separate worker instances. This is useful when you have multiple search components that need to process different datasets independently and in parallel.
  • Sharing (Same ID): Using the same workerId allows multiple FuzzySearchManager instances to share the same underlying Web Workers. This is efficient for memory usage and allows reusing the built index if the data is the same.
import { FuzzySearchManager, LogLevel } from '@aiquants/fuzzy-search'

// Instance 1: Uses "shared-worker"
const manager1 = new FuzzySearchManager({
  workerId: "shared-worker",
  // ...
})

// Instance 2: Also uses "shared-worker"
// This instance will share the same Web Workers as manager1.
// If the data is the same, the index will not be rebuilt.
const manager2 = new FuzzySearchManager({
  workerId: "shared-worker",
  // ...
})

// Instance 3: Uses "isolated-worker"
// This creates a completely new set of Web Workers.
const manager3 = new FuzzySearchManager({
  workerId: "isolated-worker",
  // ...
})
import { FuzzySearchManager, LogLevel } from '@aiquants/fuzzy-search'

const manager = new FuzzySearchManager({
  // ... other options
  logLevel: LogLevel.DEBUG, // Set log level (DEBUG, INFO, WARN, ERROR, NONE)
  workerId: "my-search-instance", // Unique ID for worker isolation
  logger: console, // Optional: Custom logger implementation
})

React Integration

Hooks

import { useFuzzySearch } from '@aiquants/fuzzy-search/react'

function SearchComponent() {
  const {
    searchTerm,
    setSearchTerm,
    filteredItems,
    isLoading,
    highlightText,
    stats
  } = useFuzzySearch(items, ['name', 'email'], {
    threshold: 0.4,
    debounceMs: 300,
    enableHighlighting: true,
  })

  return (
    <div>
      <input
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search..."
      />
      {isLoading ? (
        <div>Searching...</div>
      ) : (
        <ul>
          {filteredItems.map((item, index) => (
            <li key={index}>
              <div dangerouslySetInnerHTML={{
                __html: highlightText(item.name)
              }} />
            </li>
          ))}
        </ul>
      )}
      <div>Found {stats.totalResults} results in {stats.executionTime}ms</div>
    </div>
  )
}

Important Note: Memoization Required

When using useFuzzySearch, you must memoize the keys array and options object using useMemo. If you pass new object references on every render, the hook will detect them as configuration changes and re-initialize the search worker, potentially causing an infinite loop.

import { useMemo } from 'react'

function SearchComponent() {
  // ✅ Correct: Memoize keys and options
  const keys = useMemo(() => ['name', 'email'], [])
  const options = useMemo(() => ({
    threshold: 0.4,
    debounceMs: 300,
    enableHighlighting: true,
  }), [])

  const {
    searchTerm,
    setSearchTerm,
    filteredItems,
    // ...
  } = useFuzzySearch(items, keys, options)

  // ...
}

Voice Search

import { useVoiceSearch } from '@aiquants/fuzzy-search/react'

function VoiceSearchComponent() {
  const {
    isListening,
    transcript,
    startListening,
    stopListening,
    isSupported
  } = useVoiceSearch({
    onResult: (text) => console.log('Speech result:', text),
    continuous: false,
    interimResults: true,
  })

  if (!isSupported) {
    return <div>Voice search not supported</div>
  }

  return (
    <div>
      <button onClick={isListening ? stopListening : startListening}>
        {isListening ? 'Stop' : 'Start'} Voice Search
      </button>
      <div>Transcript: {transcript}</div>
    </div>
  )
}

Configuration Options

interface FuzzySearchOptions {
  // Basic options
  threshold: number          // Similarity threshold (0-1)
  maxResults: number         // Maximum number of results
  caseSensitive: boolean     // Case-sensitive matching

  // Performance options
  enableNgramIndex: boolean  // Enable n-gram indexing
  enablePhonetic: boolean    // Enable phonetic matching
  ngramSize: number          // N-gram size
  minNgramOverlap: number    // Minimum n-gram overlap

  // Learning options
  learningWeight: number     // Weight for learning adjustments

  // UI options
  debounceMs: number         // Debounce delay in milliseconds
  enableHighlighting: boolean // Enable result highlighting

  // Custom weights
  customWeights: Record<string, number> // Field-specific weights
}

Web Worker Support

For large datasets, the search can be performed in a Web Worker to avoid blocking the main thread:

import { FuzzySearchManager } from '@aiquants/fuzzy-search'

const searchManager = new FuzzySearchManager({
  useWebWorker: true, // Enable Web Worker
  threshold: 0.4,
})

// Search will automatically use Web Worker for large datasets
const results = await searchManager.search('query', largeItems, ['name'])

Performance Tips

  1. Enable n-gram indexing for large datasets (>1000 items)
  2. Use Web Workers for datasets with >5000 items
  3. Adjust threshold based on your use case (lower = more results)
  4. Use debouncing to reduce search frequency
  5. Configure custom weights for field importance

API Reference

Core Classes

  • FuzzySearchManager: Main search management class
  • SearchIndexer: Advanced indexing system

React Hooks

  • useFuzzySearch: Main search hook
  • useVoiceSearch: Voice search hook
  • useSearchHistory: Search history management

Utility Functions

  • quickSearch: Simple search function
  • levenshteinDistance: Distance calculation
  • highlightText: Text highlighting

Browser Support

  • Chrome / Chromium 60+
  • Firefox 55+
  • Safari 11+
  • Edge 79+

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.