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

@inkoxide/wasm

v1.0.0

Published

The Inkoxide PDF engine as WebAssembly, for browsers: declarative document tree in, PDF bytes out.

Readme

@inkoxide/wasm

The Inkoxide PDF engine built as WebAssembly: a declarative document tree (JSON) in, PDF bytes out, in browsers. The same engine, limits and error codes as the native inkoxide package, with the same functions, plus init, threadCount, loadHyphenation and clearCaches, and one more render option, deferredFonts.

This page is for using the engine directly. To render templates in a browser, in a Web Worker, use the browser entry of your framework's package (@inkoxide/react/browser, @inkoxide/vue/browser, …), which runs this package for you: see In the browser.

npm install @inkoxide/wasm
import { init, registerFont, renderSync, InkoxideError } from '@inkoxide/wasm';

// Once: compiles `inkoxide.wasm`, which bundlers resolve next to this module.
await init();
registerFont('Inter-Regular', new Uint8Array(await (await fetch('/fonts/Inter-Regular.ttf')).arrayBuffer()));

const doc = {
  version: 1,
  fonts: [{ id: 'Inter-Regular', family: 'Inter' }],
  pages: [{ size: { width: 595.28, height: 841.89 },
            children: [{ type: 'text', runs: [{ text: 'Hello', font: 'Inter-Regular', size: 24 }] }] }],
};

try {
  const pdf = renderSync(JSON.stringify(doc));
  const url = URL.createObjectURL(new Blob([pdf], { type: 'application/pdf' }));
} catch (e) {
  if (!(e instanceof InkoxideError)) throw e;
  console.error(e.code, e.path, e.message);
}

How it differs from inkoxide:

  • One thread, or a pool. Renders run on the calling thread, render included: in a page, render in a Web Worker. threads may only be 1, unless init started a pool (below).
  • JSON only. format: 'msgpack' is a PARSE_ERROR.
  • Downloads on demand. The module (about 1.4 MB with brotli) leaves out the built-in hyphenation patterns: render fetches a language's from hyphenation/ next to the module the first time a document hyphenates text in it, and the fonts of its deferredFonts option (id → URL) that a document draws with, or needs as a fallback. renderSync cannot fetch, and fails with NOT_LOADED: call loadHyphenation(['de']) first, or use render.
  • init(source?, options?) takes a URL, a Response, the bytes or a compiled WebAssembly.Module, of either module. On one thread it needs no cross-origin isolation; a Content Security Policy needs script-src 'wasm-unsafe-eval'. A module that cannot be fetched or compiled rejects it with NOT_LOADED, as does any call before it has resolved.
  • A bug in the engine (INTERNAL) ends its instance: the next call runs in a new one, with the fonts and hyphenation patterns registered so far, which the package keeps a copy of.
  • Memory grows and is not given back. clearCaches() empties the caches renders share, for later renders to reuse the memory.
  • Fonts. A WOFF2 font gives other PDF bytes than its TrueType file.

Threads. In a worker of a page that is cross-origin isolated (Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp, on every response), or in Node, init can start a pool of threads: it then loads inkoxide-threads.wasm, which shares its memory with that many Web Workers (thread.js; worker threads in Node), and renders spread their pages over them, for the same bytes:

// A channel reaches every tab of the origin: a name of this engine's own.
const trapChannel = `inkoxide-trap-${crypto.randomUUID()}`;
await init(undefined, { threads: Math.min(navigator.hardwareConcurrency, 8), trapChannel });
threadCount(); // the pool's size, or 1 where memory cannot be shared

A render's threads: 1 keeps to the calling thread. A bug on a pool thread cannot end the call that waits for it: the thread posts { message } on the BroadcastChannel named trapChannel, and the page, listening, ends the worker. @inkoxide/render/browser, under each front end's browser entry, does all of this with its threads option.

From a CDN. A page cannot start a worker from a script of another origin. When this package is served from one, the pool's threads start as workers of the page's origin from a blob: URL that imports thread.js. The CDN must serve thread.js, the modules and the other assets with CORS (Access-Control-Allow-Origin); on a page that is cross-origin isolated, its files also need CORS or Cross-Origin-Resource-Policy: cross-origin. A Content Security Policy needs worker-src blob:, and script-src and connect-src that allow the CDN. A script of the page's own origin starts its workers as before, and worker-src 'self' is enough.

From a CDN, give init the module with its Subresource Integrity hash, so that the browser refuses any other bytes, and the folder of the hyphenation patterns:

const base = 'https://cdn.jsdelivr.net/npm/@inkoxide/[email protected]/';
await init(fetch(`${base}inkoxide.wasm`, { integrity: 'sha384-0tq2eWJQXL9wv3Om/Swz1rFtRoOKdRznjAmlz/o+dGAWzu6iriBTXM+uf/iUWRri' }), {
  hyphenation: `${base}hyphenation/`,
});

With threads, each module has its hash. Fetch only the one the page will use. A hash covers the module alone: the pool's thread.js, which its workers import, and the hyphenation patterns load without one.

const threads = crossOriginIsolated ? 4 : 1;
const module = threads > 1
  ? fetch(`${base}inkoxide-threads.wasm`, { integrity: 'sha384-Er0NyqOHjYzPSJFnwOpb2fjzNH2F2F+ntDgcT//1uw3thvxnWPsF5Kfvq7ty4lTB' })
  : fetch(`${base}inkoxide.wasm`, { integrity: 'sha384-0tq2eWJQXL9wv3Om/Swz1rFtRoOKdRznjAmlz/o+dGAWzu6iriBTXM+uf/iUWRri' });
await init(module, { threads, hyphenation: `${base}hyphenation/` });

Options, defaults and error codes are typed in index.d.ts and described in the API reference.