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:
node_modules, the way Node finds it, including pnpm and workspace links.- The lockfile:
package-lock.json,pnpm-lock.yaml,yarn.lock(classic, berry, Plug'n'Play) orbun.lock. - 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(inpnpm-workspace.yaml) orminimum-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"insrc/lib/zod.ts, pathsintsconfig.jsonorjsconfig.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:
- a changelog inside the package,
- the GitHub release for that version,
- the
CHANGELOG.mdin the repository.
It splits the notes into entries and sorts them:
| The entry | What radius does |
|---|---|
| Names something your code uses | Shows it, with your lines |
| Describes a change it can't link to your code | Lists it, and the update can't be quiet |
| Housekeeping: docs, tests, CI, dependency updates, thanks | Ignores it |
| Adds something new: a feature, an option, a speed-up | Ignores it |
How an entry "names" something:
- A function you call.
email()in a note matches yourz.email(). The plain word "email" in a sentence doesn't. - An option. "Default
quietto false" matches yourdotenv.config()call, because the types sayconfig()acceptsquiet. Without types, radius uses the keys your code passes, likelimitinbodyParser.json({ limit }). Options match when written as code (quiet,retry.methods) or as the header they set (Content-Security-PolicyforcontentSecurityPolicy). These matches are marked "possibly". - A commit title.
diff: fix prerelease to stablenamesdiff.
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.
| Platform | Where |
|---|---|
| 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:
| Address | For |
|---|---|
| your npm registry (npmjs.org by default) | package information and package files |
api.github.com | release notes, with your token if you have one |
raw.githubusercontent.com | CHANGELOG.md files |
The registry and its token come from your .npmrc. The GitHub token is only sent to api.github.com.