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/cache

v3.7.2

Published

Binary cache format for IFC-Lite - fast model loading

Readme

@ifc-lite/cache

Binary cache format for IFClite. Caches the parsed data store and geometry in a compact binary format so a previously-loaded IFC reopens in milliseconds instead of re-running the full parse + tessellation pipeline. Content-addressable (xxHash64 of the source IFC), so cache invalidation is automatic.

Properties and quantities are not part of that speedup. BinaryCacheWriter.write serializes whatever the data store's property/quantity tables already hold. A STEP-parsed store resolves properties lazily on demand and never populates those tables, so a cache written straight from a STEP parse round-trips with EMPTY property/quantity tables — a cache-restored model queries properties exactly as slow as a fresh parse (see docs/guide/querying.md). If your application needs fast repeat property queries too, retain the source buffer alongside the cache entry and re-attach on-demand extraction on read, the way the viewer's cache hook does.

Geometry cache format v24 also preserves CoordinateInfo.boundsRecoveryFallbackCount and CoordinateInfo.lengthUnitScale, including defined zero values. An absent field remains unknown; reading a pre-v24 cache does not infer these values. Versioned cache keys invalidate older entries once so fresh loads can retain the complete coordinate metadata.

Format v25 keeps each mesh's optional shadingColor, pre-placement localBounds and localToWorld placement matrix across a cache hit. Absent fields stay absent; a pre-v25 entry reads them as unknown and is rebuilt once through the versioned cache key.

Installation

npm install @ifc-lite/cache

Skip the parse on warm load

import {
  xxhash64Hex,
  BinaryCacheReader,
  BinaryCacheWriter,
  toCacheDataStore,
} from '@ifc-lite/cache';
import { IfcParser } from '@ifc-lite/parser';
import { GeometryProcessor } from '@ifc-lite/geometry';

// Your own persistence layer: IndexedDB, fs, S3, ...
declare const myStorage: {
  get(key: string): Promise<ArrayBuffer | undefined>;
  put(key: string, value: ArrayBuffer): Promise<void>;
};

async function loadWithCache(file: File) {
  const ifcBuffer = await file.arrayBuffer();
  const cacheKey = xxhash64Hex(ifcBuffer);

  // Try cache first
  const cached = await myStorage.get(cacheKey);
  if (cached) {
    const reader = new BinaryCacheReader();
    const { geometry } = await reader.read(cached);
    renderer.loadGeometry(geometry?.meshes ?? []);
    return; // first triangles in milliseconds
  }

  // Cold path — full parse + tessellation, then write the cache.
  const dataStore = await new IfcParser().parseColumnar(ifcBuffer);
  const geometry = await new GeometryProcessor().process(new Uint8Array(ifcBuffer));

  const writer = new BinaryCacheWriter();
  // toCacheDataStore adapts the parser's IfcDataStore (string `schemaVersion`)
  // to the CacheDataStore shape write() requires (numeric `schema` enum) —
  // see the note above about what it does and does not carry over.
  const cacheBuffer = await writer.write(toCacheDataStore(dataStore), geometry, ifcBuffer, {
    includeGeometry: true,
  });
  await myStorage.put(cacheKey, cacheBuffer);
}

Pure GLB read

If you already have a GLB blob (from a server, S3, etc.), skip the binary cache wrapper and load directly:

import { loadGLBToMeshData, parseGLB } from '@ifc-lite/cache';

declare const glbBytes: Uint8Array; // e.g. new Uint8Array(await res.arrayBuffer())

const glbMeshes = loadGLBToMeshData(glbBytes); // synchronous
// MeshData[] ready to feed into @ifc-lite/renderer

// Or get the parsed GLB structure if you need lower-level access
const { json, bin } = parseGLB(glbBytes);

Hashing utilities

Two hash functions are exposed for cache key generation:

import { xxhash64, xxhash64Hex } from '@ifc-lite/cache';

const hexKey = xxhash64Hex(buffer);  // ~5 GB/s, 16-char hex string
const rawKey = xxhash64(buffer);     // same hash as a bigint

The cache keys models by the xxHash64 of the source IFC, and reader.validate(cacheBuffer, sourceBuffer) uses the same hash to detect when the source has changed.

Format versioning

The binary layout is versioned via the exported FORMAT_VERSION constant. Readers accept entries written by the current or an older format version (with backward-compatible decoding, e.g. the per-mesh geometry-class byte added in v5) and reject entries written by a newer one, so mixed-version deployments fail safely toward a cold parse.

Version 19 preserves explicitly supplied canonical MeshData.appearanceSource provenance. Fresh canonical meshes store an identity marker; fragmented or expanded meshes reference a geometry-header source-index pool, shared across spatial chunks, plus their corner mapping. Restored metadata points to the restored mesh indices, preserving the topology identity fence required by appearance preview. Invalid identities, source lengths, pool references and corner ranges fail cache reading/writing instead of enabling an unsafe preview. Unmarked meshes remain unmarked; old records never acquire inferred canonical identity.

