Skip to content

WASM API

ABCrimson edited this page Mar 11, 2026 · 2 revisions

WASM Search API Reference

Complete API for modern-cmdk-search-wasm — the optional Rust/WebAssembly trigram search engine.


Installation

pnpm add modern-cmdk-search-wasm

The WASM binary is bundled and loaded automatically. No additional build configuration needed.


createWasmSearchEngine()

Creates a WASM-powered search engine that runs on the main thread. Drop-in replacement for the default TypeScript engine.

import { createWasmSearchEngine } from 'modern-cmdk-search-wasm';
import { createCommandMachine } from 'modern-cmdk';

const wasmEngine = await createWasmSearchEngine();

using machine = createCommandMachine({
  search: wasmEngine,
});

Performance

  • Sub-1ms fuzzy search on 100K items
  • Trigram index for high-quality fuzzy matching
  • ~150KB WASM binary (gzip: ~60KB)

createWorkerWasmSearchEngine()

Creates a WASM search engine that runs in a Web Worker, keeping the main thread free for rendering.

import { createWorkerWasmSearchEngine } from 'modern-cmdk-search-wasm';

const workerEngine = await createWorkerWasmSearchEngine();

using machine = createCommandMachine({
  search: workerEngine,
});

SharedArrayBuffer Mode

When cross-origin isolation headers are set, results are transferred via SharedArrayBuffer for zero-copy performance:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

Without these headers, results are transferred via postMessage structured clone (still fast for typical result sizes).


SearchEngine Interface

Both WASM engines implement the standard SearchEngine interface:

interface SearchEngine {
  index(items: readonly CommandItem[]): void;
  search(query: string, items: readonly CommandItem[]): IteratorObject<SearchResult>;
  remove(ids: ReadonlySet<ItemId>): void;
  clear(): void;
}

Worker Communication Protocol

Main Thread                    Web Worker              WASM Module
    |                              |                       |
    |-- new Worker() ------------>|                       |
    |                              |-- import wasm ------->|
    |                              |<-- module loaded -----|
    |<-- { type: ready } ---------|                       |
    |                              |                       |
    |-- { type: index, items } -->|                       |
    |                              |-- index_items() ---->|
    |                              |<-- index built -------|
    |<-- { type: indexed } -------|                       |
    |                              |                       |
    |-- { type: search,           |                       |
    |    query, scoresBuffer } -->|                       |
    |                              |-- search() --------->|
    |                              |<-- raw results -------|
    |                              |-- write to SAB        |
    |<-- { type: results-sab,     |                       |
    |    ids, matches } ----------|                       |
    |-- read Float32Array         |                       |
    |   (zero-copy)               |                       |
    |                              |                       |
    |-- { type: dispose } ------->|                       |
    |                              |-- instance.free() -->|
    |                              |-- self.close()        |

Trigram Index

The Rust crate builds a trigram index from item values and keywords:

  1. Each item's value and keywords are split into overlapping 3-character substrings
  2. A reverse index maps each trigram to a set of item IDs
  3. On search, the query is split into trigrams and matched against the index
  4. Results are scored by trigram overlap ratio with position bonuses

This provides:

  • O(1) lookup per trigram (hash map)
  • Sub-linear search — only items sharing trigrams with the query are scored
  • High recall — even with typos, shared trigrams surface relevant results

When to Use WASM Search

Scenario Recommendation
< 500 items Default TypeScript engine is sufficient
500 - 5K items WASM main thread for snappier feel
5K - 100K items WASM Worker for zero main-thread blocking
> 100K items WASM Worker + virtualization

Bundle Size Impact

Engine Size (raw) Size (gzip)
TypeScript (default) 0 KB (included in core) 0 KB
WASM main thread ~150 KB ~60 KB
WASM Worker ~155 KB ~62 KB

The WASM engine is fully tree-shakeable. If not imported, it adds zero bytes to your bundle.


Error Handling

try {
  const engine = await createWasmSearchEngine();
} catch (error) {
  // WASM not supported — fall back to TypeScript engine
  console.warn('WASM search unavailable, using default engine');
  const { createSearchEngine } = await import('modern-cmdk');
  const engine = createSearchEngine();
}

Both createWasmSearchEngine() and createWorkerWasmSearchEngine() return Promises that reject if WASM instantiation fails (e.g., Content Security Policy blocks WebAssembly.instantiate).

Clone this wiki locally