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

@quillmark/quiver

v0.16.0

Published

Quiver registry and build tooling for Quillmark

Readme

@quillmark/quiver

Load and build collections of quills for rendering with @quillmark/wasm.

Install

npm install @quillmark/quiver @quillmark/wasm

Distribution model

A Quiver has one authored shape: the source layout (Quiver.yaml at the package root, quills under quills/<name>/<x.y.z>/). Authors publish it as an npm package. Consumers decide how to consume it:

  • Node consumers load the source layout directly with Quiver.fromPackage, or load a packed (build-output) artifact from disk with Quiver.fromBuiltDir.
  • Browser consumers run Quiver.build(...) as a build step and serve the output as static assets, loading it with Quiver.fromBuiltUrl.

Each loader names exactly what it loads. Source: fromPackage(specifier), fromDir(path). Build output: fromBuiltUrl(url) (HTTP/HTTPS, browser-safe), fromBuiltDir(path) (filesystem, Node-only). No auto-detection, no branching on artifact shape.

This keeps the author flow to a single command (npm publish or git tag) and puts the deployment-topology decision where it belongs: with the consumer.

The render model

@quillmark/quiver produces quills; @quillmark/wasm renders them.

  • quiver.getQuill(ref) returns a core Quill — engine-free, portable data, good for schema inspection, validation, blueprint access, and seeding. It is the only way to obtain a quill from a quiver.
  • const engine = new Engine() (from the @quillmark/wasm root) renders it: await engine.render(quill, doc, opts?). The Engine routes on quill.backendId, lazily loads that backend, clones the quill and document into the backend's WASM memory, renders, and frees the clones — so a core Quill passes straight to engine.render with no boundary-crossing step.

Engine.render, open, supportedFormats, and supportsCanvas are all async — always await them. The canonical Quill/Document/Engine types are not re-exported from @quillmark/quiver; import them straight from the @quillmark/wasm peer dependency, which is the single source of truth.

Authoring a quiver

Lay out the source per the spec, then publish to npm (or push a git tag):

my-quiver/
  Quiver.yaml
  quills/
    <name>/<x.y.z>/
      Quill.yaml
      ...
  package.json

Recommended CI: use the bundled @quillmark/quiver/testing harness — it loads with Quiver.fromDir, compiles every quill, and renders each quill's example document so validation errors surface on publish, not on the consumer's build. The harness uses node:test (built into Node 18+); no extra test-runner dependency required. If you prefer vitest/jest/mocha, write a 12-line loop against the main API instead.

// quiver.test.ts — run with: node --test
import { Engine } from "@quillmark/wasm";
import { runQuiverTests } from "@quillmark/quiver/testing";

runQuiverTests(import.meta.url, new Engine());

Manual validation (rendering samples)

The CI harness proves every quill compiles; it does not produce output a human can look at. To eyeball real renders, run the @quillmark/quiver/preview helper:

// scripts/preview.ts — run with: node --experimental-strip-types scripts/preview.ts
import { Engine } from "@quillmark/wasm";
import { renderQuiverSamples } from "@quillmark/quiver/preview";

await renderQuiverSamples(import.meta.url, {
  engine: new Engine(),
});
// → writes ./preview/<name>@<version>.<fmt> + index.html

It renders every quill's illustrative example document (seeded via quill.seedDocument() — a fully filled-out, always-renderable sample; the blueprint itself carries <must-fill> sentinels and is not directly renderable), writes the artifacts to outDir (default preview/), and emits an index.html gallery. A .gitignore is written into outDir so the generated artifacts are never accidentally committed. A quill that throws is recorded as failed — with every diagnostic, not just the first — without aborting the run, so one broken quill never hides the rest.

To iterate on a subset, pass include / exclude (each entry matches a quill name or canonical ref):

await renderQuiverSamples(import.meta.url, {
  engine: new Engine(),
  exclude: ["broken-quill"], // or: include: ["[email protected]"]
});

Linking the source repo? @quillmark/quiver/preview resolves to ./dist/preview.js, which only exists after npm install && npm run build in the @quillmark/quiver checkout. If you npm link it and see Cannot find module './dist/preview.js', build the linked package first.

Consuming a quiver (Node)

import { Engine, Document } from "@quillmark/wasm";
import { Quiver } from "@quillmark/quiver/node";

const quiver = await Quiver.fromPackage("@org/my-quiver");
const engine = new Engine();

const doc = Document.fromMarkdown(markdownString);
const quill = await quiver.getQuill(doc.quillRef);
const result = await engine.render(quill, doc, { format: "pdf" });

getQuill accepts both selector refs ("memo", "memo@1") and canonical refs ("[email protected]"). It resolves the selector, materializes the quill via Quill.fromTree(tree) (engine-free), and caches one instance per canonical ref for the lifetime of the Quiver. Concurrent calls for the same ref coalesce into a single load.

The returned quill lives in the core build's WASM memory and is suitable for schema inspection, validation, blueprint access, and seeding. To render it, hand it straight to engine.render(quill, doc) — the Engine clones both handles into the backend on demand, so no separate boundary-crossing step is needed:

// Editor path — core only, ~0.66 MB gzip
const coreQuill = await quiver.getQuill(ref);
coreQuill.schema; // ✓ schema, validate, blueprint, seed — all fine
const doc = coreQuill.seedDocument(); // a fully-filled example document

// Render path — the same core handles render directly.
const result = await engine.render(coreQuill, doc, { format: "pdf" });

If you only need the canonical ref (without materializing), use resolve:

const canonicalRef = await quiver.resolve("memo"); // "[email protected]"

