Packages Layout
Each TypeScript package under packages/ follows the same skeleton, paired with a corresponding Rust crate under crates/.
Per-package skeleton
packages/<name>/
├── package.json ← npm metadata, scripts (build, test, typecheck)
├── tsconfig.json ← extends ../../tsconfig.base.json
├── README.md ← short package overview (gitignored output here)
├── src/
│ ├── index.ts ← public exports — what users import
│ ├── wasm.ts ← async WASM loader, called lazily
│ ├── index.test.ts ← vitest tests
│ └── shaders/ ← *.wgsl compute shaders (bin2d wired, opt-in — ADR 0004; others are unwired drafts)
├── wasm/ ← generated by wasm-bindgen — gitignored
└── dist/ ← generated by tsc — gitignoredPer-crate skeleton
crates/vizcrush-<name>/
├── Cargo.toml ← inherits from workspace root
└── src/
├── lib.rs ← module declarations + #[wasm_bindgen] re-exports
├── <module>.rs ← one file per algorithm
└── <module>_test.rs ← cargo testThe 9 algorithm packages
| TypeScript package | Rust crate | What it ships |
|---|---|---|
@vizcrush/core | vizcrush-core (unrelated content) | init, detectCapabilities, selectBackend, defineKernel/createWasmLoader (the WASM/JS dispatch kernel every other package builds on). The Rust crate of the same name is a separate, internal-only utility (shared bounds-finding for vizcrush-bin/bin3d/spatial/spatial3d) — nothing in this row is implemented there. |
@vizcrush/downsample | vizcrush-downsample | lttb, lttbSync, minMaxLttb, m4, ltob |
@vizcrush/aggregate | vizcrush-aggregate | stats, percentile, StreamingStats, appendAndDownsample |
@vizcrush/transform | vizcrush-transform | sortBy, normalize, filterRange |
@vizcrush/bin | vizcrush-bin | bin1d, bin2d (incl. opt-in WebGPU path), hexbin |
@vizcrush/bin3d | vizcrush-bin3d | bin3d |
@vizcrush/spatial | vizcrush-spatial | buildQuadtree, queryRange, queryNearest |
@vizcrush/spatial3d | vizcrush-spatial3d | buildOctree, queryRange3d, queryNearest3d, frustumCull |
@vizcrush/ai | vizcrush-ai (planned) | detectAnomalies, detectChangepoints, autoOptimize, summarize, computeShapeVector, shapeSimilarity, parseDataQuery |
The 2 integration packages
| Package | Rust crate? | What it ships |
|---|---|---|
@vizcrush/react | none | useVizcrush, useDownsample, useBin2d, useStats, useStreamingStats |
@vizcrush/mcp-server | none | MCP server (stdio + HTTP) exposing 23 tools, 3 prompts, 2 resources |
Public API conventions
| Convention | Why |
|---|---|
Public exports go in src/index.ts | Single source of truth for what users can import |
| Functions are async by default | WASM loading is async |
| Inputs and outputs are typed arrays | Zero-copy across JS↔WASM, cache-friendly inside Rust |
Paired (x, y) results are interleaved | Drop-in for most charting libraries |
options.backend for per-call override | "auto" default, "wasm" / "js" to force |
Cross-package dependencies
The packages form a small DAG:
@vizcrush/core
▲
│
├──── @vizcrush/downsample
├──── @vizcrush/aggregate ──┐
├──── @vizcrush/transform │
├──── @vizcrush/bin │
├──── @vizcrush/bin3d │
├──── @vizcrush/spatial │
├──── @vizcrush/spatial3d │
└──── @vizcrush/ai │
│
@vizcrush/react ◄────────── (uses all of the above)
@vizcrush/mcp-server ◄────── (uses all of the above)@vizcrush/core is the only mandatory dependency for the algorithm packages. The integration packages (react, mcp-server) depend on whichever algorithm packages they expose.
Adding a new package
The full sequence:
- Create
crates/vizcrush-<name>/with a minimalCargo.tomlthat inherits workspace settings - Create
packages/<name>/with apackage.json,tsconfig.json,src/index.ts, and asrc/wasm.tsloader - Add the new package name to
pnpm-workspace.yaml(it's already a wildcard, so this is automatic) and to the workspaceCargo.toml'smembersarray - Add a
lint,typecheck, andbuildscript in the newpackage.jsonmatching the existing packages - Run
pnpm installto wire workspace deps, thenpnpm buildto verify - Add a docs page under
docs/site/docs/packages/<name>.mdand updatemkdocs.yml's nav
See also
- Architecture — high-level layering
- Building from Source — local dev workflow
- Contributing — how to propose changes