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

@harpern/edu-modelhub-extractor

v0.1.0-beta.1

Published

Zero-dependency TypeScript library for reading and writing EDU_modelhub GLB extension metadata

Readme

@osuecampus/edu-modelhub-extractor

Zero-dependency TypeScript library for reading, writing, and validating the EDU_modelhub GLB extension: educational metadata (alt text, regions, annotations, licensing) embedded directly in glTF binary (.glb) files.

  • Zero runtime dependencies — pure TypeScript over DataView, works in Node.js and browsers.
  • ESM and CJS builds, with bundled type declarations.
  • Adapters for mapping external metadata (sidecar JSON, IIIF manifests, Schema.org, Sketchfab API) into the EDU_modelhub shape.

Install

The official package is not yet on npm. Publishing under the @osuecampus scope is waiting on npm org access and is the next step. Once it lands:

npm install @osuecampus/edu-modelhub-extractor
# or
yarn add @osuecampus/edu-modelhub-extractor

The first release line ships as a prerelease (0.1.0-beta.0, dist-tag beta). Note that npm marks a package's very first published version as latest regardless of the tag, so a plain install will resolve to the beta until a stable 0.1.0 follows.

Until then, the beta is available under the maintainer's personal scope. It is this exact build with the same API:

npm install @harpern/edu-modelhub-extractor@beta
# or
yarn add @harpern/edu-modelhub-extractor@beta

When the official package appears, change the package name in your install and import lines; nothing else changes. The @harpern package will be marked deprecated at that point with a message pointing at the new name, so your package manager will tell you when to switch.

Usage

Read metadata from a GLB

import { parseGlb } from "@osuecampus/edu-modelhub-extractor";
import { readFile } from "node:fs/promises";

const bytes = await readFile("model.glb");
const metadata = parseGlb(bytes); // EduModelHub | null
if (metadata) {
  console.log(metadata.model.title, metadata.annotations.length);
}

parseGlbJson(bytes) is also exported if you need the full glTF root JSON object rather than just the extension payload. Both throw GlbParseError on malformed GLB input.

Write metadata into a GLB

import { parseGlb, writeGlb } from "@osuecampus/edu-modelhub-extractor";
import { readFile } from "node:fs/promises";

const bytes = await readFile("model.glb");
const metadata = parseGlb(bytes);
if (metadata) {
  metadata.model.altText = "A labeled anatomical model of a human heart.";
  const updated = writeGlb(bytes, metadata); // new ArrayBuffer; input untouched
}

writeGlb injects or replaces the EDU_modelhub extension and returns a new ArrayBuffer. The BIN chunk (geometry, textures) is preserved verbatim.

Validate a payload

import { parseGlb, validate } from "@osuecampus/edu-modelhub-extractor";
import { readFile } from "node:fs/promises";

const metadata = parseGlb(await readFile("model.glb"));
if (metadata) {
  const findings = validate(metadata); // ValidationResult[]
  const errors = findings.filter((f) => f.severity === "error");
  if (errors.length === 0) {
    // payload conforms to the EDU_modelhub spec
  }
}

An empty array means the payload is valid; findings carry a field dot-path, a message, and a severity of "error" or "warning".

Adapt external metadata

Each adapter maps a third-party metadata format to an EduModelHub payload:

import {
  adaptSidecar,   // sidecar JSON files
  adaptIiif,      // IIIF Presentation manifests
  adaptSchemaOrg, // Schema.org 3DModel JSON-LD
  adaptSketchfab, // Sketchfab model API responses
} from "@osuecampus/edu-modelhub-extractor";

const response = await fetch("https://api.sketchfab.com/v3/models/MODEL_UID");
const metadata = adaptSketchfab(await response.json());

Adapters throw AdaptError when required fields cannot be mapped. Object-style adapters (sidecarAdapter, iiifAdapter, schemaOrgAdapter, sketchfabAdapter) implementing the MetadataAdapter interface are also exported.

Handling untrusted input

GLB files and external metadata often come from third parties: user uploads, harvested collections, public APIs. The library is built to be safe at that boundary:

  • parseGlb, parseGlbJson, and writeGlb throw GlbParseError on any malformed GLB, including JSON chunks whose root is not an object.
  • validate() never throws. Any JSON-representable value produces findings instead of exceptions, so it is safe to run directly on whatever parseGlb returns, and wrong-typed fields become error findings.
  • parseGlb strips __proto__, constructor, and prototype keys from the extracted payload, so the result is safe to deep-merge into other objects. validate() reports such keys as errors. Sidecar .modelhub.json files are parsed by your code rather than this library, so run the exported stripDangerousKeys() on them before merging them into anything.

Two things remain the consumer's responsibility:

  • Escape on render. Every string field in a payload (title, altText, body, attribution, region labels, tags) is untrusted text. The adapters' stripHtml step removes tags for readability only; it is not an HTML sanitizer and its output is not safe to insert as HTML. Render these fields as text nodes, or escape them with your framework's default escaping.
  • Limit input size. The library only reads the bytes you hand it, but a hostile file can still be arbitrarily large. Enforce upload size limits before parsing.

Types

All spec types are exported: EduModelHub, ModelMetadata, Region, Annotation, Anchor, ModelLicense, ModelSource, and more. See the bundled .d.ts for the full surface.

About EDU_modelhub

EDU_modelhub is a glTF extension developed by Oregon State University Ecampus for carrying educational metadata inside 3D models. The reference viewers on the project site are built on this library, and the Blender authoring add-ons write the metadata it reads.

License

Apache-2.0