Skip to content

fix(intent): read local package.json version before npm registry fallback#104

Open
RazorAlexMachin wants to merge 1 commit intoTanStack:mainfrom
RazorAlexMachin:feat-local-version-fallback
Open

fix(intent): read local package.json version before npm registry fallback#104
RazorAlexMachin wants to merge 1 commit intoTanStack:mainfrom
RazorAlexMachin:feat-local-version-fallback

Conversation

@RazorAlexMachin
Copy link
Copy Markdown

@RazorAlexMachin RazorAlexMachin commented Mar 30, 2026

🎯 Changes

Read the local package.json version in checkStaleness before falling back to the npm registry fetch. This fixes version drift detection for packages not published to public registry.npmjs.org (e.g. GitHub Packages, Artifactory, private registries).

Fixes #103

Problem

fetchNpmVersion() hardcodes https://registry.npmjs.org/ — any scoped/private package not on the public registry returns a 404, causing currentVersion to always be null and silently disabling version drift detection.

Solution

  • Add readLocalVersion(packageDir) — reads version from the local package.json
  • Add fetchCurrentVersion(packageDir, packageName) — tries local first, falls back to npm registry
  • Update checkStaleness() to use the new fallback chain

This works for both use cases:

  • Maintainer workspace: reads package.json directly (works for all registry types)
  • Consumer node_modules: reads package.json from the installed package directory

Tests

Two new test cases added to staleness.test.ts:

  1. reads version from local package.json when npm fetch fails — simulates private package (npm 404)
  2. prefers local package.json over npm registry — verifies local takes precedence

All 17 tests pass.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm vitest run packages/intent/tests/staleness.test.ts.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Bug Fixes
    • Fixed version drift detection for packages in private or alternative registries by checking local package version first before falling back to npm registry lookup.

…back

Fixes TanStack#103 — fetchNpmVersion hardcodes registry.npmjs.org, which returns
404 for private/scoped packages (GitHub Packages, Artifactory, etc.),
breaking version drift detection.

Changes:
- Add readLocalVersion() to read package.json from packageDir
- Add fetchCurrentVersion() that tries local first, then npm registry
- Update checkStaleness() to use the new fallback chain
- Add test: private package with npm 404 → reads local version
- Add test: local package.json takes precedence over npm registry
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The pull request implements a two-step version resolution strategy in the staleness checker. The fetchCurrentVersion function now attempts to read the local package.json version first, then falls back to npm registry queries only when necessary. This enables version drift detection for packages in private registries.

Changes

Cohort / File(s) Summary
Changeset Metadata
.changeset/fix-local-version-fallback.md
Added changeset entry documenting a patch bump for @tanstack/intent addressing local version fallback in intent stale.
Version Resolution Logic
packages/intent/src/staleness.ts
Modified fetchCurrentVersion to read local package.json version via readLocalVersion before querying npm registry; checkStaleness now uses this resolved version for drift detection.
Test Coverage
packages/intent/tests/staleness.test.ts
Added two new test cases: one verifying local version is used when npm fetch fails, another confirming local version takes precedence over registry data when both exist.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A hop and a skip, we fetch the local way,
Read package.json before npm's say,
Private registries now drift no more,
Version detection unlocks the door! ✨📦

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: reading local package.json version before falling back to npm registry, directly addressing the core fix.
Linked Issues check ✅ Passed The PR fully addresses issue #103 by implementing local package.json reading with npm registry fallback, restoring version drift detection for private/scoped packages.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the objective: implementing local version reading with registry fallback in staleness detection, plus corresponding tests and changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description is comprehensive and follows the template structure. It includes a clear description of changes with motivation, completed checklists, and release impact confirmation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/intent/src/staleness.ts (1)

45-45: Guard against empty local version values before suppressing fallback.

A blank string currently counts as a valid local version, which can skip npm fallback and produce unreliable drift classification.

