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

paratext

v1.0.9

Published

Hyperlinks, inline images, the window title, the clipboard, notifications and the bell, each with a static fallback for a terminal, pipe or agent that cannot show it. Zero dependencies. Drop-in paths for ansi-escapes, terminal-link and term-img.

Readme

paratext owns everything around terminal output that is not the output: hyperlinks, inline images, the window title, the clipboard, desktop notifications, the working directory and the bell — OSC, the escape class (ESC ]) that addresses the terminal program rather than the character grid. Every OSC capability has a static projection, so a pipe or an agent gets Docs (https://x.dev), never raw escape bytes. Zero dependencies.

Drop-in for ansi-escapes (the package root, 4 / 4), terminal-link (paratext/terminal-link, 8 / 8) and term-img (paratext/term-img, 18 / 18): npx burgee migrate moves the imports, each graded by the incumbent's own suite and level with it.

Paratext is the literary term for everything around a text that is not the text — the title, the cover, the margins, the notes. This package owns the terminal equivalent. The intent and design live at .sdlc/intents/paratext/.

Install

npm install paratext
pnpm add paratext
yarn add paratext
bun add paratext

Quick start

import { emit, processRuntime } from 'paratext';

const runtime = processRuntime();
console.log(emit(runtime, 'link', { text: 'Docs', url: 'https://x.dev' }));
// iTerm2:  \e]8;;https://x.dev\aDocs\e]8;;\a
// a pipe:  Docs (https://x.dev)

The same call gives the terminal's bytes where it understands them and the static projection everywhere else; the next section is why that is the only safe default.

Nothing in this layer is detectable

No terminal answers "do you do OSC 1337". So every capability carries a static projection, and emit returns it whenever support is absent or unknown: an image becomes its caption, a notification a printed line, a hyperlink text (url). Emitting the bytes and hoping is what puts \u001B]1337;File=inline=1;… across the screen of anyone who piped your output to a file, and it is what every incumbent does.

A capability is data

Terminals invent OSC codes faster than any package ships releases — Kitty's graphics protocol, WezTerm's user-vars, Ghostty's progress bar. So the surface is a registry of plain objects rather than a fixed list of functions, and the seven built-ins register through the same public call a third party makes.

import { register } from 'paratext';

register({
  name: 'kitty-image',
  osc: 'BEL',
  when: { tty: true, term: 'xterm-kitty' },
  encode: '\u001B_Ga=T,f=100;{base64}\u001B\\',
  fallback: '{caption}', // required — there is no opt-out
});

No functions, so a capability travels through JSON, is diffable, and can be validated without running its author's code. check(document) grades one against paratext/schema.json, the same file every package in the family ships. paratext/plugin is the host: a plugin's capabilities arrive under capabilities, and every other key — another layer's tokens, spinners, handlers — is ignored without complaint.

What "grades against the schema" means, exactly. check() and register() read the capability entry of that file and enforce required, type, oneOf, const, minLength, minimum, items, and additionalProperties: false, which between them is every keyword the capability shape writes. Each refusal names the path — capabilities.link.when.tty, not just the capability — and carries the family's error code. What it does not read is $ref, pattern, minItems, maxLength, enum, allOf, anyOf and not: none of them appears under a capability, so nothing is silently unchecked today, but a keyword added to the file tomorrow would be. Until 0.3 this was presence-checking only, and when: 'not an object' was therefore accepted — a string destructures to four empty clauses, so the support guess said yes and the sequence went into the pipe. That is now a refusal.

Writing a plugin

A plugin is one module whose default export is a plain object: a name, the family's contract: 1, and its capabilities under capabilities, each filed under its own name. This one sets the window title (OSC 2, ended by BEL, \u0007) and prints nothing where it cannot:

// title-plugin.mjs
export default {
  name: 'titler',
  contract: 1,
  capabilities: {
    title: {
      name: 'title',
      osc: 2, // the OSC code, or 'BEL' for the bell and for protocols that are not OSC
      when: { tty: true }, // every clause must hold; {} is always
      encode: '\u001B]2;{text}\u0007', // the bytes, as a template
      fallback: '', // what prints where it cannot; '' is an answer, leaving it out is not
    },
  },
};

paratext check loads it, validates it against the schema, and prints both projections of each capability, then ok — or, exit 1, every problem it found at once, each with its code and a fix: to apply:

$ npx paratext check title-plugin.mjs
titler — 1 capabilities
  title  osc 2  encode "\u001b]2;{text}\u0007"  fallback ""
titler: ok

Migrating

One import per incumbent:

- import ansiEscapes, { cursorTo, eraseLines, link, image, beep } from 'ansi-escapes';
+ import ansiEscapes, { cursorTo, eraseLines, link, image, beep } from 'paratext';
- import terminalLink from 'terminal-link';
+ import terminalLink from 'paratext/terminal-link';
- import terminalImage, { UnsupportedTerminalError } from 'term-img';
+ import terminalImage, { UnsupportedTerminalError } from 'paratext/term-img';

paratext/term-img takes a file path or image bytes, as term-img does. It is the one entry that imports node:fs, to read that path; the root and every other entry touch nothing but strings (Coming from term-img).

