dep-radius

How it works

The steps from your project folder to an answer. No AI, just your code, the type files and the release notes.

radius gives the same answer every time for the same project, versions and release notes. There is no AI, no account and no server of ours involved.

1. Find your dependencies

radius reads every package.json in the project. It skips build folders like dist and .next.

For each dependency, it finds the installed version in this order:

  1. node_modules, the way Node finds it, including pnpm and workspace links.
  2. The lockfile: package-lock.json, pnpm-lock.yaml, yarn.lock (classic, berry, Plug'n'Play) or bun.lock.
  3. The lowest version the range allows. This is a guess, so radius flags it and never says quiet.

A lockfile written for a different version than your package.json asks for is out of date, and so is a node_modules that doesn't match an up-to-date lockfile. That happens when a pull request bumps a version without updating the lockfile, or before you install again. radius skips the out-of-date source, reads the next one, says which version it skipped (out-of-sync), and never calls that update quiet.

Your own workspace packages (workspace:, link:, file:) are skipped. Packages installed from git or a URL are listed as not analysed.

2. Pick the version to compare with

By default, radius picks the newest version on your major (or your minor, for 0.x). That's what a ^ range allows.

It skips prereleases, deprecated versions, versions above the latest tag, and versions that are too new. "Too new" means younger than:

  • --min-age, if you set it,
  • or your project's minimumReleaseAge (in pnpm-workspace.yaml) or minimum-release-age (in .npmrc),
  • or one day.

A newer major shows up as "also available", but isn't checked. --latest or package@version picks another target. --since uses the version in your working tree.

3. Read your code

radius reads .js, .ts, .jsx, .tsx, .mjs, .cjs files, and the scripts in .vue, .svelte and .astro files. It notes every import, require and import(), and what you do with them. For example, z.string().email() uses the names string and email.

It also follows:

  • your files that re-export a package, like export * from "zod" in src/lib/zod.ts,
  • paths in tsconfig.json or jsconfig.json,
  • workspace packages that re-export it,
  • values made from the package in one file and used in another.

Comments and strings don't count. Only real code does.

4. Compare the types

radius downloads both versions, checks their integrity, and reads only their .d.ts files, in memory. It compares every export with its own copy of TypeScript. Your node_modules and your TypeScript version don't matter, so the result is the same on every machine.

Each export is marked unchanged, added, widened (for example a new optional parameter), changed, deprecated or removed. Only changes to names your code uses count.

5. Read the release notes

For every version between yours and the target, radius takes the first notes it finds:

  1. a changelog inside the package,
  2. the GitHub release for that version,
  3. the CHANGELOG.md in the repository.

It splits the notes into entries and sorts them:

The entryWhat radius does
Names something your code usesShows it, with your lines
Describes a change it can't link to your codeLists it, and the update can't be quiet
Housekeeping: docs, tests, CI, dependency updates, thanksIgnores it
Adds something new: a feature, an option, a speed-upIgnores it

How an entry "names" something:

  • A function you call. email() in a note matches your z.email(). The plain word "email" in a sentence doesn't.
  • An option. "Default quiet to false" matches your dotenv.config() call, because the types say config() accepts quiet. Without types, radius uses the keys your code passes, like limit in bodyParser.json({ limit }). Options match when written as code (quiet, retry.methods) or as the header they set (Content-Security-Policy for contentSecurityPolicy). These matches are marked "possibly".
  • A commit title. diff: fix prerelease to stable names diff.

If a name appears in the code examples of a quarter of all entries, only its mentions outside examples count. Otherwise every z.object() example would match everything.

6. Decide

The type check and the notes check stay separate. The rules on Quiet, review, blocked turn them into one answer.

The cache

radius saves everything it downloads: package information, package files, compared types, and GitHub release lists (for an hour). A second run mostly reads from the cache.

PlatformWhere
Linux$XDG_CACHE_HOME/dep-radius, or ~/.cache/dep-radius
macOS~/Library/Caches/dep-radius
Windows%LOCALAPPDATA%\dep-radius\Cache

Use RADIUS_CACHE_DIR or --cache-dir to put it somewhere else. Deleting it is always safe.

What leaves your machine

Your code never does. radius only calls:

AddressFor
your npm registry (npmjs.org by default)package information and package files
api.github.comrelease notes, with your token if you have one
raw.githubusercontent.comCHANGELOG.md files

The registry and its token come from your .npmrc. The GitHub token is only sent to api.github.com.

On this page