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

@venn-lang/toolchain

v0.9.0

Published

Which version of the language a directory is asking for, and where the versions on a machine live.

Readme

@venn-lang/toolchain

Which version of the language a directory is asking for, and where the versions on a machine live.

The venn binary does not contain the language. It works out which version a directory wants, installs it if it is not there, and hands the command over. This package holds the part that decides.

Nothing here reads a .vn file or knows what one is. That separation is the point: the binary you install stays small, and the language is a thing it fetches and keeps, one directory per version.

Two questions, kept apart

What a directory asked for is one question. Which installed version that turns out to mean is another, and it needs to know what is installed.

import { createNodeFs } from "@venn-lang/contracts/node";
import { describe, resolveVersion, selectVersion } from "@venn-lang/toolchain";

const request = await resolveVersion({
  fs: createNodeFs(),
  directory: process.cwd(),
  defaultVersion: "0.1.3",
});

const choice = selectVersion({ request, installed: ["0.1.3", "0.2.0", "0.2.4"] });

console.log(describe(choice));
// 0.2.4, the newest matching 0.2.x, pinned by /work/api/venn.toml

Neither touches the network, writes anything, or runs anything. What to do about a version nobody installed belongs to whoever asked.

What decides

In order, first answer wins:

| | | | --- | --- | | venn in [package] of a venn.toml | a project pinning its language where the rest of its decisions live | | a .venn-version file | a directory that is not a project, or a pin you would rather not put in a manifest under review | | the default given | the version chosen for everything that does not ask | | nothing | *, which the newest installed version answers |

The search walks up from the directory given, nearest first, so a command run inside tests/api gets the version its project declared. The nearest pin wins, which lets one member of a workspace hold itself back while the rest move on.

A manifest that pins nothing, or cannot be parsed at all, is passed over rather than raising: a broken manifest is the compiler's to complain about, with a location and a line, which is more than this could say.

A pin can be a range

[package]
venn = "0.2.x"        # any 0.2, newest installed
venn = "^1.2.0"       # any 1.x from 1.2 up
venn = ">=1 <1.5"     # a window
venn = "0.2.0"        # that one

Always the newest that matches, never an arbitrary one. Pinning 0.2 and getting 0.2.1 today and 0.2.4 tomorrow would make a pin worse than no pin.

A prerelease only answers a range that asks for it by name. Running on a release candidate is a decision, and 1.x quietly picking up a 1.5.0-rc.1 that happened to be installed is not how anyone would want to make it.

Asking the registry what exists

The same range language, against what is published rather than what is installed. A tag is looked up rather than parsed, so latest means what the registry says it means.

import { catalogueOf, createFetchJson, releaseFor } from "@venn-lang/toolchain";

const catalogue = await catalogueOf({ fetchJson: createFetchJson() });

releaseFor({ catalogue, request: "latest" });
// { version: "0.1.3", tarball: "https://…/cli-0.1.3.tgz", integrity: "sha512-…" }

releaseFor({ catalogue, request: "0.1.x" });   // the newest published 0.1
releaseFor({ catalogue, request: "9.x" });     // undefined

fetchJson is passed in rather than reached for, so this is testable without a network and a mirror or a proxy is a different function rather than a rewrite.

It asks for the abbreviated document, which holds the tags, the versions and each tarball with its hash. For this package that is 4 KB where the full one is 21, and the full one grows with every release while the abbreviated one keeps its shape.

A version published without a tarball or without an integrity hash is left out. It could not be installed and could not be checked, so offering it would only fail later and further away.

Installing one

const release = releaseFor({ catalogue, request: "latest" });

await installVersion({
  fs: createNodeFs(),
  release,
  into: `${homedir()}/.venn/versions`,
  fetchBytes: createFetchBytes(),
});
// ~/.venn/versions/0.1.3

The download is checked against the hash the registry published before anything is unpacked. A mirror, a proxy or anything else in between can hand over different bytes, and only the hash says so.

Files are unpacked beside the destination and moved in at the end, so an interrupted install leaves nothing that looks finished. A half-written version directory is worse than none: the next command finds it, believes it, and fails somewhere further away.

What an archive is not allowed to do

A name inside a tarball is a claim about where its content should end up, made by whoever built it. ../../.ssh/authorized_keys is a perfectly valid tar entry, and a reader that joins names onto a path without asking will write it exactly where it says.

So an entry is only written when its name stays inside: no segment that climbs, nothing anchored to a root or a drive, no backslash used to smuggle one past, and nothing outside the package/ prefix. Anything else is skipped and the rest of the archive still installs.

Only regular files are taken. Directories arrive implicitly with the files in them, and a symlink, a hard link or a device node has no business coming out of a package tarball.

The whole decision, in one call

const home = vennHome({ env: process.env, home: homedir() });
const plan = await planFor({ fs, home, directory: process.cwd() });

switch (plan.kind) {
  case "run":     // hand over to plan.entry
  case "install": // fetch plan.request first, then hand over
  case "stop":    // say plan.reason
}

The orchestrator holds no policy of its own: it asks what to do and does it. A test can ask the same question without anything happening.

A version that is not installed is fetched rather than refused. Someone who pinned a version and ran a command has already said which one they want, and being asked whether they meant it helps nobody.

What counts as installed

A directory under versions/ whose manifest declares an entry point that is actually there. Read from the disk rather than from a file listing them, because a file can disagree with the disk and then two things need repairing instead of one.

The entry point comes from the version's own bin field rather than a fixed path, so a version decides where it keeps things. That also lets a version published before the language separated from the orchestrator keep working: it declares venn, where a newer one declares venn-run, and both are found.

The reason travels with the answer

describe exists because someone asking which version they are on has usually just been surprised by it. 0.2.0 starts that conversation. 0.2.0, pinned by /work/api/venn.toml ends it.