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

@vmprint/contracts

v1.0.2

Published

Core interfaces for VMPrint engines, contexts, and font managers.

Readme

@vmprint/contracts

The shared interface layer for the vmprint ecosystem.

What This Is

@vmprint/contracts defines the TypeScript interfaces that every piece of vmprint speaks across. The engine, rendering contexts, font managers, and overlay providers all talk to each other through these contracts — not through concrete implementations.

The package has zero dependencies. It contains no runtime code. After compilation, the dist/ is type declaration files and structurally empty JavaScript modules — interfaces are erased by TypeScript. The install footprint is negligible.

Why It Exists Separately

The conventional approach is to put shared types inside the main package and re-export them. The problem with that is anyone who wants to implement one of the interfaces — a custom font manager, a rendering context for a new target — has to take the whole engine as a dependency to get the types.

The engine has real weight. Its dependency tree includes fontkit (~1.1 MiB packed) for glyph metric parsing. That's appropriate for the engine. It's not appropriate for a rendering context that just needs to know the shape of Context, or a font manager that just needs to know the shape of FontManager.

@vmprint/contracts breaks that coupling. You depend on contracts. You implement the interface. You publish your package. The engine is not in your dependency tree unless you actually need the engine.

@vmprint/contracts    (no dependencies)
       │
       ├── @vmprint/engine          (depends on contracts)
       ├── @vmprint/context-*       (depends on contracts)
       └── @vmprint/font-managers   (depends on contracts)

Interfaces

FontManager

The contract for font loading and registry management. Implement this to provide fonts from any source — a CDN, object storage, a pre-loaded in-memory buffer, an OS font directory — without the engine caring where they came from.

interface FontManager {
  getFontRegistrySnapshot(): FontConfig[];
  resolveFamilyAlias(family: string): string;
  getAllFonts(registry: FontConfig[]): FontConfig[];
  getEnabledFallbackFonts(registry: FontConfig[]): FallbackFontSource[];
  getFontsByFamily(family: string, registry: FontConfig[]): FontConfig[];
  getFallbackFamilies(registry: FontConfig[]): string[];
  registerFont(config: FontConfig, registry: FontConfig[]): void;
  loadFontBuffer(src: string): Promise<ArrayBuffer>;
}

See the standalone font managers repository for the reference implementation and a guide to writing custom font managers.

VmprintOutputStream

A portable output stream interface. Callers (e.g. the CLI) implement this against their specific I/O mechanism — a file write stream, an in-memory buffer, a web response — and pass it to a context via pipe(). This keeps Node.js and platform I/O concerns out of both the context contract and any context implementation.

interface VmprintOutputStream {
  write(chunk: Uint8Array | string): void;
  end(): void;
  waitForFinish(): Promise<void>;
}

Context

The rendering surface contract. Implement this to paint vmprint's layout output to any target: PDF, SVG, canvas, a DOM surface, a test spy.

interface Context {
  addPage(): void;
  end(): void;
  pipe(stream: VmprintOutputStream): void;  // no-op if output streaming is not supported
  registerFont(id: string, buffer: Uint8Array, options?: { standardFontPostScriptName?: string }): Promise<void>;
  font(family: string, size?: number): this;
  fontSize(size: number): this;
  save(): void;
  restore(): void;
  translate(x: number, y: number): this;
  rotate(angle: number, originX?: number, originY?: number): this;
  opacity(opacity: number): this;
  fillColor(color: string): this;
  strokeColor(color: string): this;
  lineWidth(width: number): this;
  dash(length: number, options?: { space: number }): this;
  undash(): this;
  moveTo(x: number, y: number): this;
  lineTo(x: number, y: number): this;
  rect(x: number, y: number, w: number, h: number): this;
  roundedRect(x: number, y: number, w: number, h: number, r: number): this;
  fill(rule?: 'nonzero' | 'evenodd'): this;
  stroke(): this;
  fillAndStroke(fillColor?: string, strokeColor?: string): this;
  text(str: string, x: number, y: number, options?: ContextTextOptions): this;
  image(source: string | Uint8Array, x: number, y: number, options?: ContextImageOptions): this;
  getSize(): { width: number; height: number };
}

pipe() is required on the interface but may be a no-op. Contexts that manage their own output (e.g. accumulate bytes in memory and expose them through their own API) simply implement it as pipe(_stream) {}. Contexts that support streaming — like PdfContext — write rendered output into the stream as pages are produced. The caller owns the stream and calls waitForFinish() on it after rendering is complete.

See the standalone contexts repository for the reference implementation and a guide to writing custom contexts.

OverlayProvider

A hook for drawing before and after page content without modifying the layout. Used for watermarks, debug grids, crop marks, confidentiality banners, and print production marks.

interface OverlayProvider {
  backdrop?(page: OverlayPage, context: OverlayContext): void;
  overlay?(page: OverlayPage, context: OverlayContext): void;
}
  • backdrop — called before the page content is painted. Draws appear behind all page elements.
  • overlay — called after the page content is painted. Draws appear on top.

Both methods receive OverlayPage, which carries the page dimensions and the full box tree from the layout pass. This means overlays can make layout-aware decisions — position a watermark relative to the text area, draw margin rules at the exact margin coordinates, highlight specific box types for debugging.

OverlayContext is a drawing-only subset of Context: the full shape and styling API, without the document lifecycle methods (addPage, end) or font registration. The rendering context handles those; the overlay just draws.

interface OverlayPage {
  readonly index: number;
  readonly width: number;
  readonly height: number;
  readonly boxes: readonly OverlayBox[];
}

A minimal watermark example:

import { OverlayProvider, OverlayPage, OverlayContext } from '@vmprint/contracts';

class DraftWatermark implements OverlayProvider {
  overlay(page: OverlayPage, ctx: OverlayContext): void {
    ctx.save();
    ctx.opacity(0.08);
    ctx.fillColor('#000000');
    ctx.font('Helvetica', 72);
    ctx.translate(page.width / 2, page.height / 2);
    ctx.rotate(-45);
    ctx.text('DRAFT', -120, -36);
    ctx.restore();
  }
}

Transmuter

The contract for author-facing source conversion. Implement this to convert specific source formats (like Markdown) into VMPrint's DocumentInput AST.

interface Transmuter<Input = string, Output = unknown, Options = TransmuterOptions> {
  transmute(input: Input, options?: Options): Output;
  getBoilerplate?(): string;
}
  • transmute — converts input source into typeset boxes or a document IR.
  • getBoilerplate — (Optional) returns a recommended configuration block (e.g., YAML) to assist users in setting up a new document for this transmuter's format.

This decoupling allows tools like draft2final to orchestrate multiple input formats and default configurations without being coupled to the internal logic of any specific format.

Usage

npm install @vmprint/contracts
import type { FontManager, Context, OverlayProvider, Transmuter } from '@vmprint/contracts';

Because all exports are TypeScript interfaces, the import adds no runtime weight — types are fully erased at compile time. Importing @vmprint/contracts in a production build costs exactly zero bytes.


Licensed under the Apache License 2.0.