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

glyphtex-engine

v0.1.0

Published

Typed TypeScript bindings for the GlyphTeX TeX engine (Tectonic compiled to WebAssembly)

Readme

glyphtex-engine

LaTeX → PDF in the browser and on the server. Wraps Tectonic (XeTeX + xdvipdfmx) compiled to WebAssembly.

import { TexEngine } from 'glyphtex-engine';

const engine = await TexEngine.load(fetch('/tectonic.wasm'));

// The TeX bundle: document classes, packages, fonts, and the format file.
for (const [name, bytes] of bundle) engine.addFile(name, bytes);

engine.addFile('main.tex', String.raw`
  \documentclass{article}
  \begin{document}\section{Hello}\end{document}
`);

const result = engine.compile({ entry: 'main.tex', synctex: true });

if (result.status !== 'failed') {
  const pdf = engine.pdf();          // Uint8Array
}
for (const d of result.diagnostics) {
  console.log(`${d.severity} ${d.file ?? '?'}:${d.line ?? '?'} — ${d.message}`);
}

Status is not a boolean

compile() resolves for any run that completed. status distinguishes four outcomes, and only one of them means "no output":

| status | Meaning | | --- | --- | | spotless | No warnings or errors. | | warnings | Warnings only — the overwhelmingly common case for real documents. | | errors | TeX reported errors but recovered. A PDF usually still exists. | | failed | Nothing was produced. message explains why. |

Treating errors as fatal would reject documents that TeX itself considers publishable, so check status !== 'failed' before reading the PDF.

Missing files are data, not exceptions

When the engine asks for a file that is not in the virtual filesystem, it is reported in result.missingFiles rather than being guessed at or thrown:

const result = engine.compile({ entry: 'main.tex' });
if (result.missingFiles.length) {
  for (const name of result.missingFiles) {
    engine.addFile(name, await fetchPackage(name));
  }
  // Auxiliary files are retained, so the retry picks up where it left off.
  const retry = engine.compile({ entry: 'main.tex' });
}

This is the hook for on-demand package fetching. It is deliberately not an error: a missing file is recoverable, and TeX has no graceful degradation for a wrong one — substituting a near-match font silently corrupts output or hangs the engine.

Multi-file projects

Add every file, then point entry at the root. \input, \include, and \includegraphics resolve against the same virtual filesystem.

engine.addFiles({
  'main.tex': mainSource,
  'chapters/intro.tex': introSource,
  'figures/plot.pdf': plotBytes,
  'refs.bib': bibSource
});
engine.compile({ entry: 'main.tex' });

Options

All fields are optional; omitted ones take the Rust-side defaults. See CompileOptions for the full list — it is generated from the Rust definitions, so your editor's completions are authoritative.

| Option | Default | Notes | | --- | --- | --- | | entry | main.tex | Root document. | | jobname | derived from entry | Base name for .pdf, .log, .synctex. | | maxPasses / minPasses | 4 / 1 | Reruns stop once auxiliary files stop changing. | | haltOnError | false | Matches tectonic: TeX recovers from most errors. | | synctex | false | Emit <jobname>.synctex. | | outputFormat | pdf | xdv skips xdvipdfmx and is faster. | | paper | letter | e.g. a4. | | deterministic | false | Byte-identical output across runs. | | logLevel | error | trace logs every file open — very verbose. |

Types come from Rust

The declarations in src/generated/ are produced from the Rust definitions in crates/glyphtex-tex-api by ts-rs, so they cannot drift from what the engine accepts.

pnpm generate   # regenerate after changing the Rust types

That crate has no C dependencies and does not include the engine, so this works on any machine — no Emscripten toolchain required.

Not supported in WebAssembly

These need a subprocess, which does not exist in this sandbox:

  • minted — needs Python/Pygments. Use listings, which is pure TeX.
  • biber — a Perl program with no wasm build. bibtex runs in-process.
  • TikZ externalization — needs shell escape.

shellEscape is exposed for API parity with a native build; it has no effect here.

Tests

pnpm build
GLYPHTEX_WASM=path/to/tectonic_wasm.wasm GLYPHTEX_BUNDLE=path/to/bundle pnpm test

The suite skips itself when those are absent.