@tabviz/core
v0.5.0
Published
Interactive forest plots and rich tables — JavaScript runtime for the tabviz R package
Downloads
2,194
Maintainers
Readme
@tabviz/core
JavaScript runtime + authoring API for tabviz — the publication-quality
forest plots and rich interactive tables otherwise consumed via the
tabviz R package.
Status: 0.2.0 — pre-1.0. Authoring API (function builders mirroring R's
tabviz()/col_*()/viz_*()/theme_*()) landed; wire format stable at v1.0; subpath shape stable. Seedocs/dev/r-ts-parity-notes.mdfor per-helper R↔TS parity status and known gaps.
Quick start — build a spec, mount it
import {
tabviz, createTabviz,
colText, colInterval, vizForest,
} from "@tabviz/core";
import "@tabviz/core/style.css";
const spec = tabviz({
data: rows,
label: "study",
theme: "lancet",
columns: [
colText({ field: "study", header: "Study" }),
colInterval({ point: "hr", lower: "lcl", upper: "ucl" }),
vizForest({ point: "hr", lower: "lcl", upper: "ucl", scale: "log" }),
],
title: "Hazard ratios across studies",
});
const instance = createTabviz(document.querySelector("#plot")!, spec);tabviz() returns a WebSpec (the wire-format payload). createTabviz
mounts it. Every R helper has a TS mirror — argument names + defaults
match; snake_case in R → camelCase in TS.
What this package will publish
@tabviz/core exposes five subpaths over a single versioned npm
package — one repo, one version, one source of truth.
| Subpath | What it exports | Who reaches for it |
|---|---|---|
| @tabviz/core | createTabviz, createSplitTabviz factories + wire-format types | Web-app consumers wanting visual parity with R-rendered tabvizes; vanilla TS/JS in any framework |
| @tabviz/core/svelte | ForestPlot, SplitForestPlot components, createForestStore factories | Svelte consumers composing the components directly |
| @tabviz/core/export | exportToSVG, exportToPNG, computeNaturalDimensions | Headless rendering pipelines (V8, node-rsvg, server-side export) |
| @tabviz/core/spec | TypeScript types + JSON Schema (v1.0.json) | Consumers validating wire payloads |
| @tabviz/core/htmlwidgets | R-side htmlwidgets adapters | The R package's vendored build artifacts (not for direct npm use) |
Installation
npm install @tabviz/core svelteSvelte is the underlying component runtime. You don't write Svelte
code — createTabviz returns a plain imperative handle — but the
main entry and the /svelte subpath both need Svelte 5 installed as
a peer. The /export and /spec subpaths are framework-free and
work without it.
Quick start (preview)
import { createTabviz } from "@tabviz/core";
import "@tabviz/core/style.css";
const instance = createTabviz(document.querySelector("#plot")!, spec, {
width: 800,
height: 600,
});
instance.on("selected", (rowIds) => console.log("selection:", rowIds));
instance.sortBy({ column: "estimate", direction: "asc" });Spec shape is defined in @tabviz/core/spec; you can validate against
the JSON Schema (one schema file per minor version — currently
v1.0.json).
Wire-format versioning
Every WebSpec carries an explicit version field. The runtime
validates it on every render. See
docs/dev/versioning.md for the SemVer
policy and minor-evolution rules.
Consuming from non-Svelte frameworks
The createTabviz factory returns a typed
TabvizInstance with update, sortBy,
applyFilter, selectRows, setSemantic, setTheme, setZoom,
setAspectRatio, on (typed event subscriptions), destroy. Internally
it mounts the Svelte components into the host element; from the React /
Vue / Solid side you treat the host element + instance handle as an
opaque imperative widget. You still install Svelte as a peer dep (the
runtime is bundled-less so the consumer's app and @tabviz/core share
the same Svelte instance).
Source layout
| Path | Purpose |
|---|---|
| src/core/ | Framework-agnostic factories (createTabviz, createSplitTabviz) |
| src/svelte/ | Top-level Svelte components published as /svelte |
| src/export/ | Pure-function SVG/PNG generators + V8 shim |
| src/spec/ | TypeScript types + JSON Schema per minor |
| src/htmlwidgets/ | R-side htmlwidgets adapters (Shiny proxy, message channels) |
| src/stores/ | Svelte 5 reactive stores |
| src/lib/ | Cross-cutting utilities (formatters, scales, banding, etc.) |
| src/components/ | Internal sub-components (controls, viz, table cells) |
The lib/ and components/ trees are implementation details bundled
into the published chunks; they're not part of the public subpath
surface.
Building
Four build paths produce four artifact sets:
npm run build # the three R-side IIFE bundles
npm run build:widget # inst/htmlwidgets/tabviz.js
npm run build:split # inst/htmlwidgets/tabviz_split.js
npm run build:v8 # inst/js/svg-generator.js
npm run build:npm # dist/ ESM bundles + style.css + .d.ts typesR-side bundles ship vendored inside inst/ for htmlwidgets consumers.
The npm path emits per-subpath ESM into dist/:
dist/
├── index.mjs # @tabviz/core entry
├── svelte.mjs # @tabviz/core/svelte entry
├── export.mjs # @tabviz/core/export entry
├── spec.mjs # @tabviz/core/spec entry
├── style.css # standalone CSS for npm consumers
├── chunks/ # shared chunks
├── core/, svelte/, export/, spec/, … # emitted .d.ts treesbuild:npm runs vite, then tsc --emitDeclarationOnly, then a small
postprocessor (scripts/rewrite-dts-aliases.mjs) that rewrites the
internal $lib/$stores/$types/… aliases in the published .d.ts
files to relative paths so npm consumers don't need our tsconfig.paths.
CI gates
Two scripts gate publish-readiness:
npm run check:size # fails on any bundle >10% over budget
npm run check:lockfiles # validates package-lock.json + bun.lock both sync with package.jsonThe size budget lives in bundle-size-budget.json (raw bytes,
intentional growth is bumped in the same PR with justification).
Toolchain
The build artifact ships from npm (npm ci in CI, package-lock.json
is canonical). Local development with bun is supported and tracked —
bun.lock is checked in so bun install + bun test work fast, but
the published artifact is built from package-lock.json. The two
lockfiles must agree on resolved versions; npm run check:lockfiles
gates this in CI.
Testing
npm test # bun + vitest
npm run test:bun # bun only — pure-TS tests
npm run test:vitest # vitest — Svelte-runes tests (.runes.ts)Bun runs the bulk of fast pure-TS tests. Vitest runs tests that need
the Svelte 5 runes compiler — those live under *.runes.ts so bun's
default *.{test,spec}.ts glob skips them.
Related
- tabviz R package README — the primary consumer surface
- Frontend split spec — program plan
- Wire-format versioning — SemVer policy
- Event contract — typed event reference
- Source tagging — Shiny envelope contract
- Spec fields reference — which
WebSpecfields are general vs forest-specific vs viz-family - R ↔ JS sync points — every cross-side mirror and its sync mechanism
