GeoSpec
engine 0.4.0

Install

Add the packages or the wheel, see which engine build each import loads, and check the install with one script.

Open Markdown

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-engine

geospec 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@beta

On 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-engine

What gets installed

PackageDelivers
geospecexpectGeo, the model loader, the assertion client and the geospec command; JavaScript only
geospec-engineThe WASM module and the native addon's loader
geospec-engine-<platform>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.

PackagePlatform
geospec-engine-darwin-arm64macOS on Apple Silicon
geospec-engine-linux-x64-gnuLinux on x64 with glibc 2.28 or newer
geospec-engine-linux-arm64-gnuLinux 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_engine_native 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

HostImportNotes
Node 24+ on macOS arm64 or Linux x64 or arm64 glibcgeospec-engine/nodeNative addon from the prebuild; no initialize
Node 24+ elsewheregeospec-engineWASM; call initialize() once first
Chromium, Firefox, WebKitgeospec-engineWASM; 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

ImportGives
geospecexpectGeo and the expectation types; describe and it for spec files
geospec/modelcreateModelLoader, which binds loadModel to an engine
geospec/assertion-clientcreateGeoSpecAssertionClient with query, and GeoSpecAssertionError; no Node imports
geospec/vitestsetupGeoSpecVitest, which registers the matchers on Vitest's expect
geospec-engineEngine and initialize, over WASM
geospec-engine/nodeEngine, over the native addon

One module, geospec, exports every name on this site: GeoSpecEngine, load_model, expect_geo, evaluate_geo, query_geo, GeoSpecAssertionError 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:

next.config.js
export default { serverExternalPackages: ['geospec-engine'] };
vite.config.js
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-engine/wasm-binary 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/assertion-client, 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:

pnpm-workspace.yaml
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.

check.ts
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.

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

passed

Check the package

npm releases publish from CI through OIDC with provenance. Check the signatures of what npm installed:

npm audit signatures

PyPI releases publish from CI through OIDC with provenance; each wheel's PyPI file page carries its attestation.

On this page