Sorex - TypeScript API

This is the complete reference for Sorex's browser API. The surface is intentionally small: one loader function, one searcher class with four methods. Everything else is handled internally.

The key design choice is callback-based progressive search. Instead of awaiting a single result, you receive updates after each search tier completes. This lets you show fast exact matches immediately while slower fuzzy matches continue in the background. The SearchResult type includes tier information so you can style results differently by match quality.

For how the loader works internally (streaming compilation, thread pool setup), see Runtime. For framework patterns and race condition handling, see Integration.


Installation

The WASM module is embedded in .sorex files built with sorex index. The generated sorex.js handles initialization:

import { loadSorex } from './sorex.js';

const searcher = await loadSorex('/search/index.sorex');

// Search with callbacks for progressive updates
searcher.search('query', 10, {
  onUpdate: (results) => renderResults(results),  // Called after each tier
  onFinish: (results) => renderResults(results)   // Called when complete
});

Public API

The loader exports only what you need:

export { loadSorex, type SorexSearcher, type SearchResult };

loadSorex

Loads a .sorex file and returns a ready-to-use searcher.

async function loadSorex(url: string): Promise<SorexSearcher>

Features:

  • Extracts embedded WASM from .sorex file
  • Compiles WASM during download (streaming compilation)
  • Initializes thread pool for parallel search (when available)
  • Falls back gracefully to single-threaded mode in Safari

Example:

const searcher = await loadSorex('/search/index.sorex');

SorexSearcher

The main search interface.

Three-tier progressive search: exact -> prefix -> fuzzy.

search(
  query: string,
  limit: number,
  callback?: {
    onUpdate?: (results: SearchResult[]) => void,
    onFinish?: (results: SearchResult[]) => void
  }
): void

searchSync

Synchronous search returning all results at once. Supports optional SearchOptions.

searchSync(query: string, limit: number, options?: SearchOptions): SearchResult[]

Parameters:

  • query - Search query string
  • limit - Maximum number of results
  • options - Optional search configuration (see SearchOptions below)

Example:

// Default: deduplicate sections (one result per document)
const results = searcher.searchSync('kernel', 10);

// Show all matching sections within documents
const allSections = searcher.searchSync('kernel', 10, { dedupSections: false });

search Parameters:

  • query - Search query string
  • limit - Maximum number of results
  • callback.onUpdate - Called after each tier completes with accumulated results
  • callback.onFinish - Called when all tiers complete with final sorted results

Example:

// Basic usage - just get final results
searcher.search('auto-tuning', 10, {
  onFinish: (results) => console.log(results)
});

// Progressive UI updates
searcher.search('kernel', 10, {
  onUpdate: (results) => {
    // Called after T1, T2, T3 with accumulated results
    renderResults(results);
  },
  onFinish: (results) => {
    // Final sorted results
    renderResults(results);
  }
});

docCount

Returns the number of indexed documents.

docCount(): number

vocabSize

Returns the number of unique terms in the vocabulary.

vocabSize(): number

free

Releases WASM memory. Call when done with the searcher (important in SPAs).

free(): void

SearchResult

interface SearchResult {
  href: string;              // URL path (e.g., "/posts/2024/01/my-post")
  title: string;             // Document title
  excerpt: string;           // Short description
  sectionId: string | null;  // Section ID for deep linking
  tier: 1 | 2 | 3;           // Match tier (1=exact, 2=prefix, 3=fuzzy)
  matchType: number;         // Match type (0=title, 1=section, 2+=content)
  score: number;             // Relevance score (higher is better)
  matchedTerm: string | null; // Vocabulary term that matched (for highlighting)
}

matchedTerm: The actual vocabulary term that matched the query. Useful for:

  • Highlighting the matched term in results
  • Showing what the fuzzy search matched against (e.g., query "ruts" → matchedTerm "rust")
  • Prefix expansion display (e.g., query "typ" → matchedTerm "typescript")

SearchOptions

interface SearchOptions {
  dedupSections?: boolean;   // Whether to deduplicate sections (default: true)
}

dedupSections (default: true):

  • When true: Returns one result per document. The best matching section (by match type, then score) is used for deep linking via sectionId.
  • When false: Returns multiple results per document if different sections match. Useful for showing all matching locations within a document.

Complete Example

import { loadSorex } from './sorex.js';

class SearchController {
  private searcher: SorexSearcher | null = null;
  private currentSearchId = 0;

  async init(indexUrl: string) {
    this.searcher = await loadSorex(indexUrl);
    console.log(`Loaded ${this.searcher.docCount()} documents`);
  }

  search(query: string) {
    if (!this.searcher || query.length < 2) {
      this.renderResults([]);
      return;
    }

    // Increment search ID to handle race conditions
    const searchId = ++this.currentSearchId;

    this.searcher.search(query, 10, {
      onUpdate: (results) => {
        if (searchId !== this.currentSearchId) return;
        this.renderResults(results);
      },
      onFinish: (results) => {
        if (searchId !== this.currentSearchId) return;
        this.renderResults(results);
      }
    });
  }

  private renderResults(results: SearchResult[]) {
    const html = results.map(r => {
      const url = r.sectionId ? `${r.href}#${r.sectionId}` : r.href;
      return `<a href="${url}"><h3>${r.title}</h3><p>${r.excerpt}</p></a>`;
    }).join('');
    document.getElementById('results')!.innerHTML = html;
  }

  destroy() {
    this.searcher?.free();
    this.searcher = null;
  }
}

API Summary

Method Description
loadSorex(url) Load .sorex file, returns Promise
search(query, limit, callback?) Progressive search with callbacks
searchSync(query, limit, options?) Synchronous search with optional dedup control
docCount() Number of indexed documents
vocabSize() Number of vocabulary terms
free() Release WASM memory

See Also