Migrate examples/coffee-vector-search to WASM.

This commit is contained in:
Sebastian Jeltsch
2025-09-09 16:08:01 +02:00
parent 1fbf0f862e
commit 5cb677ae55
20 changed files with 685 additions and 8 deletions

9
Cargo.lock generated
View File

@@ -1327,6 +1327,15 @@ dependencies = [
"thiserror 2.0.16",
]
[[package]]
name = "coffee-vector-search-guest"
version = "0.0.0"
dependencies = [
"serde",
"serde_json",
"trailbase-wasm",
]
[[package]]
name = "colorchoice"
version = "1.0.4"

View File

@@ -21,6 +21,7 @@ members = [
"docs/examples/record_api_rs",
"examples/custom-binary",
"examples/wasm-guest-rust",
"examples/coffee-vector-search/guests/rust",
]
default-members = [
"crates/assets",

View File

@@ -100,7 +100,7 @@ Let's have a quick look at `examples/coffee-vector-search/traildepot/scripts/mai
which defines a `/search` API route we'll later use in our application to
find coffees most closely matching our desired coffee notes:
import handlerCode from "@root/examples/coffee-vector-search/traildepot/scripts/main.ts?raw";
import handlerCode from "@root/examples/coffee-vector-search/traildepot/scripts.delme/main.ts?raw";
<Code
code={handlerCode}

View File

@@ -1,12 +1,38 @@
build_n_data: build data_init
TRAILBIN ?= RUST_BACKTRACE=1 cargo run --
TRAILDEPOT := traildepot
build: dist
APPDIR := .
DISTDIR := ${APPDIR}/dist
ADDRESS := 127.0.0.1:4000
run: init assets guest
${TRAILBIN} --data-dir=${TRAILDEPOT} run --public-dir=${DISTDIR} --address=${ADDRESS}
guest: ${TRAILDEPOT}/wasm/coffee_vector_search_rust_guest.wasm
#guest: ${TRAILDEPOT}/wasm/coffee_vector_search_ts_guest.wasm
assets:
rm -rf dist && pnpm build
data_init:
init:
rm -rf traildepot/data && mkdir -p traildepot/data && cat import.sql | sqlite3 traildepot/data/main.db -
run:
cargo run -- run --public-dir dist
${TRAILDEPOT}/wasm/coffee_vector_search_rust_guest.wasm: guests/rust/src/*.rs
cargo build -p coffee-vector-search-guest --target wasm32-wasip2 --release \
&& mkdir -p ${TRAILDEPOT}/wasm \
&& rm -rf ${TRAILDEPOT}/wasm/* \
&& cp ../../target/wasm32-wasip2/release/coffee_vector_search_guest.wasm $@
.PHONY: init
${TRAILDEPOT}/wasm/coffee_vector_search_ts_guest.wasm: guests/typescript/src/*.ts
cd guests/typescript \
&& pnpm install \
&& pnpm build \
&& cd ../.. \
&& mkdir -p ${TRAILDEPOT}/wasm \
&& rm -rf ${TRAILDEPOT}/wasm/* \
&& cp guests/typescript/dist/component.wasm $@
clean:
rm -rf dist traildepot/data ${TRAILDEPOT}/wasm/*
.PHONY: init assets guest run clean

View File

@@ -0,0 +1,433 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "example-wasm"
version = "0.0.0"
dependencies = [
"anyhow",
"wit-bindgen",
]
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "hashbrown"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
dependencies = [
"foldhash",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "indexmap"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
dependencies = [
"equivalent",
"hashbrown",
"serde",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "prettyplease"
version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "semver"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "slab"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "wasm-encoder"
version = "0.235.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.235.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.235.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917"
dependencies = [
"bitflags",
"hashbrown",
"indexmap",
"semver",
]
[[package]]
name = "wit-bindgen"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a18712ff1ec5bd09da500fe1e91dec11256b310da0ff33f8b4ec92b927cf0c6"
dependencies = [
"wit-bindgen-rt",
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fd734226eac1fd7c450956964e3a9094c9cee65e9dafdf126feef8c0096db65"
dependencies = [
"bitflags",
"futures",
"once_cell",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531ebfcec48e56473805285febdb450e270fa75b2dacb92816861d0473b4c15f"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7852bf8a9d1ea80884d26b864ddebd7b0c7636697c6ca10f4c6c93945e023966"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.235.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a57a11109cc553396f89f3a38a158a97d0b1adaec113bd73e0f64d30fb601f"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.235.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1f95a87d03a33e259af286b857a95911eb46236a0f726cbaec1227b3dfc67a"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]

View File

@@ -0,0 +1,14 @@
[package]
name = "coffee-vector-search-guest"
version = "0.0.0"
authors = ["someone"]
edition = "2024"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
serde = { version = "^1.0.203", features = ["derive"] }
serde_json = "^1.0.117"
trailbase-wasm = { workspace = true }

View File

@@ -0,0 +1,10 @@
deploy: clean build
cp ../../../../target/wasm32-wasip2/release/coffee_vector_search_guest.wasm ../../traildepot/wasm/
build:
cargo build --target wasm32-wasip2 --release
clean:
rm -rf ../../traildepot/scripts/*.wasm
.PHONY: build deploy clean

View File

@@ -0,0 +1,57 @@
#![forbid(unsafe_code, clippy::unwrap_used)]
#![allow(clippy::needless_return)]
#![warn(clippy::await_holding_lock, clippy::inefficient_to_string)]
use trailbase_wasm::db::{Value, query};
use trailbase_wasm::http::{HttpError, HttpRoute, Json, Request, StatusCode, routing};
use trailbase_wasm::{Guest, export};
async fn search_handler(req: Request) -> Result<Json<Vec<Vec<Value>>>, HttpError> {
let mut aroma: i64 = 8;
let mut flavor: i64 = 8;
let mut acidity: i64 = 8;
let mut sweetness: i64 = 8;
for (param, value) in req.url().query_pairs() {
match param.as_ref() {
"aroma" => aroma = value.parse().unwrap_or(aroma),
"flavor" => flavor = value.parse().unwrap_or(flavor),
"acidity" => acidity = value.parse().unwrap_or(acidity),
"sweetness" => sweetness = value.parse().unwrap_or(sweetness),
_ => {}
}
}
// Query with vector-search for the closest match.
let rows: Vec<Vec<Value>> = query(
r#"
SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, FORMAT("[%f, %f, %f, %f]", $1, $2, $3, $4))
LIMIT 100
"#
.to_string(),
vec![
Value::Integer(aroma),
Value::Integer(flavor),
Value::Integer(acidity),
Value::Integer(sweetness),
],
)
.await
.map_err(|err| HttpError::message(StatusCode::INTERNAL_SERVER_ERROR, err))?;
return Ok(Json(rows));
}
// Implement the function exported in this world (see above).
struct Endpoints;
impl Guest for Endpoints {
fn http_handlers() -> Vec<HttpRoute> {
return vec![routing::get("/search", search_handler)];
}
}
export!(Endpoints);

View File

@@ -0,0 +1,2 @@
dist/
node_modules/

View File

@@ -0,0 +1,27 @@
{
"name": "coffee-vector-search-guest",
"version": "0.1.0",
"keywords": [],
"author": "",
"main": "dist/index.js",
"scripts": {
"generate:types": "jco types ../../../../guests/typescript/wit/ -o generated/types",
"build:tsc": "vite build",
"build:wasm": "jco componentize dist/index.mjs -w ../../../../guests/typescript/wit -o dist/component.wasm",
"build:wasm:aot": "jco componentize dist/index.mjs -w ../../../../guests/typescript/wit --aot -o dist/component.wasm",
"build": "npm run build:tsc && npm run build:wasm",
"format": "prettier -w src"
},
"dependencies": {
"trailbase-wasm": "workspace:*"
},
"devDependencies": {
"@bytecodealliance/componentize-js": "^0.18.4",
"@bytecodealliance/jco": "^1.13.3",
"@types/node": "^24.3.0",
"prettier": "^3.6.2",
"typescript": "^5.9.2",
"vite": "^7.1.2",
"vitest": "^3.2.4"
}
}

View File

@@ -0,0 +1,3 @@
import e from "./index";
export const { initEndpoint, incomingHandler } = e;

View File

@@ -0,0 +1,27 @@
import { defineConfig } from "trailbase-wasm";
import { Request, HttpHandler } from "trailbase-wasm/http";
import { query } from "trailbase-wasm/db";
async function searchHandler(req: Request): Promise<string> {
// Get the query params from the url, e.g. '/search?aroma=4&acidity=7'.
const aroma = req.getQueryParam("aroma") ?? 8;
const flavor = req.getQueryParam("flavor") ?? 8;
const acid = req.getQueryParam("acidity") ?? 8;
const sweet = req.getQueryParam("sweetness") ?? 8;
// Query the database for the closest match.
const rows = await query(
`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, FORMAT("[%f, %f, %f, %f]", $1, $2, $3, $4))
LIMIT 100`,
[+aroma, +flavor, +acid, +sweet],
);
return JSON.stringify(rows);
}
export default defineConfig({
httpHandlers: [HttpHandler.get("/search", searchHandler)],
});

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "es2022", /* Specify what module code is generated. */
"moduleResolution": "bundler",
"paths": {},
"outDir": "./dist/", /* Specify an output folder for all emitted files. */
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@@ -0,0 +1,19 @@
import { defineConfig } from "vite";
export default defineConfig({
build: {
outDir: "./dist",
minify: false,
lib: {
entry: "./src/component.js",
name: "runtime",
fileName: "index",
formats: ["es"],
},
rollupOptions: {
external: (source) => {
return source.startsWith("wasi:") || source.startsWith("trailbase:");
},
},
},
})

View File

@@ -6,7 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"format": "prettier -w src traildepot/scripts",
"format": "prettier -w src",
"lint": "eslint .",
"preview": "vite preview"
},

