Skip to content

fix(core): refresh stale plugin cache#37688

Open
tobwen wants to merge 1 commit into
anomalyco:devfrom
tobwen:fix-plugin-latest-cache
Open

fix(core): refresh stale plugin cache#37688
tobwen wants to merge 1 commit into
anomalyco:devfrom
tobwen:fix-plugin-latest-cache

Conversation

@tobwen

@tobwen tobwen commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #25293

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Plugins declared with a floating @latest spec were pinned to the first installed version: Npm.add short-circuited on a directory-existence check (afs.existsSafe) and never re-resolved the dist-tag, so newer registry releases were silently ignored.

For mutable registry specs (@latest, ranges, bare names), Npm.add now reads the cached package.json version, fetches dist-tags.latest from the registry, and re-runs arborist.reify() when the registry version is newer. Pinned versions, aliases, and file/git/remote specs skip the check. AddOptions.refresh forces a re-resolve for pinned/file specs.

The registry fetch has a 5s AbortSignal.timeout and collapses any failure to undefined, so a down registry keeps the cached install. If the refresh reify itself fails (transient network error or lock contention), add falls back to the still-present cached entry point instead of throwing, so a refresh attempt does not break the plugin load.

This builds on #35777, which attempted the same dist-tags.latest comparison but threw InstallFailedError when the refresh reify failed, breaking the plugin load despite a working cache on disk. This PR wraps that refresh reify in Effect.catch to fall back to the cache, and adds the alias spec type to the immutable list.

How did you verify your code works?

  • bun typecheck (turbo, 30 packages): clean.
  • packages/core full suite: 1083 pass, including new tests for isMutableRegistrySpec (mutable, immutable, alias, invalid spec), shouldRefreshInstall (newer, equal, older, prerelease, missing, non-semver), a forced refresh that updates a cached file spec, and a fallback that returns the cached install when a @latest reify fails.
  • oxlint on changed files: 0 errors.

Screenshots / recordings

N/A (core only).

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Plugins declared with a floating `@latest` spec were pinned to the first
installed version: `Npm.add` short-circuited on a directory-existence check
and never re-resolved the dist-tag.

For mutable registry specs (`@latest`, ranges, bare names), compare the
cached version against registry `dist-tags.latest` and `reify` when newer.
Pinned versions, aliases, and file/git/remote specs skip the check. If the
refresh `reify` then fails (transient registry/network error, or lock
contention), fall back to the still-present cached install instead of
breaking the plugin load.

`AddOptions.refresh` forces a re-resolve for pinned/file specs.
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Related PR Found:

This PR is directly related. According to the PR description, #37688 builds on #35777 by addressing the same issue (stale @latest plugin cache) but improves the error handling. #35777 threw InstallFailedError when the refresh reify failed, breaking the plugin load. PR #37688 wraps that refresh in Effect.catch to fall back to the cache instead, making the refresh attempt non-breaking even if the registry is temporarily unavailable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plugin @latest cache can stay pinned to stale npm version

1 participant