Proposed small hardening
-    return typeof pkgJson.version === 'string' ? pkgJson.version : null
+    return typeof pkgJson.version === 'string' && pkgJson.version.trim() !== ''
+      ? pkgJson.version
+      : null
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/intent/src/staleness.ts` at line 45, The current return treats an
empty string as a valid local version (returning pkgJson.version) which can
suppress the npm fallback; update the check where pkgJson.version is returned to
ensure it is a non-empty string (e.g., typeof pkgJson.version === 'string' &&
pkgJson.version.trim().length > 0) and return null if the version is empty or
whitespace so downstream drift classification will fall back to npm; target the
return handling of pkgJson.version in packages/intent/src/staleness.ts (the
place that currently does "return typeof pkgJson.version === 'string' ?
pkgJson.version : null").
packages/intent/tests/staleness.test.ts (1)

325-333: Assert fetch is not called when local package.json version exists.

This tightens the contract from “local wins” to “local short-circuits network fallback.”

Proposed assertion additions
   const report = await checkStaleness(tmpDir, '@private/lib')
   expect(report.currentVersion).toBe('2.5.0')
   expect(report.versionDrift).toBe('minor')
+  expect(globalThis.fetch).not.toHaveBeenCalled()
   const skill = requireFirstSkill(report)
   expect(skill.needsReview).toBe(true)
   expect(skill.reasons[0]).toContain('version drift')
@@
   const report = await checkStaleness(tmpDir, '@example/lib')
   // Local package.json should take precedence
   expect(report.currentVersion).toBe('3.0.0')
   expect(report.versionDrift).toBe('major')
+  expect(globalThis.fetch).not.toHaveBeenCalled()

Also applies to: 347-354

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/intent/tests/staleness.test.ts` around lines 325 - 333, The test
should assert the network fetch is never invoked when a local package.json
version exists: after calling checkStaleness(tmpDir, '@private/lib') (the test
using mockFetchNotOk and requireFirstSkill), add an assertion that the mocked
fetch function was not called (e.g., expect(fetchMock).not.toHaveBeenCalled() or
the project's equivalent mock used by mockFetchNotOk). Do the same addition in
the other similar test block (around the other checkStaleness case at lines
347-354) to ensure the local short-circuits network fallback behavior is
enforced; reference the mock helper mockFetchNotOk and the function under test
checkStaleness to locate where to place the new assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/intent/src/staleness.ts`:
- Line 45: The current return treats an empty string as a valid local version
(returning pkgJson.version) which can suppress the npm fallback; update the
check where pkgJson.version is returned to ensure it is a non-empty string
(e.g., typeof pkgJson.version === 'string' && pkgJson.version.trim().length > 0)
and return null if the version is empty or whitespace so downstream drift
classification will fall back to npm; target the return handling of
pkgJson.version in packages/intent/src/staleness.ts (the place that currently
does "return typeof pkgJson.version === 'string' ? pkgJson.version : null").

In `@packages/intent/tests/staleness.test.ts`:
- Around line 325-333: The test should assert the network fetch is never invoked
when a local package.json version exists: after calling checkStaleness(tmpDir,
'@private/lib') (the test using mockFetchNotOk and requireFirstSkill), add an
assertion that the mocked fetch function was not called (e.g.,
expect(fetchMock).not.toHaveBeenCalled() or the project's equivalent mock used
by mockFetchNotOk). Do the same addition in the other similar test block (around
the other checkStaleness case at lines 347-354) to ensure the local
short-circuits network fallback behavior is enforced; reference the mock helper
mockFetchNotOk and the function under test checkStaleness to locate where to
place the new assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8087092-67f4-4868-9ba3-f331c2d69675

📥 Commits

Reviewing files that changed from the base of the PR and between 2f29aa2 and 6b4b8f5.

📒 Files selected for processing (3)
  • .changeset/fix-local-version-fallback.md
  • packages/intent/src/staleness.ts
  • packages/intent/tests/staleness.test.ts

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 30, 2026

View your CI Pipeline Execution ↗ for commit 6b4b8f5

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-30 18:54:16 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 30, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@tanstack/intent@104

commit: 6b4b8f5

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

intent stale returns null currentVersion for private/scoped packages

2 participants