View File

@@ -1,7 +1,14 @@
# Deployment-specific directories:
backups/
data/
secrets/
uploads/
wasm/
scripts.delme/
# Runtime files, will be overriden by `trail`.
trailbase.d.ts
trailbase.js
# Any potential MaxMind GeoIP dbs.
*.mmdb

28
pnpm-lock.yaml generated
View File

@@ -647,6 +647,34 @@ importers:
specifier: ^7.1.3
version: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.39.0)(yaml@2.8.1)
examples/coffee-vector-search/guests/typescript:
dependencies:
trailbase-wasm:
specifier: workspace:*
version: link:../../../../guests/typescript
devDependencies:
'@bytecodealliance/componentize-js':
specifier: ^0.18.4
version: 0.18.5
'@bytecodealliance/jco':
specifier: ^1.13.3
version: 1.14.0
'@types/node':
specifier: ^24.3.0
version: 24.3.0
prettier:
specifier: ^3.6.2
version: 3.6.2
typescript:
specifier: ^5.9.2
version: 5.9.2
vite:
specifier: ^7.1.2
version: 7.1.3(@types/node@24.3.0)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.39.0)(yaml@2.8.1)
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.0)(happy-dom@15.11.7)(jiti@2.5.1)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.39.0)(yaml@2.8.1)
examples/collab-clicker-ssr:
dependencies:
solid-js:

View File

@@ -8,6 +8,7 @@ packages:
- 'docs/examples/record_api_ts'
- 'examples/blog/web'
- 'examples/coffee-vector-search'
- 'examples/coffee-vector-search/guests/typescript'
- 'examples/collab-clicker-ssr'
- 'examples/data-cli-tutorial'
- 'examples/tanstack-db-sync'