The root is ansi-escapes' surface, both halves of it — setCwd included.

The CSI half — the cursor, erasing, scroll regions, the alternate screen, synchronized output — is byte-exact with [email protected]. It does not degrade, because the incumbent's does not: a cursor move silently dropped would corrupt the screen of a program that relied on it.

The CSI half is also published on its own, as paratext/csi — 2,700 bytes, and it registers none of the built-ins the root does. It is where flagstaff and caique take their cursor moves from:

import { cursorUp, eraseLines, synchronizedOutput } from 'paratext/csi';

The OSC half — link, image, setCwd, beep — gives the incumbent's bytes where the terminal understands them and the static projection everywhere else. That is the one deliberate difference.

iTerm and ConEmu are still declared, as undefined, so a drop-in module loads rather than dying on an ESM named import — and TypeScript types them such that calling one is a compile error, not a surprise at run time. iTerm.annotation has no equivalent yet.

Or let the codemod make the change: npx burgee migrate --dry-run lists every import it would rewrite — only drop-ins graded level with their incumbent — and npx burgee migrate makes it (Migrate).

Compatibility

Each drop-in path is graded by its incumbent's own suite, unedited, through compat-oracle.

Graded by the compat oracle against [email protected]'s own suite: 4 / 4, level with the control. Three of its four cases assert CSI; until the CSI half landed, that row was 1 / 4.

paratext/terminal-link is graded by terminal-link's suite and paratext/term-img by term-img's, and both are level with the incumbent. term-img's six path cases went green when paratext/term-img began reading a path the way term-img does. The current grades are generated under Benchmarks below and published on the compatibility page.

Benchmarks

Every number here is produced by npm run bench and published at burgee.interlace.tools/docs/benchmarks.

Graded by the incumbent's own test suite:

| suite | passing | | :-- | --: | | ansi-escapes | 4 / 4 | | term-img | 18 / 18 | | terminal-link | 8 / 8 |

Weight, installed and tree-inclusive: 119,655 bytes against 2,235,987 for the incumbents it replaces — a ratio of 0.0535.

For agents

  • A pipe or an agent never sees raw escape bytes. Every capability has a static projection, and emit returns it whenever support is absent or unknown: Docs (https://x.dev), not \u001B]8;;….
  • A capability is data. No functions, so a capability travels through JSON, is diffable, and check(document) grades it against paratext/schema.json without running its author's code; each refusal names the path and the family's error code.
  • A plugin can be checked before it ships. npx paratext check ./kitty.mjs validates it and exits 0, 1 with a code and a fix, or 2 on a usage error.
  • The docs are machine-readable at paratext.interlace.tools/llms.txt and llms-full.txt.

API

emit, register, check and processRuntime from the root, beside the whole ansi-escapes surface; paratext/plugin for the host. Every export, with its types, is on paratext.interlace.tools.

Where it sits

Plugins register under the capabilities key, against the one schema the whole family shares.

caique, controlroom, flagstaff build on it, and it builds on nothing in this family.

The family

Ten packages, one repository, one release pipeline. Each installs and works on its own, and each owns one job. Six of them — bellpull, closeout, linegauge, paratext, roundel, seniority — depend on nothing; the others depend only on packages in this table, and every dependency points one way, down the layers.

No package declares a dependency from outside the family; react and react-reconciler are optional peers of controlroom, which npm does not install. package-shape-lock holds that for every manifest, and independence-install-lock installs each package alone and finds nothing but the family packages it declares.

Releases are published by one workflow through npm trusted publishing: no npm token is used, and each release carries SLSA provenance, which npm view <package> dist.attestations shows. trusted-publishing-lock keeps both true.

| Package | What it is | Migrates from | | :-- | :-- | :-- | | burgee | The CLI framework: one declaration, every surface | commander, yargs and meow | | roundel | Colour: one output policy, semantic tokens, a theme | chalk and picocolors | | flagstaff | The frame loop: spinners, progress, boxes and tables | ora, log-update, boxen and cli-table3 | | caique | Prompts that are flags first, and never hang | @inquirer/core | | linegauge | Measuring, wrapping, truncating and slicing styled text | string-width, wrap-ansi, strip-ansi, slice-ansi, cli-truncate and widest-line | | paratext (this package) | Hyperlinks, images, title, clipboard and notifications | ansi-escapes, terminal-link and term-img | | seniority | Configuration precedence and discovery, with provenance | cosmiconfig, lilconfig, dotenv and rc | | closeout | Exit handlers, terminal restore and a bounded shutdown | signal-exit, exit-hook, restore-cursor, cli-cursor and onetime | | bellpull | Subprocesses, and which executable actually ran | cross-spawn and which | | controlroom | Keyboard-driven terminal screens, inline or full-screen | ink |

Every migration guide, and the family-wide compatibility and benchmarks pages, are on burgee.interlace.tools.

Contributing

Issues and pull requests are welcome at ofri-peretz/burgee; read CONTRIBUTING.md first. Report a vulnerability privately, as SECURITY.md describes — never in a public issue.

Licence

MIT © Ofri Peretz — see LICENSE.