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

@intx/tool-packaging

v0.5.0

Published

Resolver and loader for the npm-distributed Interchange tool-package format

Readme

@intx/tool-packaging

Hub-side closure resolver and sidecar-side loader for the npm-distributed tool-package format. The contract below describes what the layered pipeline actually guarantees end-to-end; downstream code (the apply protocol, the session service, the deploy-flow integration tests) leans on it as written.

Loader resolution contract

A tool package's interchange.tools entry, and the JS body it pulls in transitively, may import (or require) any bare specifier the manifest's resolved closure satisfies. The loader builds a per-instance nested node_modules/ layout under the scratch directory so each package's direct dependencies are reachable through Node's standard ancestor walk; the layout composes recursively, so a transitive's own transitives are reachable from inside it.

Concretely the loader:

  1. Walks every manifest entry and validates that each one is source-consistent: every entry the manifest says lives at a registry resolves end-to-end against that registry; an asset entry whose package.format is "tarball" resolves against the asset's assetMounts map; an asset entry whose package.format is "source" (a git subtree, used by source-format workflow definitions) resolves against the separate gitDirs map.
  2. Materializes every entry into the content-addressable tarball cache: bytes pulled from the entry's source on a miss are verified through cache.put, and the bytes are then unpacked once via cache.extractTarball. A single sha512 produces a single extraction shared across instances. A "source" asset entry is the exception: it has no tarball and no SRI, so it bypasses the cache entirely — its subtree is checked out in place from the indexed gitDir (materializeGitEntry) and verified against the frozen git treeOid rather than an sha512.
  3. Lays out each entry under <scratch>/store/<name>/<version>/ by copying the file tree from the cache extraction — or, for a "source" entry, from its git checkout. Each layout directory then gets node_modules/<dep> symlinked into it pointing at the sibling store/<dep>/<depVersion>/ chosen for that requirer. Symlinks at the node_modules/ boundary let Node's realpath-based resolver walk to the dep's own layout dir (which has its own node_modules/).
  4. Dynamically imports each top-level entry's interchange.tools module and collects the AnnotatedToolFactory / AnnotatedPluginFactory values it exports.

Properties that fall out of this layout:

  • Closure satisfies itself. A top-level package's body, plus every transitive it pulls in, resolves bare-specifier imports against the manifest closure with no help from the sidecar host's own node_modules.
  • No hoisting between top-levels. Each top-level keeps its own nested view; two top-levels that disagree on a transitive's version get two distinct entries in the store, both materialized.
  • Version conflicts coexist. Diamond dependencies share a single store entry (one symlink target per (name, version)); divergent versions live as separate store entries and Node's ancestor walk picks the right one for each requirer.
  • Runtime-agnostic tarballs. The loader does no Bun-specific resolution. A tool package whose interchange.tools entry is a .js file with no workspace-only deps loads identically on Node and Bun. The workspace's built-in tool packages bundle their entries through bin/build-builtins.ts precisely so the produced tarballs satisfy this property.

Optional-dependency semantics

The closure walker honors npm's "an optional subtree is best-effort" contract: when an optionalDependencies edge fails to resolve — the packument is absent, the range picks nothing, the registry rejects the fetch — the whole subtree under that edge is dropped from the manifest silently. A hard dep of an optional dep is part of that optional subtree; a failure there drops the parent too, instead of leaving a parent in the closure whose own hard requirement is missing. Failures outside any optional subtree abort the walk and surface as a manifest.invalid deploy-apply error.

Asset modification race

The hub-side AssetRegistrySource reads tarball packuments from a package-registry asset at resolution time. The sidecar's createPack then resolves the asset's main ref and serves the tarball bytes the resolver pinned. A concurrent PUT or DELETE against the asset's tarballs/ prefix in the window between the resolver's read and the sidecar's fetch can cause the apply to fail with tarball.missing or integrity.mismatch.

The substrate's writeTreePreservingPrefix primitive makes PUTs and DELETEs against the asset atomic with respect to one another, but it does not couple them to the sidecar's deploy-apply read. The intentional behavior is: the apply fails loudly as a rejected deploy-apply. There is no silent corruption — the integrity check on the sidecar side guarantees that mismatched bytes are rejected — but the operator does see a failed apply if the asset is mutated concurrently with a launch.

A follow-up that snapshots the asset's commit SHA at resolution time and replays against it at apply time is in scope if this shows up in operator practice; it is not implemented today.

Call-time imports and import.meta.url

Tool packages may resolve files relative to their own on-disk location at run time — import.meta.url, require.resolve(), or an await import(new URL("./sibling.js", import.meta.url)) evaluated long after the module first loaded. The loader imports each top-level package's interchange.tools entry from a path under <instanceDir>/packages/<deploy-id>/ that is never renamed, so a module's import.meta.url stays valid for the life of the deploy.

The prior deploy's directory is retained across the next apply, so a session still draining against an older deploy keeps resolving its own files until a further apply supersedes it. See ASSET-LAYOUT.md for the on-disk layout and retention rules.

Follow-up reference

  • INTR-180: drizzle mock replacement for tests that introspect the package's resolver outputs.