@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-minisearch

npm · 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 billing prefers records mentioning both and still finds one mentioning either; pass createProvider({ combineWith: 'OR' }) to rank the union from the start.

  • Hits carry the original document, not a copy of stored fields, so a hit's doc is the same object you indexed (===). Do not mutate it and expect the index to notice — call replace(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 and replace/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:value narrows to that value, and combining it with text narrows further; an unknown word:thing searches as plain text.
  • A quoted phrase only matches records containing it verbatim, and a -term removes records containing that term.
  • Clearing the box restores the full list, with no stale results.