Install
Add the packages or the wheel, see which engine build each import loads, and check the install with one script.
Two packages: geospec, the API, published on the beta tag, and geospec-engine, the engine as a WASM module plus a native addon where a prebuild exists. Node 24 or later.
npm install geospec@beta geospec-enginegeospec run, the spec runner, is the geospec command that geospec installs. geospec depends on geospec-engine itself, so a project that only runs specs needs one package:
npm install --save-dev geospec@betaOn Linux x64, geospec depends on geospec-engine, zod, @taucad/runtime and @taucad/esbuild; with the engine and one platform package, node_modules is about 140 MB.
One wheel, geospec-engine, installs the geospec package and the engine as a CPython extension. CPython 3.13 or 3.14.
pip install geospec-engineWhat gets installed
| Package | Delivers |
|---|---|
geospec | expectGeo, the model loader, the assertion client and the geospec command; JavaScript only |
geospec-engine | The WASM module and the native addon's loader |
geospec- | The native addon, as an optional dependency of geospec-engine |
Three platform packages exist. Each declares the os, cpu and libc it serves; the package manager installs the one that matches.
| Package | Platform |
|---|---|
geospec- | macOS on Apple Silicon |
geospec- | Linux on x64 with glibc 2.28 or newer |
geospec- | Linux on arm64 with glibc 2.28 or newer |
An install on any other platform, such as Windows or Linux with musl, succeeds. There geospec-engine/node throws Cannot find native binding and never falls back, while geospec-engine runs the WASM module. The compatibility table names the CI job behind each row.
The wheel installs geospec, with load_model and expect_geo, the geospec_ extension and a pytest plugin that registers itself. Wheels exist for CPython 3.13 and 3.14 on macOS arm64 and on manylinux_2_28 x86_64 and aarch64; PyPI carries stable releases only.
Native addon or WASM
| Host | Import | Notes |
|---|---|---|
| Node 24+ on macOS arm64 or Linux x64 or arm64 glibc | geospec-engine/node | Native addon from the prebuild; no initialize |
| Node 24+ elsewhere | geospec-engine | WASM; call initialize() once first |
| Chromium, Firefox, WebKit | geospec-engine | WASM; serve or bundle the .wasm file |
Either import gives an Engine for createModelLoader({ engine }), and both return the same result bytes for the same claim: the choice changes speed, not verdicts. The WASM module is 16.7 MB and compiles once per process; the native addon skips that cost, and its Engine takes an optional cache location and execution-permit count. Both take the host limits the API reference lists.
Python has one build, the extension inside the wheel; GeoSpecEngine owns it.
What to import
| Import | Gives |
|---|---|
geospec | expectGeo and the expectation types; describe and it for spec files |
geospec/model | createModelLoader, which binds loadModel to an engine |
geospec/ | create with query, and Geo; no Node imports |
geospec/vitest | setupGeoSpecVitest, which registers the matchers on Vitest's expect |
geospec-engine | Engine and initialize, over WASM |
geospec-engine/node | Engine, over the native addon |
One module, geospec, exports every name on this site: GeoSpecEngine, load_model, expect_geo, evaluate_geo, query_geo, Geo and GeoSpecRegex.
Node, browsers and bundlers
geospec-engine/node resolves through the node export condition, so Node, a Vite SSR build and a Next.js server build reach the addon and a browser bundle cannot. Keep the engine external in a server bundle, so the addon loads from disk:
export default { serverExternalPackages: ['geospec-engine'] };export default { ssr: { external: ['geospec-engine'] } };A browser build needs the .wasm file reachable at runtime: initialize() loads it from the package, and initialize(input) takes bytes, a Response, a URL or a string, so serve geospec- and pass its URL (Vite gives it with a ?url import). geospec/model reads files through node:fs/promises; a page keeps the engine in a worker and reaches it through geospec/, as Run in the browser shows.
Nothing to bundle: the extension loads from the wheel.
Installing for another platform
An image for another platform needs that platform's package. pnpm and Yarn Berry accept the set to install:
supportedArchitectures:
os: [linux, darwin]
cpu: [x64, arm64]
libc: [glibc]pip picks the wheel for the interpreter and platform it runs on.
Check the install
Save the script next to the plate, a 40 × 40 × 20 mm STEP part, and run node check.ts. It loads the native addon, so it proves the platform package is in place; the quick start runs the same claim over WASM.
import { readFile } from 'node:fs/promises';
import { expectGeo } from 'geospec';
import { createModelLoader } from 'geospec/model';
import { Engine } from 'geospec-engine/node';
const engine = new Engine();
const loadModel = createModelLoader({
engine,
readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const size = expectGeo(plate).toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
console.log(size.report?.status);
await loadModel.dispose();
engine.close();Save the script next to the plate, a 40 × 40 × 20 mm STEP part, and run python check.py.
from geospec import GeoSpecEngine, expect_geo, load_model
with GeoSpecEngine(model_root="."):
plate = load_model(file="plate-with-through-bore.step", format="step")
size = expect_geo(plate).to_have_bounding_box(size={"x": 40, "y": 40, "z": 20})
print(size.result["status"])Expected output
passedCheck the package
npm releases publish from CI through OIDC with provenance. Check the signatures of what npm installed:
npm audit signaturesPyPI releases publish from CI through OIDC with provenance; each wheel's PyPI file page carries its attestation.