Searchable Feature gallery

Architecture

This page describes the shipped build-time and browser-runtime components, their data flow, and the decisions that keep the system static and framework-independent.

Build time

The Python searchable package (python/searchable, module searchable.indexer) discovers rendered HTML, extracts title/body/language/CMS controls, runs shared analysis, and builds postings, facets, pins, synonyms, fuzzy dictionaries, and stored documents. It writes an immutable manifest and content-hashed shards. It is a build-time tool, not a runtime dependency of the TypeScript package, which only reads its output.

Query time

@ktjn/searchable validates the manifest, resolves all shard files relative to it, analyzes the query, fetches only the required data, evaluates filters and ranking, and loads stored fields for the final hits.

A second, independent client implementation, searchable (Python, python/searchable/, module searchable.client), reads the exact same manifest/shard contract for CLI and backend-service use. Feature work on search behavior (ranking, filtering, synonyms, fuzzy matching, etc.) should consider both clients, not just the TypeScript one — see Python client API.

The shared cases in spec/fixtures/client-conformance/ run against one Python-built index through both clients. Their normalized observable results must agree.

Data flow for a query

rendered HTML -> indexer -> manifest + content-hashed shards -> static host
                                                               |
                                                               +-- query -> SearchClient (TypeScript) -> analysis -> lazy shard fetch -> ranking -> hits
                                                               |
                                                               +-- query -> SearchClient (Python) -> analysis -> lazy shard fetch -> lexical ranking -> hits

Deployment topology

The generated directory lives beside the static site or on an allowed CDN. The browser needs only the manifest URL.

The browser client uses TypeScript, Vite, Vitest, and Playwright and has no application-framework dependency. The client performs plain HTTP GETs and never requires a query-time write path or backend.

The public API stays small while optional costs are gated by use. Facets, synonyms, pins, and fuzzy logic are bundled but inert until requested. Dynamic third-party plugin registration is not implemented and is tracked only in the roadmap.

Accepted decisions are summarized in Architecture decisions.