Skip to content

Add an in-memory, single-threaded WebAssembly build and TypeScript package - #15

Merged
iamcarbon merged 1 commit into
mainfrom
claude/tanvity-wasm-in-memory-build-eba796
Sep 25, 2026
Merged

iamcarbon merged 1 commit into
mainfrom
claude/tanvity-wasm-in-memory-build-eba796

Conversation

@myluma

@myluma myluma commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

What

  • rust/src/single_threaded.rs is a thread-free replacement for tantivy's IndexWriter, behind a new single-threaded Cargo feature. It is built from tantivy's public pieces (SegmentWriter, write_alive_bitset, merge_filtered_segments, LogMergePolicy) and keeps IndexWriter's behaviour:
    • the same opstamps from commit and rollback;
    • a delete affects only documents added before it;
    • delete_all and rollback work the same way;
    • the writer lock is held for the writer's lifetime;
    • segments flush when they outgrow the memory budget;
    • tantivy's default merge policy still applies.
  • scripts/build-wasm.sh compiles the existing C ABI to wasm32-wasip1 with that feature, without mmap or zstd, as a cdylib. JavaScript calls the same tantivy_* functions Swift does. The only WASM-specific code is rust/src/wasm.rs, which adds tantivy_alloc / tantivy_dealloc.
  • js/ is a TypeScript package, @carbon/tantivy, marked private: true for now so it can't be published by accident. It mirrors the Swift API: SchemaBuilder, Index, IndexWriter, Query, SearchHit and SearchCollection. The mapping table is in js/README.md.
    • It has no runtime dependencies, and the 8 WASI imports are shimmed in engine.ts.
    • Each index gets its own WebAssembly instance, so close() returns its memory and a panic retires only that index.
  • CI:
    • The Swift job becomes a matrix that runs the full suite against both writers.
    • A new Ubuntu job builds the WASM module and runs the JS tests.
  • scripts/build-host.sh forwards its arguments to cargo, e.g. --features single-threaded.

Why

We want to run TantivySwift's engine in the browser without SharedArrayBuffer or cross-origin-isolation headers, which the stock threaded build would need. Keeping the JS package on the same C ABI keeps its API in step with Swift.

Reviewer notes

  • The Swift build is unchanged. The native default feature re-enables mmap and columnar-zstd-compression exactly as tantivy's defaults did, and IndexWriter is still used. The only other native change is that indexes are now created through Index::builder(), which is equivalent.
  • Merges run during commit() in the single-threaded writer, instead of in the background.
  • Why the merge copies files: merge_filtered_segments writes into its own index, so the merge output is copied back into the main index. IndexMerger isn't public.
  • Size: the WASM module is 5.1 MB, 1.6 MB gzipped, with symbols stripped. opt-level=s saves about 280 KB gzipped but makes search about 1.6× slower, so the build stays at 3.

Testing

  • swift test passes 420/420 with the threaded writer and 420/420 with --features single-threaded, including the on-disk tests.
  • npm test passes 40/40 under Node 24.
  • js/example/index.html ran in Chromium: it loaded, indexed and searched, with no console errors.
  • cargo test passes under both feature sets.

🤖 Generated with Claude Code

…ckage

tantivy's IndexWriter spawns indexing, merge and doc-store threads, which
WebAssembly without threads cannot start. A new `single-threaded` Cargo
feature swaps in a writer (rust/src/single_threaded.rs) that does the same
work on the calling thread from tantivy's public pieces, keeping IndexWriter's
opstamps, delete ordering and merge policy. The feature is off by default:
the Swift build keeps tantivy's threaded writer and on-disk indexes.

scripts/build-wasm.sh compiles the existing C ABI for wasm32-wasip1 with that
feature and without mmap/zstd. js/ wraps it in a dependency-free TypeScript
package mirroring the Swift API, one WebAssembly instance per index.

CI runs the Swift suite against both writers and adds a WASM + JS test job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iamcarbon
iamcarbon merged commit 1cafe14 into main Sep 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants