Dev setup, quality gates, project structure, and release flow.
vp): the sole developer command interface for dependencies, tasks, checks, builds, and development servers.package.json.Use vp for every command you run directly. vp run dispatches package scripts, which may use Bun for tests, scripts, and single-binary compilation.
git clone https://github.com/czbiohub-sf/nd-embedding-atlas.git
cd nd-embedding-atlas
vp install
cd docs && vp install && cd ..The root install resolves the application and shared packages. The docs app keeps its own
lockfile and install under docs/.
# Audit root tooling and every workspace
vp outdated
vp outdated -r
# Update workspace resolutions within declared ranges
vp update -r
# Add or remove a dependency in the selected workspace
vp add --filter @ndea/app <package>
vp remove --filter @ndea/app <package>
# Update the independent docs application
cd docs
vp outdated
vp updateShared catalog constraints live in the root package.json; update each constraint there
once, then run vp install. Update root-only tooling packages reported by vp outdated
explicitly with vp update <package...>; a blanket root update would replace unrelated
catalog: references with package-local ranges. Review overrides separately because they
are deliberately pinned.
# Full dev stack: backend on :5055 + Vite frontend on :5173 with HMR
vp run dev path/to/data.zarrThe command starts apps/ndea/src/cli/index.ts and the app's Vite server.
Production builds open a shipped --preset layout (annotate by default).
Frontend-only when the backend already runs separately:
vp dev apps/ndeavp check vite.config.ts bunli.config.ts scripts
vp run -r check # package checks in dependency order
vp run -r test # package tests in dependency order
vp run build # frontend + single-file binaryCI runs the same checks and tests, verifies generated CLI metadata, and builds the native binary matrix.
Fallow checks for dead code, duplication, complexity, and dependency problems. Run it through the root Vite+ task when reviewing a branch:
vp run audit --changed-since mainThis audit is optional and does not run in CI.
Enforced by vp check:
import type for type-only importsvite.config.ts lint / fmt blocks@/ path alias → apps/ndea/src/frontend/useX hook modules. Oxlint enforces these
filename shapes; review enforces each file's semantic role.@ndea/* barrels.apps/ndea/ # CLI, server, frontend, and binary builder
packages/protocol/ # Shared Zod request and response schemas
packages/sdk/ # Host contracts and shared application types
packages/zarr/ # Bun-backed AnnData, MuData, and OME-Zarr I/O
docs/ # Independent Waku app and lockfileSee AGENTS.md for the canonical command catalogue, key abstractions, and gotchas.
| Channel | How it ships |
|---|---|
stable | Manual: tag vX.Y.Z and push; release.yml builds + publishes |
pre-release | Manual: tag vX.Y.Z-alpha.N / -beta.N / -rc.N and push |
GitHub marks hyphenated semver tags as pre-releases. Stable/latest resolve through GitHub's latest release endpoint; pre-release resolves to the newest published, non-draft semver pre-release.
The release workflow uploads scripts/install.sh and scripts/install.ps1 as per-tag assets, so users can pin the installer URL: curl …/releases/download/v0.X.Y/install.sh | sh. The docs workflow copies both to https://czbiohub-sf.github.io/nd-embedding-atlas/install.sh and …/install.ps1, which always reflect the latest installers on main.
Each platform builds on its own runner. duckdb.node is a native N-API addon, so a cross-compiled binary would embed the host's addon rather than the target's:
| Runner | bun --target | Release asset |
|---|---|---|
macos-14 | bun-darwin-arm64 | ndea-darwin-arm64 |
ubuntu-latest | bun-linux-x64 | ndea-linux-x64 |
ubuntu-24.04-arm | bun-linux-arm64 | ndea-linux-arm64 |
windows-latest | bun-windows-x64 | ndea-windows-x64.exe |
Note that process.platform reports win32 while Bun's target triple spells the OS windows, and the DuckDB bindings directory is node-bindings-win32-x64. apps/ndea/scripts/build.ts owns the mapping between the three.
After editing apps/ndea/src/cli/commands/**, regenerate the completion metadata:
vp run genUpdates .bunli/commands.gen.ts, which feeds shell-completion script generation. CI fails on drift (.github/scripts/check-bunli-gen.sh).
vp run build
./dist/ndea doctorndea doctor prints binary path, symlink integrity, active version, and the installed-versions tree. Exit code 1 on hard anomalies (broken symlink, missing active binary). Add --check-network to probe GitHub Releases API reachability with a 3-second timeout. On Windows the symlink and installed-versions sections are omitted, because that layout has neither.
CI also runs .github/scripts/verify-isolated.sh, which copies the fresh binary into an otherwise empty directory before launching it. That guards the single-file invariant: the binary embeds libduckdb and extracts it to a cache directory, so a sibling library file must never be required. The check matters most on Windows, where the loader searches the executable's own directory early enough that a stray duckdb.dll would mask a broken embed.
This site is built with Fumapress (Fumadocs + Waku). Pages are MDX under docs/content/; the app lives in docs/ with its own package.json and node_modules, isolated from the main project's dependency graph.
# From the repository root
vp run docs:build
vp run docs:serve # http://localhost:8080/nd-embedding-atlas/
# Local development with hot reload
vp run docs:devBecause pages are MDX, they can embed React. vp run docs:build writes
docs/dist/public; CI deploys that directory to GitHub Pages.