# Quick start

Install geospec, claim the size of a STEP part and read passed, failed or refused in TypeScript or Python.

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

Install, claim the size of one STEP part and read the verdict: in the page, then as a script.

**TypeScript**

The API is the `geospec` package, published on the `beta` tag, over the `geospec-engine` package. Node 24 or later.

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

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

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

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

**Python**

One wheel installs the `geospec` package and the engine. 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>

[Install](https://www.geospec.xyz/docs/install.mdx) covers what each package delivers, browsers and bundlers.

## Claim the size of a plate [#claim-the-size-of-a-plate]

The plate is a 40 × 40 × 20 mm STEP part with a 10 mm bore. `toHaveBoundingBox` compares the measured bounds with the size you state, at a tolerance of 0.02 mm. Change `z: 20` to `z: 25` and the claim fails with the measured size in its diagnostic.

**Python**

Python spells each matcher in snake case: `to_have_bounding_box`.

```ts
expectGeo(part)
  .toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
```

## Run it as a script [#run-it-as-a-script]

**TypeScript**

The same claim over the WASM build, which runs on every Node host. Save the script next to [the plate](/demo/plate-with-through-bore.step) and run `node check.ts`.

```ts title="check.ts"
import { readFile } from 'node:fs/promises';
import { expectGeo } from 'geospec';
import { createModelLoader } from 'geospec/model';
import { Engine, initialize } from 'geospec-engine';

await initialize();
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) 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
```

**TypeScript**

A matcher returns an assertion with `passed`, `report.status` and `report.result.evidence.measured`, here `size: [40, 40, 20]`. A failed or refused claim throws `GeoSpecAssertionError` from `geospec/assertion-client`: its message is the diagnostic and `diagnostics[0].code` the code.

**Python**

`expect_geo` returns a report with `passed`, `result["status"]` and `result["evidence"]["measured"]`, here `size: [40, 40, 20]`. A failed or refused claim raises `GeoSpecAssertionError`: its message is the diagnostic and `.code` the code.

A missed size reads `GEOSPEC_BOUNDING_BOX_MISMATCH`; a refusal names the evidence the subject lacks. [Read a refusal](https://www.geospec.xyz/docs/guides/read-a-refusal.mdx) tells the two apart.

## What it does not do [#what-it-does-not-do]

* **Meshes carry no feature evidence.** A GLB or glTF subject refuses hole, face, chamfer and fillet claims with `GEOSPEC_UNSUPPORTED_EVIDENCE`; load a STEP export, as [Find holes and features](https://www.geospec.xyz/docs/guides/find-holes-and-features.mdx) does.
* **No unit or frame inference.** A STEP file is read in its own unit. A GLB is metres unless the load says millimetres (`sourceUnit: 'mm'`, in Python `source_unit="mm"`). Measurements are millimetres. [How it works](https://www.geospec.xyz/docs/how-it-works.mdx) has what each format carries.
* **No rendering or repair.** The engine measures the geometry it is given and changes nothing.