@molecule/app-client-search-minisearch
Provider bond · client-search · App (browser) · v1.0.2 · Apache-2.0
MiniSearch bond for @molecule/app-client-search — in-memory full-text search with prefix matching, typo tolerance and field boosts
npm install @molecule/app-client-search-minisearchnpm · Source on GitHub · Implements @molecule/app-client-search
How it works
@molecule/app-client-search-minisearch is a provider bond on the app (browser) side: it implements the client-search core interface (@molecule/app-client-search) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { createIndex, setProvider } from '@molecule/app-client-search'
import { provider } from '@molecule/app-client-search-minisearch'
setProvider(provider)
const index = createIndex(templates, {
idField: 'slug',
fields: ['name', 'description', 'tags', 'packages'],
filterFields: ['category', 'tags'],
boost: { name: 3, tags: 2 },
})
index.search('tag:realtime chat')Works with: @molecule/app-client-search
Reference
MiniSearch bond for @molecule/app-client-search.
In-memory full-text search over a list the app already holds: prefix
matching, typo tolerance, per-field boosts, and the core's shared query
grammar (category:auth -deprecated "sign in"). MiniSearch is a small
dependency-free library that indexes a few thousand records in
milliseconds and answers a keystroke well under a frame.
Quick Start
import { createIndex, setProvider } from '@molecule/app-client-search'
import { provider } from '@molecule/app-client-search-minisearch'
setProvider(provider)
const index = createIndex(templates, {
idField: 'slug',
fields: ['name', 'description', 'tags', 'packages'],
filterFields: ['category', 'tags'],
boost: { name: 3, tags: 2 },
})
index.search('tag:realtime chat')
Type
provider
Installation
npm install @molecule/app-client-search-minisearch @molecule/app-client-search minisearch
API
Interfaces
MinisearchConfig
Provider-specific configuration options.
interface MinisearchConfig {
/**
* How several free-text terms combine. `'AND'` (the default) requires every
* term, falling back to `'OR'` when that finds nothing, so `stripe billing`
* prefers documents mentioning both but still finds one that mentions either.
* `'OR'` ranks the union from the start.
*/
combineWith?: 'AND' | 'OR'
}
Functions
createProvider(config)
Creates a MiniSearch client search provider.
function createProvider(config?: MinisearchConfig): ClientSearchProvider
config— Provider configuration.
Returns: A client search provider.
Constants
provider
Default MiniSearch provider instance.
const provider: ClientSearchProvider
Core Interface
Implements @molecule/app-client-search interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/app-client-search'
import { provider } from '@molecule/app-client-search-minisearch'
export function setupClientSearchMinisearch(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/app-client-search^1.0.0
Runtime Dependencies
-
@molecule/app-client-search -
minisearch -
Free-text terms are ANDed first, then ORed as a fallback.
stripe billingprefers records mentioning both and still finds one mentioning either; passcreateProvider({ combineWith: 'OR' })to rank the union from the start. -
Hits carry the original document, not a copy of stored fields, so a hit's
docis the same object you indexed (===). Do not mutate it and expect the index to notice — callreplace(doc). -
Filters (
field:value), phrases and exclusions are evaluated by the core's shared helpers against the real document, so their meaning is identical across bonds; only ranking is MiniSearch's. -
The index is rebuilt from scratch by
createIndex: for a list that changes a few records at a time, keep the index andreplace/remove.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Typing in the search box narrows the list on every keystroke, and the best match is first (a name match beats a description match).
- A typo (
paymnts) still finds the right records. -
field:valuenarrows to that value, and combining it with text narrows further; an unknownword:thingsearches as plain text. - A quoted phrase only matches records containing it verbatim, and a
-termremoves records containing that term. - Clearing the box restores the full list, with no stale results.