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

@ifc-lite/server-client

v5.1.0

Published

TypeScript client SDK for IFC-Lite Server

Readme

@ifc-lite/server-client

TypeScript SDK for the IFClite server. Handles content-addressable caching (skip the upload if the server already has it), streaming via SSE, and Parquet/Arrow response decoding.

Installation

npm install @ifc-lite/server-client

Parse with caching (one-shot)

import { IfcServerClient } from '@ifc-lite/server-client';

const client = new IfcServerClient({ baseUrl: 'https://your-server.com' });

const result = await client.parseParquet(file);

// Hashes the file client-side first, sends only the hash. If the server
// has it cached, the upload is skipped entirely — second loads are instant.
console.log(`${result.metadata.entity_count} entities, ${result.meshes.length} meshes`);

Stream a large file

import type { MeshData as ServerMeshData } from '@ifc-lite/server-client';
import type { MeshData } from '@ifc-lite/geometry';

// Server meshes are the snake_case wire shape. Converting them is a real seam,
// not a rename: `origin` and `geometryClass` must be OMITTED rather than
// stamped when absent or zero, so a world-baked mesh decodes identically over
// every transport, and the two disjoint source ids are tested against
// `undefined` rather than for truthiness. The viewer keeps the tested copy at
// apps/viewer/src/utils/serverMesh.ts (issue #1841, #3199); keep yours in step.
function convertServerMesh(m: ServerMeshData): MeshData {
  return {
    expressId: m.express_id,
    ifcType: m.ifc_type,
    positions: new Float32Array(m.positions),
    normals: m.normals ? new Float32Array(m.normals) : new Float32Array(0),
    indices: new Uint32Array(m.indices),
    color: m.color,
    ...(m.origin?.some((v) => v !== 0) ? { origin: m.origin } : {}),
    ...(m.geometry_class ? { geometryClass: m.geometry_class } : {}),
    ...(m.geometry_item_id !== undefined ? { geometryItemId: m.geometry_item_id } : {}),
    ...(m.material_id !== undefined ? { materialId: m.material_id } : {}),
  };
}

for await (const event of client.parseStream(file)) {
  switch (event.type) {
    case 'progress':
      console.log(`${event.processed}/${event.total}`);
      break;
    case 'batch':
      renderer.addMeshes(
        event.meshes.map(convertServerMesh),
        true, // streaming: throttle batch rebuilds — first triangles ~300ms in
      );
      break;
    case 'complete':
      console.log(`Done: ${event.stats.total_meshes} meshes`);
      break;
    case 'error':
      console.error(event.message);
      break;
  }
}

Health check + server info

try {
  const info = await client.health();
  console.log(`Server v${info.version} (${info.status})`);
} catch {
  console.warn('Server unreachable, falling back to client-side parse');
}

Lower-level: Parquet decoders

If you're consuming server responses outside the SDK (e.g. from a worker, or another runtime), the same Parquet/Arrow decoders are exposed directly:

import { decodeParquetGeometry } from '@ifc-lite/server-client';

const meshes = await decodeParquetGeometry(arrayBuffer);

The decoded data model's materials rows identify each IfcRelAssociatesMaterial through association_id and its RelatingMaterial through definition_id. kind uses the exact IFC definition name (IfcMaterial, IfcMaterialLayerSet, IfcMaterialProfileSet, IfcMaterialConstituentSet, or IfcMaterialList). Member names and categories are separate from the referenced IfcMaterial name and category; material_id also identifies an unnamed list member. material_name_present distinguishes a missing IfcMaterial.Name from an authored empty string, preserving the source parser's list-member fallback. member_count lets consumers reject incomplete association groups. These fields are absent in older server payloads. A client validating material values should treat such partial rows as unresolved rather than as proof of a mismatch.

For IfcMaterialLayerSetUsage and IfcMaterialProfileSetUsage, definition_id is the usage entity from RelatingMaterial, while kind reports the effective referenced set.

Which frame the meshes are in

The one-shot parse responses and the Parquet metadata headers carry mesh_coordinate_space, the frame the server baked the vertices into (parseParquetStream's events do not carry it):

| Value | What was subtracted | |---|---| | site_local | the IfcSite placement's translation, and its rotation removed | | model_rtc | a detected model-level anchor; no rotation removed | | raw_ifc | nothing; vertices are in raw IFC world space |

It is typed MeshCoordinateSpace | undefined, so a switch over the three is exhaustive. Absent means the server did not say - either it predates the tag, or it sent a value outside the three, which this client drops (with a console warning) rather than passing off as a tier. Handle absence; do not treat it as raw_ifc.

import {
  asMeshCoordinateSpace,
  IfcServerClient,
  MESH_COORDINATE_SPACES,
  withNarrowedCoordinateSpace,
  type MeshCoordinateSpace,
} from '@ifc-lite/server-client';

const client = new IfcServerClient({ baseUrl: 'https://your-server.com' });
const result = await client.parseParquet(file);

switch (result.mesh_coordinate_space) {
  case 'site_local': /* result.site_transform puts it back in world space */ break;
  case 'model_rtc':  /* metadata.coordinate_info.origin_shift is the anchor */ break;
  case 'raw_ifc':    /* already world space */ break;
  case undefined:    /* the server did not declare one */ break;
}

// Going the other way, from a string you got from somewhere else:
const fromElsewhere: unknown = 'site_local';
const space: MeshCoordinateSpace | undefined = asMeshCoordinateSpace(fromElsewhere);

// And for a wire object you parsed yourself, e.g. a cached JSON response.
// It MUTATES the object and hands it back with the tag retyped:
const checked = withNarrowedCoordinateSpace<{ mesh_coordinate_space?: unknown }>(
  JSON.parse('{"mesh_coordinate_space":"raw_ifc"}')
);

// readonly ['site_local', 'model_rtc', 'raw_ifc']
console.log(space, checked.mesh_coordinate_space, MESH_COORDINATE_SPACES);

API

See the Server Guide and API Reference.

License

MPL-2.0