From 3c5bcf6d86c70e1c24382b3af4c23c28b5d4c2fa Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 25 Feb 2026 10:00:15 +0000 Subject: [PATCH] perf: improve caching of packuments --- app/composables/usePackageComparison.ts | 5 +- server/utils/dependency-resolver.ts | 32 +++++------- .../use-package-comparison.spec.ts | 49 ++++++++++--------- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/app/composables/usePackageComparison.ts b/app/composables/usePackageComparison.ts index aa03fbae3b..b0b8818f1d 100644 --- a/app/composables/usePackageComparison.ts +++ b/app/composables/usePackageComparison.ts @@ -70,6 +70,7 @@ export interface PackageComparisonData { */ export function usePackageComparison(packageNames: MaybeRefOrGetter) { const { t } = useI18n() + const { $npmRegistry } = useNuxtApp() const numberFormatter = useNumberFormatter() const compactNumberFormatter = useCompactNumberFormatter() const bytesFormatter = useBytesFormatter() @@ -124,9 +125,7 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter) { namesToFetch.map(async (name): Promise => { try { // Fetch basic package info first (required) - const pkgData = await $fetch( - `https://registry.npmjs.org/${encodePackageName(name)}`, - ) + const { data: pkgData } = await $npmRegistry(`/${encodePackageName(name)}`) const latestVersion = pkgData['dist-tags']?.latest if (!latestVersion) return null diff --git a/server/utils/dependency-resolver.ts b/server/utils/dependency-resolver.ts index 2b89a3c87a..327299a538 100644 --- a/server/utils/dependency-resolver.ts +++ b/server/utils/dependency-resolver.ts @@ -1,6 +1,5 @@ import type { Packument, PackumentVersion, DependencyDepth } from '#shared/types' import { mapWithConcurrency } from '#shared/utils/async' -import { encodePackageName } from '#shared/utils/npm' import { maxSatisfying } from 'semver' /** Concurrency limit for fetching packuments during dependency resolution */ @@ -17,27 +16,20 @@ export const TARGET_PLATFORM = { } /** - * Fetch packument with caching (returns null on error for tree traversal) + * Fetch packument with caching (returns null on error for tree traversal). + * Delegates to fetchNpmPackage() to share a single cache for all packument fetches. */ -export const fetchPackument = defineCachedFunction( - async (name: string): Promise => { - try { - return await $fetch(`https://registry.npmjs.org/${encodePackageName(name)}`) - } catch (error) { - if (import.meta.dev) { - // oxlint-disable-next-line no-console -- log npm registry failures for debugging - console.warn(`[dep-resolver] Failed to fetch packument for ${name}:`, error) - } - return null +async function fetchPackument(name: string): Promise { + try { + return await fetchNpmPackage(name) + } catch (error) { + if (import.meta.dev) { + // oxlint-disable-next-line no-console -- log npm registry failures for debugging + console.warn(`[dep-resolver] Failed to fetch packument for ${name}:`, error) } - }, - { - maxAge: 60 * 60, - swr: true, - name: 'packument', - getKey: (name: string) => name, - }, -) + return null + } +} /** * Check if a package version matches the target platform. diff --git a/test/nuxt/composables/use-package-comparison.spec.ts b/test/nuxt/composables/use-package-comparison.spec.ts index f1e47398ed..071e1d354c 100644 --- a/test/nuxt/composables/use-package-comparison.spec.ts +++ b/test/nuxt/composables/use-package-comparison.spec.ts @@ -48,24 +48,26 @@ describe('usePackageComparison', () => { describe('lastUpdated facet', () => { it('uses version-specific publish date, not time.modified', async () => { + const registryData = { + 'name': 'test-package', + 'dist-tags': { latest: '2.0.0' }, + 'time': { + // This is the WRONG value - updated by metadata changes + 'modified': '2024-12-01T00:00:00.000Z', + // This is the CORRECT value - actual publish date + '2.0.0': '2024-06-15T00:00:00.000Z', + }, + 'license': 'MIT', + 'versions': { + '2.0.0': { dist: { unpackedSize: 15000 } }, + }, + } vi.stubGlobal( '$fetch', - vi.fn().mockImplementation((url: string) => { - if (url.startsWith('https://registry.npmjs.org/')) { - return Promise.resolve({ - 'name': 'test-package', - 'dist-tags': { latest: '2.0.0' }, - 'time': { - // This is the WRONG value - updated by metadata changes - 'modified': '2024-12-01T00:00:00.000Z', - // This is the CORRECT value - actual publish date - '2.0.0': '2024-06-15T00:00:00.000Z', - }, - 'license': 'MIT', - 'versions': { - '2.0.0': { dist: { unpackedSize: 15000 } }, - }, - }) + vi.fn().mockImplementation((url: string, options?: { baseURL?: string }) => { + const fullUrl = options?.baseURL ? `${options.baseURL}${url}` : url + if (fullUrl.startsWith('https://registry.npmjs.org/')) { + return Promise.resolve(registryData) } return Promise.resolve(null) }), @@ -93,8 +95,9 @@ describe('usePackageComparison', () => { it('stores version-specific time in metadata', async () => { vi.stubGlobal( '$fetch', - vi.fn().mockImplementation((url: string) => { - if (url.startsWith('https://registry.npmjs.org/')) { + vi.fn().mockImplementation((url: string, options?: { baseURL?: string }) => { + const fullUrl = options?.baseURL ? `${options.baseURL}${url}` : url + if (fullUrl.startsWith('https://registry.npmjs.org/')) { return Promise.resolve({ 'name': 'test-package', 'dist-tags': { latest: '1.0.0' }, @@ -128,8 +131,9 @@ describe('usePackageComparison', () => { it('marks packages not published in 2+ years as stale', async () => { vi.stubGlobal( '$fetch', - vi.fn().mockImplementation((url: string) => { - if (url.startsWith('https://registry.npmjs.org/')) { + vi.fn().mockImplementation((url: string, options?: { baseURL?: string }) => { + const fullUrl = options?.baseURL ? `${options.baseURL}${url}` : url + if (fullUrl.startsWith('https://registry.npmjs.org/')) { return Promise.resolve({ 'name': 'old-package', 'dist-tags': { latest: '1.0.0' }, @@ -159,8 +163,9 @@ describe('usePackageComparison', () => { it('marks recently published packages as neutral', async () => { vi.stubGlobal( '$fetch', - vi.fn().mockImplementation((url: string) => { - if (url.startsWith('https://registry.npmjs.org/')) { + vi.fn().mockImplementation((url: string, options?: { baseURL?: string }) => { + const fullUrl = options?.baseURL ? `${options.baseURL}${url}` : url + if (fullUrl.startsWith('https://registry.npmjs.org/')) { return Promise.resolve({ 'name': 'fresh-package', 'dist-tags': { latest: '1.0.0' },