If you need the raw file tree (e.g. to drive a backend binary directly), call (await quiver.getQuill(ref)).toTree() on the core Quill — it is I/O-free once the quill is materialized.

Consuming a quiver (browser)

Browsers cannot read the source layout directly, so build at deploy time and serve the output as static files:

// build script (Node) — typically wired into your existing build pipeline
import { Quiver } from "@quillmark/quiver/node";

await Quiver.build(
  "./node_modules/@org/my-quiver",
  "./public/quivers/my-quiver",
);
// browser runtime
import { Engine, Document } from "@quillmark/wasm";
import { Quiver } from "@quillmark/quiver";

const quiver = await Quiver.fromBuiltUrl("/quivers/my-quiver/");
const engine = new Engine();

const doc = Document.fromMarkdown(markdownString);
const quill = await quiver.getQuill(doc.quillRef);
const result = await engine.render(quill, doc, { format: "pdf" });

SSR seeding (skip the latest.json pointer)

Quiver.fromBuiltUrl(url) first fetches <url>/latest.json — a stable-named, non-content-addressed pointer to the current manifest — before fetching the manifest itself. Because that one filename is stable, a CDN edge or browser cache can serve a stale pointer after a release and silently pin the client to the old catalog, with no error. Per-host cache headers only fix this one serving layer at a time.

If you already hold the manifest bytes at build time — a common case for SSR consumers, which read the built artifact during their own build — seed the catalog from them directly with Quiver.fromManifest. It never requests latest.json; bundles and fonts are still fetched lazily and content-addressed, relative to baseUrl, exactly as with fromBuiltUrl:

// Server build step — read the manifest the build wrote.
// build-output/latest.json → { "manifest": "manifest.<hash>.json" }
import { readFile } from "node:fs/promises";

const { manifest } = JSON.parse(
  await readFile("./public/quivers/my-quiver/latest.json", "utf8"),
);
const manifestBytes = new Uint8Array(
  await readFile(`./public/quivers/my-quiver/${manifest}`),
);
// Inline manifestBytes into the page payload (e.g. base64) for the client.
// Browser / SSR runtime — seed from the bytes you shipped, no pointer fetch.
import { Engine, Document } from "@quillmark/wasm";
import { Quiver } from "@quillmark/quiver";

const quiver = await Quiver.fromManifest("/quivers/my-quiver/", manifestBytes);
const engine = new Engine();

const doc = Document.fromMarkdown(markdownString);
const quill = await quiver.getQuill(doc.quillRef);
const result = await engine.render(quill, doc, { format: "pdf" });

fromManifest is browser-safe (no node:* imports) and shares fromBuiltUrl's error semantics: quiver_invalid for malformed manifest bytes, transport_error for a file:// baseUrl or a later bundle-fetch failure.

Server-side runtime (Node, packed artifact on disk)

For server-side rendering where the packed artifact ships in the deployment image, use Quiver.fromBuiltDir to read it from disk. This avoids the self-fetch / load-balancer round-trip that fromBuiltUrl would force on a self-hosted deployment, and lets the source quiver stay in devDependencies:

import { Quiver } from "@quillmark/quiver/node";

// Packed at build time, e.g. into ./static/quills/my-quiver
const quiver = await Quiver.fromBuiltDir("./static/quills/my-quiver");

Advanced: pre-built distribution to a CDN

If you need to ship the runtime artifact directly (e.g. consumers cannot run a Node build step), publish Quiver.build output to a CDN and have consumers point fromBuiltUrl at the CDN URL:

import { Quiver } from "@quillmark/quiver/node";

await Quiver.build("./my-quiver", "./dist/my-quiver");
// upload ./dist/my-quiver to https://cdn.example.com/quivers/my-quiver/
const quiver = await Quiver.fromBuiltUrl("https://cdn.example.com/quivers/my-quiver/");

Warm (prefetch all quill trees)

await quiver.warm();

warm() is I/O-only: it loads every quill's tree (over the network for fromBuiltUrl, off the filesystem for fromPackage / fromDir / fromBuiltDir) and caches them. It does not require an engine and does not materialize Quill instances — that happens lazily on the first getQuill call, which is microseconds. A subsequent getQuill reuses the cached tree, skipping the load.

Error handling

All errors are instances of QuiverError with a code field.

import { QuiverError } from "@quillmark/quiver";

try {
  await quiver.resolve("unknown_quill");
} catch (err) {
  if (err instanceof QuiverError) {
    console.error(err.code);    // e.g. "quill_not_found"
    console.error(err.message); // human-readable description
    console.error(err.ref);     // offending ref, when available
  }
}

Error codes: invalid_ref, quill_not_found, quiver_invalid, transport_error.

Using getQuill vs calling Quill.fromTree directly

getQuill(ref) is the correct entry point for any code that loads a quill through a Quiver. It handles ref resolution, tree fetching, Quill.fromTree construction, and per-ref caching in one call. Do not reach for Quill.fromTree directly inside a Quiver consumer:

import { Quill } from "@quillmark/wasm";

// wrong — bypasses Quiver's cache; duplicates work getQuill already does
const tree = new Map<string, Uint8Array>();
tree.set("Quill.yaml", new TextEncoder().encode("name: memo\n..."));
// ...assemble the rest of the file tree by hand...
const quill = Quill.fromTree(tree);

// right
const quill = await quiver.getQuill(ref);

Quill.fromTree is for code that builds quills outside of a Quiver (e.g. a server route that receives a raw file tree over the network, or a test fixture that constructs a quill from a hand-rolled in-memory tree).