@@ -33,42 +33,46 @@ export interface PackageComparisonData {
3333export function usePackageComparison ( packageNames : MaybeRefOrGetter < string [ ] > ) {
3434 const packages = computed ( ( ) => toValue ( packageNames ) )
3535
36- // Reactive state
37- const packagesData = ref < ( PackageComparisonData | null ) [ ] > ( [ ] )
36+ // Cache of fetched data by package name (source of truth)
37+ const cache = shallowRef ( new Map < string , PackageComparisonData > ( ) )
38+
39+ // Derived array in current package order
40+ const packagesData = computed ( ( ) => packages . value . map ( name => cache . value . get ( name ) ?? null ) )
41+
3842 const status = ref < 'idle' | 'pending' | 'success' | 'error' > ( 'idle' )
3943 const error = ref < Error | null > ( null )
4044
45+ // Track which packages are currently being fetched
46+ const loadingPackages = shallowRef ( new Set < string > ( ) )
47+
4148 // Track install size loading separately (it's slower)
4249 const installSizeLoading = ref ( false )
4350
44- // Track last fetched packages to avoid redundant fetches
45- let lastFetchedPackages : string [ ] = [ ]
46-
47- // Fetch function
51+ // Fetch function - only fetches packages not already in cache
4852 async function fetchPackages ( names : string [ ] ) {
4953 if ( names . length === 0 ) {
50- packagesData . value = [ ]
5154 status . value = 'idle'
52- lastFetchedPackages = [ ]
5355 return
5456 }
5557
56- // Skip fetch if packages haven't actually changed
57- if (
58- names . length === lastFetchedPackages . length &&
59- names . every ( ( n , i ) => n === lastFetchedPackages [ i ] )
60- ) {
58+ // Only fetch packages not already cached
59+ const namesToFetch = names . filter ( name => ! cache . value . has ( name ) )
60+
61+ if ( namesToFetch . length === 0 ) {
62+ status . value = 'success'
6163 return
6264 }
6365
64- lastFetchedPackages = [ ...names ]
6566 status . value = 'pending'
6667 error . value = null
6768
69+ // Mark packages as loading
70+ loadingPackages . value = new Set ( namesToFetch )
71+
6872 try {
6973 // First pass: fetch fast data (package info, downloads, analysis, vulns)
7074 const results = await Promise . all (
71- names . map ( async ( name ) : Promise < PackageComparisonData | null > => {
75+ namesToFetch . map ( async ( name ) : Promise < PackageComparisonData | null > => {
7276 try {
7377 // Fetch basic package info first (required)
7478 const pkgData = await $fetch < {
@@ -131,26 +135,35 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
131135 } ) ,
132136 )
133137
134- packagesData . value = results
138+ // Add results to cache
139+ const newCache = new Map ( cache . value )
140+ for ( const [ i , name ] of namesToFetch . entries ( ) ) {
141+ const data = results [ i ]
142+ if ( data ) {
143+ newCache . set ( name , data )
144+ }
145+ }
146+ cache . value = newCache
147+ loadingPackages . value = new Set ( )
135148 status . value = 'success'
136149
137- // Second pass: fetch slow install size data in background
150+ // Second pass: fetch slow install size data in background for new packages
138151 installSizeLoading . value = true
139152 Promise . all (
140- names . map ( async ( name , index ) => {
153+ namesToFetch . map ( async name => {
141154 try {
142155 const installSize = await $fetch < {
143156 selfSize : number
144157 totalSize : number
145158 dependencyCount : number
146159 } > ( `/api/registry/install-size/${ name } ` )
147160
148- // Update the specific package's install size
149- if ( packagesData . value [ index ] ) {
150- packagesData . value [ index ] = {
151- ... packagesData . value [ index ] ! ,
152- installSize ,
153- }
161+ // Update cache with install size
162+ const existing = cache . value . get ( name )
163+ if ( existing ) {
164+ const updated = new Map ( cache . value )
165+ updated . set ( name , { ... existing , installSize } )
166+ cache . value = updated
154167 }
155168 } catch {
156169 // Install size fetch failed, leave as undefined
@@ -160,6 +173,7 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
160173 installSizeLoading . value = false
161174 } )
162175 } catch ( e ) {
176+ loadingPackages . value = new Set ( )
163177 error . value = e as Error
164178 status . value = 'error'
165179 }
@@ -193,12 +207,19 @@ export function usePackageComparison(packageNames: MaybeRefOrGetter<string[]>) {
193207 return facet === 'installSize' || facet === 'dependencies'
194208 }
195209
210+ // Check if a specific column (package) is loading
211+ function isColumnLoading ( index : number ) : boolean {
212+ const name = packages . value [ index ]
213+ return name ? loadingPackages . value . has ( name ) : false
214+ }
215+
196216 return {
197217 packagesData : readonly ( packagesData ) ,
198218 status : readonly ( status ) ,
199219 error : readonly ( error ) ,
200220 getFacetValues,
201221 isFacetLoading,
222+ isColumnLoading,
202223 }
203224}
204225
0 commit comments