# Install

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

Canonical page: https://www.geospec.xyz/docs/install

**TypeScript**

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.

<CodeBlockTabs defaultValue="npm">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm install geospec@beta geospec-engine
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add geospec@beta geospec-engine
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add geospec@beta geospec-engine
    ```
  </CodeBlockTab>
</CodeBlockTabs>

`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:

```bash
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.

**Python**

One wheel, `geospec-engine`, installs the `geospec` package and the engine as a CPython extension. CPython 3.13 or 3.14.

<CodeBlockTabs defaultValue="pip">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="pip">
      pip
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="pip">
    ```bash
    pip install geospec-engine
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## What gets installed [#what-gets-installed]

**TypeScript**

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

| Package                          | Platform                                |
| -------------------------------- | --------------------------------------- |
| `geospec-engine-darwin-arm64`    | macOS on Apple Silicon                  |
| `geospec-engine-linux-x64-gnu`   | Linux on x64 with glibc 2.28 or newer   |
| `geospec-engine-linux-arm64-gnu` | 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](https://github.com/taucad/geospec/blob/main/compatibility.md) names the CI job behind each row.

**Python**

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 [#native-addon-or-wasm]

**TypeScript**

| 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](https://www.geospec.xyz/docs/api.mdx#engine) lists.

**Python**

Python has one build, the extension inside the wheel; `GeoSpecEngine` owns it.

## What to import [#what-to-import]

**TypeScript**

| Import                     | Gives                                                                                     |
| -------------------------- | ----------------------------------------------------------------------------------------- |
| `geospec`                  | `expectGeo` and the expectation types; `describe` and `it` for spec files                 |
| `geospec/model`            | `createModelLoader`, which binds `loadModel` to an engine                                 |
| `geospec/assertion-client` | `createGeoSpecAssertionClient` with `query`, and `GeoSpecAssertionError`; 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                                                           |

**Python**

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 [#node-browsers-and-bundlers]

**TypeScript**

`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:

```js title="next.config.js"
export default { serverExternalPackages: ['geospec-engine'] };
```

```js title="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](https://www.geospec.xyz/docs/guides/run-in-the-browser.mdx) shows.

**Python**

Nothing to bundle: the extension loads from the wheel.

## Installing for another platform [#installing-for-another-platform]

**TypeScript**

An image for another platform needs that platform's package. pnpm and Yarn Berry accept the set to install:

```yaml title="pnpm-workspace.yaml"
supportedArchitectures:
  os: [linux, darwin]
  cpu: [x64, arm64]
  libc: [glibc]
```

**Python**

pip picks the wheel for the interpreter and platform it runs on.

## Check the install [#check-the-install]

**TypeScript**

Save the script next to [the plate](/demo/plate-with-through-bore.step), 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](https://www.geospec.xyz/docs.mdx) runs the same claim over WASM.

```ts title="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();
```

**Python**

Save the script next to [the plate](/demo/plate-with-through-bore.step), a 40 × 40 × 20 mm STEP part, and run `python check.py`.

```python title="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 [#expected-output]

```console
passed
```

## Check the package [#check-the-package]

**TypeScript**

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

```bash
npm audit signatures
```

**Python**

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