npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

meshstep

v0.1.1

Published

STEP -> watertight process-grade mesh importer (analytic + NURBS surfaces, assemblies). Pure TypeScript, zero runtime dependencies, no WASM.

Readme

meshStep

https://cnckitchen.github.io/meshStep/


Pure-TypeScript STEP → mesh importer. Reads ISO-10303-21 B-rep geometry (AP203 / AP214 / AP242) and produces watertight, low-sliver, process-grade triangle meshes — meshes you can displace, voxelize, offset, slice, or simulate, not just look at.

Try it live: cnckitchen.github.io/meshStep — the web viewer runs the importer entirely in your browser (files never leave your machine).

Zero runtime dependencies: no WASM, no native code, no build step. The whole library is ~11,000 lines of TypeScript (~182 KB minified, ~68 KB gzipped) that run as-is in the browser, in a Web Worker, and in Node ≥ 22.18. Built to feed bumpmesh (displacement texturing) and infeall (smart infill), where mesh defects aren't cosmetic — they break the algorithm.

Install

npm install meshstep

npm

Plain ESM with TypeScript types included, zero dependencies. Works in Node ≥ 22.18 and in any modern browser via a bundler — the import is synchronous and CPU-bound, so in a UI run it inside a Web Worker (wire up onProgress and signal, as the web viewer does).

import { importStep } from "meshstep";
const result = importStep(stepFileText); // positions always in mm — see Quick start below

Why another STEP importer?

Because every existing route from "STEP file" to "triangles" optimizes for rendering, and rendering tolerates broken meshes.

