qql-wasm
v0.5.0
Published
WebAssembly bindings for QQL parsing, planning, and Qdrant execution
Maintainers
Readme
qql-wasm
WebAssembly bindings for QQL: parse, plan, optional browser execute (fetch).
Proposition
Run the same QQL frontend offline in the browser (or Workers/Deno/Bun with the
right wasm-bindgen target). Optional Client posts REST to Qdrant and can
attach HTTP/JS embedders. Plan IR is shared with native crates. Language
surface tracks Qdrant ≥ 1.19 (parse/plan/compile always; live quotas and
1.19 body fields need a ≥ 1.19 server when executing).
Install
npm install qql-wasm
# or: wasm-pack build --target web|bundler|nodejsQuick start
import init, {
Client, Stmt, parse, isValid, injectFilter, compile, explain, analyze, tokenize,
} from "qql-wasm";
await init();
// Offline: tokens + AST + route + explain
const info = analyze("QUERY TEXT 'machine learning' FROM docs USING dense LIMIT 10");
console.log(info.valid, info.route, info.explain);
// Live REST (client feature)
const client = new Client("http://localhost:6333", null);
client.setHttpEmbedder(
"http://localhost:11434/v1/embeddings",
"all-minilm:l6-v2",
384,
null,
);
const report = await client.execute(
"QUERY TEXT 'machine learning' FROM docs USING dense LIMIT 5"
);
// Isolation + optional routing
const stmt = new Stmt("QUERY TEXT 'search' FROM docs USING dense LIMIT 10");
stmt.injectFilter("tenant_id", "=", "acme");
// Prefer: ... SHARD 'acme' ...
stmt.shardKey = "acme"; // same field as SHARD — no injectShardKey
await client.executeStmt(stmt);
stmt.free();
client.free();API
Free functions
| Export | Role |
|--------|------|
| parse / isValid / tokenize | Frontend — isValid is the full gate (parse + plan), matching execution and the language conformance suite |
| injectFilter | Isolation |
| analyze | tokens + AST + route(s) + explain |
| compile / explain | Offline REST projection / plan text |
| bind(query, params) | Substitute :name (object) or ? (array) |
Stmt
| Member | Role |
|--------|------|
| injectFilter | Isolation |
| shardKey | Get/set routing (= QQL SHARD) |
| toJson (string) / toJSON (= toObject, JSON.stringify hook) / compileRoute | Serialize / project |
Client (client feature)
| Method | Role |
|--------|------|
| setHttpEmbedder / setEmbedder | Dense embed hosts |
| setBm25Params(k1, b, avgLen) | Local BM25 document encoder tuning (write-path only; defaults 1.2 / 0.75 / 256; invalid → QQL-VALIDATION-CONFIG) |
| setRouteAffinity(key) / routeAffinity | Qdrant 1.19 read affinity (get/set) |
| execute / executeStmt | REST execute → ExecutionReport (options.params same as bind) |
| compile / explain | Offline helpers |
Response shaping is strict and canonical: read operations parse exactly their
Qdrant OpenAPI response field and emit the same JSON shapes as the native SDKs'
typed reports; a missing or mistyped field fails with QQL-BACKEND-ENVELOPE
(no fallback shapes). Server telemetry stays optional and lenient.
Batched execution is at-least-once: when one RPC carries an auto-grouped or
explicit BATCH set and the connection fails or times out, members are retried
individually — a member that already landed is applied again. Keep mutations
idempotent (or use UPDATE MODE deliberately). A batch the server answered
with a cardinality mismatch or an unparsable shape is never retried; its
members report QQL-BACKEND-BATCH / QQL-BACKEND-ENVELOPE instead. Errors
thrown across the JS boundary are structured: err.code, err.kind,
err.span, and err.fields (via dx.js buildError), including option and
embedder-config misuse.
Features
| Feature | Default | Role |
|---------|---------|------|
| client | yes | gloo-net fetch client + embedders |
# Parser/compiler only
cargo build -p qql-wasm --target wasm32-unknown-unknown --no-default-featuresLimits
- JS host ABI (
wasm-bindgen), not generic WASI - Single-threaded async on
wasm32-unknown-unknown - Multivector embed needs a multi-capable host embedder (default HTTP is dense)
- Live
Clientis REST-only (no gRPC) — soSHOW QUOTAS/SET QUOTAwork against Qdrant ≥ 1.19 when executed; offlinecompile/analyzeproject them toGET|PUT /quotas
// Qdrant 1.19 read affinity (REST header `X-Qdrant-Route-Affinity`)
client.setRouteAffinity("session-acme-42");
console.log(client.routeAffinity); // "session-acme-42"
client.setRouteAffinity(null); // clear// Offline plan of 1.19 surfaces
const info = analyze("SHOW QUOTAS");
// Live REST (server ≥ 1.19)
await client.execute(
"SET QUOTA (enabled = true, max_resident_memory_percent = 80) WAIT true"
);
await client.execute(
"QUERY TEXT 'q' FROM docs USING dense WHERE title MATCH PREFIX 'Comp' LIMIT 5"
);