@quillmark/quiver
v0.5.1
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/wasmDistribution 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. - Browser consumers run
Quiver.build(...)as a build step and serve the output as static assets, loading it withQuiver.fromBuilt.
Each loader names exactly what it loads: fromPackage and fromDir always
read source layouts; fromBuilt always reads build output over an HTTP(S)
or origin-relative URL. 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.
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.jsonRecommended CI: use the bundled @quillmark/quiver/testing harness — it
loads with Quiver.fromDir and exercises every quill 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.
Consuming a quiver (Node)
import { Quillmark, Document } from "@quillmark/wasm";
import { Quiver } from "@quillmark/quiver/node";
const engine = new Quillmark();
const quiver = await Quiver.fromPackage("@org/my-quiver");
const doc = Document.fromMarkdown(markdownString);
const quill = await quiver.getQuill(doc.quillRef, { engine });
const result = quill.render(doc, { format: "pdf" });getQuill accepts both selector refs ("memo", "memo@1") and canonical
refs ("[email protected]"). It resolves the selector, materializes the quill via
engine.quill(tree), and caches per (engine, canonical-ref). Concurrent
calls for the same ref share a single load.
If you only need the canonical ref (without materializing), use resolve:
const canonicalRef = await quiver.resolve("memo"); // "[email protected]"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 { Quiver } from "@quillmark/quiver";
const quiver = await Quiver.fromBuilt("/quivers/my-quiver/");
const quill = await quiver.getQuill(doc.quillRef, { engine });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 fromBuilt 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.fromBuilt("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
fromBuilt, off the filesystem for fromPackage/fromDir) 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.
Once a tree has been turned into a Quill, the cached tree is dropped so
its bytes can be GC'd — the materialized Quill is the runtime artifact.
Calling warm() again refills the tree cache.
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.
Full specification
See PROGRAM.md for the complete API surface, runtime artifact format specification, and design decisions.
