@harpern/edu-modelhub-extractor
v0.1.0-beta.1
Published
Zero-dependency TypeScript library for reading and writing EDU_modelhub GLB extension metadata
Maintainers
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_modelhubshape.
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-extractorThe 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@betaWhen 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, andwriteGlbthrowGlbParseErroron 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 whateverparseGlbreturns, and wrong-typed fields becomeerrorfindings.parseGlbstrips__proto__,constructor, andprototypekeys from the extracted payload, so the result is safe to deep-merge into other objects.validate()reports such keys as errors. Sidecar.modelhub.jsonfiles are parsed by your code rather than this library, so run the exportedstripDangerousKeys()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'stripHtmlstep 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.