Version 20 appends the optional exact WASM RTC producer frame to CoordinateInfo, including an authoritative needsShift: false and a meaningful true frame at the zero origin. Direct reads of v13–v19 geometry remain supported with this provenance absent; it is never inferred from the older applied-only wasmRtcOffset field. Frame-present records strictly validate finite coordinates, the boolean decision, and offset consistency. Frame-absent records preserve legacy numeric offset values (including old non-finite values) for compatibility, without treating them as exact provenance.

The viewer includes FORMAT_VERSION in its cache key, so older entries reparse once and receive real provenance from the canonical geometry pipeline. Direct readers can still decode v13–v19 records for viewing, with metadata introduced by newer versions absent. When reading an older geometry header directly, pass its version as the second argument to readGeometryHeadV13(reader, version); it defaults to the current format. Keep the returned chunk metadata when calling decodeGeometryChunk, since it carries the shared source-index pool. This does not add UV/image caching: texture-carrying viewer models retain their existing cache bypass.

Source-hash contract (and omitSourceHash)

By default the header stores the full-file xxhash64 of the source in sourceHash, and reader.validate() / read({ sourceBuffer }) compare against it. Hashing a large source can be a multi-second main-thread cost, so a caller that validates the source another way (e.g. an application-layer content hash plus a file modified-time guard, as the viewer's source-decoupled cache tier does) can pass omitSourceHash: true:

import { BinaryCacheWriter, type CacheDataStore } from '@ifc-lite/cache';
import type { GeometryResult } from '@ifc-lite/geometry';

declare const cacheDataStore: CacheDataStore;
declare const geometry: GeometryResult;
declare const ifcBuffer: ArrayBuffer;

const writer = new BinaryCacheWriter();

// Skip the full-file hash; validate the source at the application layer instead.
const cacheBuffer = await writer.write(cacheDataStore, geometry, ifcBuffer, {
  includeGeometry: true,
  omitSourceHash: true,
});

Such an entry stores sourceHash = 0n and sets HeaderFlags.SourceHashUnset. Read it back via CacheHeaderInfo.hasSourceHash:

  • hasSourceHash === true → sourceHash is a real full-file hash; validate() and read({ sourceBuffer }) work as usual.
  • hasSourceHash === false → sourceHash is unset. read({ sourceBuffer }) skips header validation (it does not fail-close on a valid cache), and validate() throws a clear error instead of returning a misleading false. Validate the source yourself (e.g. compare a stored content hash / mtime).

This is backward compatible: entries written before the flag existed have it unset-as-in-absent, so their sourceHash remains a real hash and continues to validate normally.

API

See the API Reference.

License

MPL-2.0

Compact entity-index input

CacheEntityIndex.byId continues to accept an iterable of entity references. Column-aware producers may additionally implement getColumns(), returning expressIds, byteOffsets, byteLengths, typeIndices and typeStrings with the same rows as iteration. The parser's CompactEntityIndex supplies this method; the cache package does not require a parser runtime dependency.

Serialization borrows valid sorted columns synchronously, preserving stable duplicate order, normalized type names and first-row type-table order. It never mutates or detaches input backing. Unsupported column shapes use the iterable representation. Producers must keep borrowed columns stable during serialization and must not expose unrelated columns through this method. The existing binary format and generic iterable behavior are unchanged.

Browser compression worker

BinaryCacheWriter.write accepts compressGeometryChunksInWorker: true to run geometry chunk compression in one lazily created browser module worker. The viewer enables it to keep codec work off the UI thread. The default is false, preserving workerless SDK and Node use. The existing compression size floor and four-chunk concurrency limit remain in effect; disabling compressGeometryChunks creates no worker. Cache format, chunk order, compressed-versus-raw selection and readers are unchanged.

The browser application must bundle the package worker asset and permit module workers. Worker startup, codec and transport failures reject the cache write; there is no silent main-thread retry. Only newly serialized chunk buffers cross the worker boundary, and the worker terminates after geometry serialization succeeds or fails. Source/model data stay with the caller. This moves compression CPU rather than eliminating it; include cache completion when measuring cold loads.

Textured GLB capture import

parseGLBToMeshData and loadGLBToMeshData preserve base-colour TEXCOORD_0 coordinates (including normalized integer UVs and KHR_texture_transform), texture references, node TRS/matrices and mirrored winding. UV vertices remain separate across seams. Node translation stays in the double-precision origin. Use parseGLBImageResources(parsed.json, parsed.bin) to obtain original encoded PNG/JPEG images keyed by each mesh's textureRef.url. Decode these at the host boundary; this package never allocates browser image objects. Embedded PNG/JPEG magic bytes must match the declared MIME type before geometry or resources are returned; MIME labels cannot bypass the host opacity checks.

This is opaque captured-albedo import. Embedded base-colour PNG/JPEG images and repeat/clamp samplers are supported. External images/buffers, other UV sets, mirrored repeat, sparse accessors, compression, skinning/morphs, vertex colours, additional material maps, and textured MASK/BLEND produce explicit errors. The viewer also refuses transparent image pixels, retains original bytes with the model, and releases images on removal. It does not claim full PBR fidelity.