WebAssembly (WASM)
WASM shows up in two subsystems: Tree-sitter for code parsing across 7 languages, and sql.js for local SQLite. Both ship as portable .wasm — no native compilation, one bundle works on macOS, Linux, and Windows.
Why WASM here:
| Approach | Platform builds | Install friction | Perf |
|---|---|---|---|
| Native addon | 6+ (os × arch) | node-gyp + Python + C++ toolchain | Fastest |
| Pure JS | 1 | None | 10–100× slower |
| WASM | 1 | None | Near-native (1.2–2×) |
Tree-sitter grammars
Section titled “Tree-sitter grammars”web-tree-sitter wraps the Tree-sitter C library as WASM. Grammars are loaded lazily — only when a file of that language is first opened.
| Grammar | Extensions | Extracts |
|---|---|---|
tree-sitter-javascript.wasm | .js, .jsx, .mjs, .cjs | Functions, classes, methods, Express/Fastify routes |
tree-sitter-tsx.wasm | .ts, .tsx, .mts, .cts | Functions, classes, methods, NestJS + React |
tree-sitter-python.wasm | .py | Functions, classes, FastAPI / Flask / Django |
tree-sitter-java.wasm | .java | Methods, classes, Spring / JAX-RS annotations |
tree-sitter-go.wasm | .go | Functions, structs, Gin / Chi / Echo handlers |
tree-sitter-rust.wasm | .rs | Functions, structs, Actix / Axum / Rocket macros |
tree-sitter-php.wasm | .php, .phtml | Functions, classes, interfaces, Laravel / Symfony |
Runtime path resolution. Parser.init searches for tree-sitter.wasm in dist/grammars/, grammars/, out/grammars/, then node_modules/web-tree-sitter/. Handles different bundling strategies.
Parser pool. Per-language Map<language, { available: Parser[], inUse: Set<Parser> }>. Checks out a parser per file → returns after use. Avoids new Parser() + setLanguage() per file when analyzing many.
Fallback. If WASM fails to load, AstAnalyzerWorker + TreeSitterAnalyzer fall back to regex extraction covering the same languages with less accuracy (no nested structures, no multi-line signatures). Analysis always produces a result.
sql.js — SQLite in WASM
Section titled “sql.js — SQLite in WASM”Every persistent store the extension owns:
| DB file | Service | Purpose |
|---|---|---|
.codebuddy/codebase_analysis.db | SqliteDatabaseService | Codebase snapshots, git state, and chat history (table chat_history) |
.codebuddy/vector_store.db | SqliteVectorStore | Vector embeddings + FTS index + file metadata |
~/.codebuddy/telemetry/traces.db | TelemetryPersistenceService | OTel spans + metrics (global, not per-workspace) |
.codebuddy/checkpoints.db | SqljsCheckpointSaver | LangGraph state checkpoints |
.codebuddy/team_graph.db | TeamGraphStore | Team collaboration graph |
Vector store schema:
| Column | Type | Purpose |
|---|---|---|
id | TEXT | Chunk ID (filePath::offset) |
text | TEXT | Source chunk |
vector | BLOB | Float32 embedding (3072 bytes for 768-dim) |
filePath | TEXT | Source file |
startLine | INTEGER | Chunk start |
endLine | INTEGER | Chunk end |
chunkType | TEXT | function, class, method, text_chunk |
language | TEXT | Programming language |
FTS4 virtual table auto-synced via SQLite triggers. Cosine similarity computed in JavaScript with event-loop yielding for large result sets.
Persistence strategy — dirty-flag debounce:
- Any write sets
isDirty = true. - 5-second debounce timer (resets on subsequent writes).
- On fire:
db.export()→fs.writeFileSync(atomic write-temp-rename). - Extension deactivation triggers a final flush.
Batches rapid writes (e.g. indexing 200 files) into one disk write.
Memory model. sql.js runs entirely in memory. Fast reads, no disk I/O for queries. Memory proportional to data — a 50 MB vector store uses ~50 MB of heap. No WAL — concurrency handled at the JS layer via singletons + the chat-history worker’s concurrency guard.
Workers
Section titled “Workers”Tree-sitter WASM runs in main thread AND worker threads:
| Context | Service | WASM loaded |
|---|---|---|
| Main | TreeSitterParser | tree-sitter.wasm + language grammars |
| Codebase Analysis Worker | TreeSitterAnalyzer | tree-sitter.wasm + language grammars |
| AST Analyzer Worker | web-tree-sitter | tree-sitter.wasm (grammar loading optional) |
Each worker initializes its own WASM instance — memory isn’t shared across threads. grammarsPath passed via workerData so workers locate .wasm relative to the install dir.
Disposal
Section titled “Disposal”WASM memory isn’t GC’d by V8. Explicit disposal is required:
TreeSitterAnalyzer.dispose()in afinallyafter analysis completes- Parser pool entries cleaned on service deactivation
worker.terminate()releases all WASM allocations for that worker
Bundle layout
Section titled “Bundle layout”dist/ grammars/ tree-sitter.wasm (~400 KB core runtime) tree-sitter-javascript.wasm tree-sitter-tsx.wasm tree-sitter-python.wasm tree-sitter-java.wasm tree-sitter-go.wasm tree-sitter-rust.wasm tree-sitter-php.wasm sql-wasm.wasm (~1.2 MB SQLite runtime)Excluded from the esbuild bundle (loaded at runtime via fs.readFileSync), copied as static assets during build.
Related
Section titled “Related”- Workers — worker-thread architecture that uses these WASMs
- Semantic search — the primary consumer of
sql-wasm.wasm