@vielzeug/scout
v2.4.0
Published
Fast fuzzy-search with a trigram index, per-field weighting, match highlighting, and an optional reactive layer
Readme
@vielzeug/scout
Fast fuzzy-search. Builds a trigram inverted index at construction — O(candidates) per query instead of O(corpus × field_length).
Features
- Trigram index — fast candidate lookup; overlap-coefficient scoring
- Multi-field weighted ranking — per-field weights, custom stringifiers
- Match highlighting — character-range offsets for UI rendering
- Reactive layer —
createSearch()wraps any index inripplesignals with debounce - Framework adapters —
toSearchMatcher()for sourcerer,toFilterPredicate()for filter pipelines - Corpus reconciliation —
setItems()reconciles reference-based additions, removals, reindexes, and order in one notification - Unsegmented-script helper —
segmentWords()pre-splits CJK/Thai text into words viaIntl.Segmenter - Event subscription —
search.tap()observesquery/isSearching/results/disposetransitions
Install
pnpm add @vielzeug/scoutQuick start
import { createIndex } from '@vielzeug/scout';
const users = [
{ email: '[email protected]', name: 'Ada Lovelace' },
{ email: '[email protected]', name: 'Grace Hopper' },
];
const index = createIndex(users, {
fields: [
{ field: 'name', weight: 2 },
{ field: 'email' },
],
});
console.log(index.search('ada')[0]?.item.name); // Ada LovelaceReactive search
import { createIndex, createSearch } from '@vielzeug/scout';
import { effect } from '@vielzeug/ripple';
const users = [{ name: 'Ada Lovelace' }, { name: 'Grace Hopper' }];
const index = createIndex(users, { fields: ['name'] });
const search = createSearch(index, { debounce: 150 });
effect(() => {
console.log(search.results.value);
});
search.query.value = 'alice';Sourcerer integration
import { createIndex, toSearchMatcher } from '@vielzeug/scout';
import { createLocalSource } from '@vielzeug/sourcerer';
const users = [{ email: '[email protected]', name: 'Ada Lovelace' }];
const index = createIndex(users, { fields: ['name', 'email'] });
const source = createLocalSource(users, { match: toSearchMatcher(index) });Highlighting
import { highlight } from '@vielzeug/scout';
const parts = highlight('Hello World', [[0, 5]]);
// [{ text: 'Hello', highlighted: true }, { text: ' World', highlighted: false }]