Whatever converts your STEP file today — a CAD program's STL export, an OpenCASCADE-based converter, an online tool — almost certainly tessellates each B-rep face independently, with curvature-adaptive spacing tuned for screen-space smoothness. That looks right in a viewer, but structurally it's a per-face triangle soup: duplicated vertices along every face border, hairline cracks between faces, extreme sliver triangles on cylinders and fillets, and occasionally faces missing outright (the Better STEP dataset paper measured OpenCASCADE's mesher leaving faces unmeshed in ~1.5% of ABC-dataset models and ~9% of their assembly set). Algorithms that consume the mesh — displacement mapping, winding-number voxelization, Boolean/offset operations, slicing, FEM — choke on exactly those defects.

meshStep starts from the opposite end: the mesh is the product.

  • Watertight per body, by construction. Shared B-rep edges are sampled once and welded, so every body imports as a closed, consistently outward-oriented 2-manifold — verified as 0 open edges / 0 non-manifold edges across the curated test corpus (see Status for honest numbers on wilder corpora). Multi-body parts and assemblies stay separate welded components.
  • No seam twist, no sliver storm. Every face is triangulated in its natural (u,v) parameter space with a robust constrained-Delaunay pass; periodic faces (cylinder/cone/sphere/torus seams) are unwrapped flat first, so the spiral "twist" that plagues generic B-rep tessellators cannot occur. Fillets get ruling-aligned anisotropy and normal-smoothing diagonal flips for clean shading.
  • CAD topology survives into the mesh. faceOfTri / solidOfTri map every triangle back to its STEP face and body, and feature edges are exact from the B-rep EDGE_CURVE set — a cube stays a cube. Downstream tools can segment and mask by real CAD faces instead of re-detecting features from a triangle soup. STEP presentation colors come along too: colors resolves the STYLED_ITEM chains into a palette plus per-face / per-body indices, so a viewer can render the CAD colors and tools can group surfaces by shared color. And structure exposes the product tree (part names, assembly hierarchy, occurrence counts) keyed to the same body ids, so per-part selection and hiding work straight off the mesh. faces adds per-face metadata — normalized surface class (plane/cylinder/sphere/…), analytic origin/axis/radius, mesh area and mean normal — so "select this whole CAD face" or "mask every planar face" are one lookup, no dihedral-angle heuristics.
  • Analytic attributes on demand. vertexNormals: true evaluates exact per-vertex normals from the B-rep surfaces (a coarse cylinder still displaces/shades with perfectly radial normals — no faceting bands), and parameterUVs: true exports each face's own parameter-space (u,v) per triangle corner, so textures can wrap a periodic surface seamlessly in its native parameterization.
  • The true surface stays available. The optional isotropic remesh (remesh: true) splits/collapses/flips/smooths toward uniform, near-equilateral triangles and reprojects every vertex onto the exact analytic/NURBS surface (feature edges frozen) — quality passes converge to the CAD geometry, not to a frozen first tessellation.
  • Cross-validated against OpenCASCADE, not just eyeballed. The gapcheck harness converts every corpus model with both meshStep and OCCT and compares global + per-face deviation: NIST test models, real-world Printables downloads, and a ~1,800-part Voron-assembly sweep.

How it compares

| Route | In browser | Footprint | Mesh output | Watertight welded | Face→tri topology | License | |---|---|---|---|---|---|---| | meshStep | ✅ pure TS | ~68 KB gzip | uniform, low-sliver, seam-safe | ✅ per welded body | ✅ | AGPL-3.0 (commercial) | | occt-import-js | ✅ WASM | ~8 MB WASM | curvature-adaptive, per face | ❌ | ✅ face ranges | LGPL-2.1 (OCCT) | | opencascade.js | ✅ WASM | larger (custom builds) | same OCCT mesher | ❌ | manual | LGPL-2.1 (OCCT) | | cascadio / pythonOCC / FreeCAD | ❌ Python/desktop | native wheels/app | same OCCT mesher | ❌ | varies | LGPL (bundles OCCT) | | gmsh | ❌ desktop/Python | native | FE-quality, isotropic | ✅ (when it succeeds) | ⚠️ | GPL-2.0+ | | truck / foxtrot (Rust) | ✅ via WASM | MB-scale WASM | generic; twist on periodic faces | ⚠️ | ❌ | Apache-2.0 / MIT | | STL export from your CAD app | ❌ manual step | — | curvature-adaptive, sliver-heavy | usually | ❌ | proprietary app | | Commercial SDKs (CAD Exchanger, HOOPS) | server/native | licensed | good | ✅ | ✅ | proprietary, per-seat/app |

License fine print: the OCCT-based routes statically bundle an LGPL-2.1 kernel — workable, but with linking/relinking obligations that get murky in WASM; gmsh's GPL rules out closed-source embedding entirely. meshStep is AGPL-3.0 for open-source use, and because the copyright is held by a single owner with zero third-party code bundled, a clean commercial exception for closed-source products is available directly — see COMMERCIAL.md.

Honest notes on the alternatives:

  • occt-import-js is excellent for what it's built for — viewing (it powers Online 3D Viewer), and it reads IGES and BREP too. Its output is three.js-style geometry tessellated per face by OCCT's curvature-adaptive BRepMesh; welding, watertightness, and triangle quality are simply not its goals. meshStep uses it as the reference implementation in its validation harness.
  • opencascade.js gives you the full OCCT kernel in the browser — booleans, filleting, sewing, everything — at the cost of a large custom WASM build and its toolchain. If you need a modeling kernel in JS, that's the tool; if you only need STEP → clean mesh, it's a lot of freight.
  • gmsh is the serious open-source option for quality meshes from STEP — but it's a desktop/Python FEA tool, imports geometry through OCCT (so inherits its failure modes), and doesn't run in a browser tab.
  • truck is a real Rust B-rep kernel (it powers Loft's browser STEP viewer), and infeall's own importer builds on it. meshStep exists partly because truck's generic robust_triangulation can twist trimmed periodic faces — on a cylinder-with-cutout test, 13% of edges spiralled by up to 177°. foxtrot is a fast but abandoned proof-of-concept with incomplete NURBS support.
  • Commercial SDKs (CAD Exchanger, HOOPS Exchange) are robust, fast, and read every format — as native/server-side libraries with per-seat/per-app licensing.

When meshStep is the wrong tool: you need formats beyond STEP (IGES, JT, Parasolid), PMI / metadata, a modeling kernel (booleans, sewing, healing of dirty geometry), or maximum robustness against pathological enterprise CAD exports. Use an OCCT-based stack or a commercial SDK for those.

Status

Coverage:

  • Surfaces: plane, cylinder, cone, sphere, torus (incl. degenerate), B-spline / NURBS (rational and complex-form), surface of linear extrusion, surface of revolution, offset surfaces.
  • Curves: lines, circles, ellipses, B-spline curves (incl. intersection edges of drilled holes).
  • Structure: multi-body parts, assemblies with full product-structure instancing, per-representation units, BREP_WITH_VOIDS; AP242 tessellated-geometry bodies pass through as-is. Face/body presentation colors (STYLED_ITEM / OVER_RIDING_STYLED_ITEM, AP214/AP242) are extracted palette-indexed.
  • Mesh I/O: binary STL out (writeBinarySTL), plus standalone zero-dependency readers so tools can ingest existing meshes through the same library — STL in (readSTL, binary + ASCII, with soup→indexed welding) and 3MF in (read3MF: ZIP + XML parsing, component/build transforms, per-triangle colors, object types).

Robustness is tracked over four corpora of increasing wildness, cross-validated against OpenCASCADE output with the gapcheck harness (per-face deviation + watertightness). Where it stands (July 2026):

| Corpus | Result | |---|---| | 13 curated repo models (analytic + NURBS + assemblies) | all watertight (0 open, 0 non-manifold), <2% slivers, within ~0.5% of reference | | 81-model characterization corpus (real Printables downloads + NIST AP242 test cases) | 70 fully clean · 9 with localized open-edge leaks (mostly NIST models) · 2 import errors | | Voron-family assemblies, 1,820 parts | 1,793 OK · 26 WARN · 1 FAIL vs OCC cross-check at tight tolerance (the FAIL is an artifact of the OCC reference, not the mesh) | | ABC dataset chunk 0000, 10,000 wild CAD files | 9,719 watertight (97.2%) — closed, manifold, every face meshed. Remainder: 146 seam leaks · 68 timeouts · 41 untriangulated faces · 26 other |

Current limitations

meshStep is not (yet) an industrial-strength importer — it's tuned for the kind of parts people actually 3D-print, and the numbers above are deliberately honest about the rest:

  • Wild CAD still has a tail. On the ABC research corpus (uncurated files from many CAD systems, full of degenerate, microscopic, and exotic geometry) ~3% of models come out with defects: multi-turn thread/spiral faces the seam machinery can't yet cut, residual CDT failures on degenerate trims, or non-manifold spots. The tail shrinks with every release, but OpenCASCADE-based tools will still read more of the truly pathological files — they just hand back per-face triangle soup, not a watertight mesh.
  • Pathological models can be slow. Pure TypeScript is fast enough for interactive use on print-scale parts, but 0.7% of ABC models (68 of 10,000) blew a 120 s budget at tight tolerances — native OCCT is faster on huge or degenerate inputs.
  • No geometry healing. meshStep trusts the STEP file: gaps, self-intersections, or broken topology in the source B-rep are not repaired, only reported.
  • Remaining features: 3MF export (a 3MF reader is in), the Low/Med/High refinement-preset UI, and seam routing for multi-turn thread/spiral faces. Pipeline details in DESIGN.md.

Quick start (dev)

Requires Node ≥ 22.18 (native TypeScript type-stripping — no build step, no dependencies).

node test/convert.ts        # import every test STEP, export STL to out/, report quality vs reference
node test/check-all.ts      # watertightness check over the repo test models
import { importStep, writeBinarySTL } from "./src/index.ts";
const result = importStep(stepText, { surfaceDeviation: 0.01, maxEdge: 1.0 });
// result.mesh (positions/indices), result.faceOfTri, result.solidOfTri
if (!result.diagnostics.ok) console.warn(result.diagnostics); // see "Import diagnostics"
writeFileSync("out.stl", writeBinarySTL(result.mesh));

// STEP colors (null if the file has none): palette + palette index per B-rep face/body.
// Triangle t's sRGB color, and a ready-made grouping key for surfaces:
if (result.colors) {
  const { palette, faceColor } = result.colors;
  const rgb = palette[faceColor.get(result.faceOfTri[t])]; // undefined index = unstyled face
}

// Part/component tree (STEP product structure): names, nesting, occurrence counts.
// node.bodies[].id keys into result.solidOfTri — filter triangles on it to hide/select a part.
const walk = (node, depth = 0) => {
  console.log("  ".repeat(depth) + `${node.name} ×${node.occurrences} (${node.bodies.length} bodies)`);
  node.children.forEach((c) => walk(c, depth + 1));
};
walk(result.structure);

// Per-face metadata: surface class + analytic identity + area/normal, keyed by faceOfTri ids.
const info = result.faces.get(result.faceOfTri[t]);
// info.type: "plane" | "cylinder" | "cone" | "sphere" | "torus" | "bspline" | ...
// info.surface: { kind, origin?, axis?, radius?, semiAngle? }  (part-local, mm / radians)
// info.area (mm²), info.meanNormal (unit, outward), info.triangleCount

Analytic attributes (both opt-in, computed by projecting the finished mesh back onto the exact B-rep surfaces — repair-fill triangles that lie off-surface get honest fallbacks):

const r = importStep(stepText, { vertexNormals: true, parameterUVs: true });
r.normals; // Float32Array, unit xyz per vertex — analytic on curved faces, crease-averaged at edges
r.uv;      // Float32Array, (u,v) per triangle CORNER (welded vertices have one (u,v) per face;
           // NaN where no analytic surface exists). Corners are seam-unwrapped per triangle.
r.faceUV;  // Map<faceId, { uRange, vRange, uPeriod?, vPeriod? }> for normalizing into texture space

Options mirror Fusion's mesh-export dialog: surfaceDeviation (mm), normalDeviation (deg), maxEdge (mm), plus remesh: true for uniform-isotropic output (default off — the raw tessellation is watertight and shades cleaner). onProgress reports parse / tessellate / finalize progress for long imports; signal (an AbortSignal) cancels a running import at the next work-unit boundary — in a worker UI, abort first and keep terminate() as the hard stop. measureGeometry: true additionally collects exact per-edge curve identity (circle centers/radii/axes, boundary polylines coincident with the mesh) into result.measure, so a viewer can offer CAD-style measuring on the tessellation.

Units: mesh.positions — and every derived length (areas, radii, measure geometry) — are always millimetres, whatever length unit the STEP file declares (inch, metre, mixed-unit assemblies with per-part contexts included). result.units is the detected label ("mm", "in", …) for display only; nothing downstream needs to rescale. Locked by test/units.ts.

Identity & versioning — the stability contract

Applications that persist references into an imported model (face selections, edge measurements, cached meshes) can rely on the following:

  • Entity ids come from the STEP file, not from meshStep. faceOfTri / solidOfTri values, MeasureEdge.edgeId, and every id-keyed map (faces, colors, structure bodies) use the STEP file's own entity record numbers (the #123 of the ADVANCED_FACE, EDGE_CURVE, or solid record). For byte-identical input they are stable across meshStep versions by construction — persisted selections survive a meshStep upgrade. Repair passes (T-junction zips, micro-hole fills) tag their triangles with an adjacent real face id; meshStep never fabricates ids. (One exception: the AP203 CURVE_BOUNDED_SURFACE sheet-model fallback synthesizes body id 0.)
  • The triangulation itself is NOT stable across releases. Meshing improvements change vertex positions, counts, and triangle layout in nearly every version (bit-for-bit stability is enforced only within a version, by the characterization suite). Never persist triangle or vertex indices — persist entity ids (plus SolidInstance.instance for a placed occurrence) and re-resolve after re-import. Pin an exact meshstep version, and record the exported VERSION constant alongside any cached mesh so it can be invalidated on upgrade.
  • Ids are stable per file, not per design. Re-exporting the same part from CAD renumbers every entity; hash the STEP file to detect that and re-bind selections.
  • Face coverage can grow between versions. A face that failed to tessellate in an older version may succeed in a newer one (and diagnostics improve accordingly) — ids are never renumbered, but tolerate ids appearing in or dropping out of the meshed set.

The tolerances are absolute, so one default can't fit both a 5 mm clip and a 3 m assembly. estimateStepSize(src) measures the model without tessellating (parse + point scan, sub-second even on large assemblies) and autoTessellation(diagMm) turns that into size-adaptive defaults — anchored so a ~100 mm part gets the standard 0.01 mm / 1 mm:

import { estimateStepSize, autoTessellation } from "meshstep";
const est = estimateStepSize(stepText); // { bbox, diag, units } | null
const opts = est ? autoTessellation(est.diag) : {}; // { surfaceDeviation, maxEdge }
const result = importStep(stepText, opts);

Import diagnostics

Every import returns a diagnostics verdict so an application can tell a trustworthy conversion from a suspect one — and tell the user when they'd be better served exporting a mesh directly from their CAD package:

const { diagnostics: d } = importStep(stepText);
if (!d.ok) {
  const broken = d.openEdges > 0 || d.nonManifoldEdges > 0
    || d.warnings.some((w) => w.severity === "error");
  showBanner(broken
    ? "This STEP file imported with defects (missing or leaking geometry). " +
      "Consider exporting an STL/3MF directly from your CAD software instead."
    : "Some faces needed heuristic repairs — please inspect the result.");
}
  • ok — strict: true only when the mesh is closed and manifold, no faces were dropped or skipped, and no heuristic repair paths fired.
  • openEdges / nonManifoldEdges — final edge-defect audit of the returned mesh (open-by-design surface bodies from openSolids excluded). Non-zero means cracks/holes or bad welds.
  • facesDropped / facesSkipped — geometry that is missing: malformed face records dropped while reading the B-rep, and faces the tessellator could not mesh.
  • warnings — structured findings with code, severity, the STEP faceId, and a human-readable detail. Severity "error" means geometry is missing; "warning" means a rescue path (earcut hole-bridge fill, degenerate-boundary CDT rescue, fold surgery, self-intersecting trim loops) reconstructed a region heuristically — the result is usually fine, but it was not derived from clean topology.

meshDefects(mesh, solidOfTri?, openSolids?) is exported separately for re-auditing a mesh after downstream processing.

Web verification studio

A browser UI for visually checking output lives in web/ — hosted at cnckitchen.github.io/meshStep: upload a STEP (or an existing STL/3MF — same toolset, no conversion), tune the settings (auto-scaled to the model's size), and inspect the result: the model's STEP/3MF colors (on by default, toggleable), a parts tree with per-part show/hide, shaded / transparent / wireframe / edges render styles, a section view with a draggable clipping plane, CAD-style measuring (exact circle centers/radii from the B-rep), deviation coloring against a reference STL (3D-scan style), and open-edge highlighting. cd web && npm install && npm run dev.

License

AGPL-3.0-only. Copyright held by CNC Kitchen; commercial licenses for closed-source use are available — see COMMERCIAL.md. The library ships with zero third-party code, so the whole artifact can be licensed directly.

Contributions are welcome under the CLA and dependency-license policy in CONTRIBUTING.md (enforced in CI).