@@ -20,9 +20,12 @@ let nextId = 0;
2020const EXACT_NAME_MATCH_BOOST = 10 ;
2121const TAG_MATCH_BOOST = 5 ;
2222const NAME_MATCH_BOOST = 3 ;
23- const DOWNLOADS_BOOST = 1.0 ; // Stronger boost for popular packages
24- const DEPENDENTS_BOOST = 1.5 ; // Higher weight for packages others depend on
23+ const DEPENDENTS_BOOST = 1.5 ; // Highest weight for packages others depend on
24+ const GITHUB_STARS_BOOST = 1.2 ; // Medium weight for GitHub stars (between dependents and downloads)
25+ const DOWNLOADS_BOOST = 1.0 ; // Lower weight for NPM downloads
2526const RECENT_UPDATE_BOOST = 0.2 ;
27+ const OUTDATED_PENALTY = - 10 ; // Substantial penalty for outdated packages
28+ const DEPRECATED_PENALTY = - 20 ; // Severe penalty for deprecated packages
2629
2730/**
2831 * Simple utility to strip HTML tags from text
@@ -73,7 +76,13 @@ export function init(packages: Package[]) {
7376/**
7477 * Sort criteria options for package search results
7578 */
76- export type SortCriterion = 'popularity' | 'downloads' | 'dependents' | 'updated' | 'name' ;
79+ export type SortCriterion =
80+ | 'popularity'
81+ | 'downloads'
82+ | 'dependents'
83+ | 'github_stars'
84+ | 'updated'
85+ | 'name' ;
7786
7887/**
7988 * Search for packages matching the query and/or tags, returning a flat list sorted by the specified criterion
@@ -170,24 +179,24 @@ export function search(
170179 score += NAME_MATCH_BOOST ;
171180 }
172181
173- // Boost popular packages
174- if ( pkg . downloads ) {
175- // Log scale for downloads to avoid domination by very popular packages
176- score += Math . log10 ( pkg . downloads + 1 ) * DOWNLOADS_BOOST ;
177- }
182+ // Apply a balanced scoring system with log scales
183+ const dependents = pkg . dependents || 0 ;
184+ const stars = pkg . github_stars || 0 ;
185+ const downloads = pkg . downloads || 0 ;
186+
187+ // Use logarithmic scales to prevent small numbers from dominating
188+ // Each metric gets its own weighted contribution
189+ score += Math . log10 ( dependents + 1 ) * 10 * DEPENDENTS_BOOST ;
190+ score += Math . log10 ( stars + 1 ) * 5 * GITHUB_STARS_BOOST ;
191+ score += Math . log10 ( downloads + 1 ) * DOWNLOADS_BOOST ;
178192
179- // Boost packages with many dependents
180- if ( pkg . dependents ) {
181- // Dependents are weighted more heavily as they indicate package reliability
182- score += Math . log10 ( pkg . dependents + 1 ) * 10 * DEPENDENTS_BOOST ;
193+ // Apply penalties for outdated or deprecated packages
194+ if ( pkg . outdated ) {
195+ score += OUTDATED_PENALTY ;
183196 }
184197
185- // Combined popularity score (packages with both downloads and dependents are prioritized)
186- if ( pkg . downloads && pkg . dependents ) {
187- // Main formula: Downloads × Dependents
188- // Using log to prevent extremely popular packages from completely dominating
189- const popularityFormula = pkg . downloads * pkg . dependents ;
190- score += Math . log10 ( popularityFormula + 1 ) * 2 ; // Higher weight for the formula
198+ if ( pkg . deprecated ) {
199+ score += DEPRECATED_PENALTY ;
191200 }
192201
193202 // Boost recently updated packages
@@ -243,17 +252,43 @@ export function search(
243252function sortPackages ( a : Package , b : Package , criterion : SortCriterion ) : number {
244253 switch ( criterion ) {
245254 case 'popularity' :
246- // Sort by Downloads × Dependents formula
247- const aPopularity = ( a . downloads || 1 ) * ( a . dependents || 1 ) ;
248- const bPopularity = ( b . downloads || 1 ) * ( b . dependents || 1 ) ;
249- return bPopularity - aPopularity ;
255+ // Create a balanced scoring system using logarithmic scales to prevent small numbers from dominating
256+ const aDependents = a . dependents || 0 ;
257+ const bDependents = b . dependents || 0 ;
258+ const aStars = a . github_stars || 0 ;
259+ const bStars = b . github_stars || 0 ;
260+ const aDownloads = a . downloads || 0 ;
261+ const bDownloads = b . downloads || 0 ;
262+
263+ // Use log scale with appropriate weights to maintain priority but prevent small values from dominating
264+ // Log base 10 with +1 to handle zeros, multiplied by importance factor
265+ let aScore =
266+ Math . log10 ( aDependents + 1 ) * 3 +
267+ Math . log10 ( aStars + 1 ) * 2 +
268+ Math . log10 ( aDownloads + 1 ) * 1 ;
269+
270+ let bScore =
271+ Math . log10 ( bDependents + 1 ) * 3 +
272+ Math . log10 ( bStars + 1 ) * 2 +
273+ Math . log10 ( bDownloads + 1 ) * 1 ;
274+
275+ // Apply penalties for outdated or deprecated packages
276+ if ( a . outdated ) aScore += OUTDATED_PENALTY ;
277+ if ( a . deprecated ) aScore += DEPRECATED_PENALTY ;
278+ if ( b . outdated ) bScore += OUTDATED_PENALTY ;
279+ if ( b . deprecated ) bScore += DEPRECATED_PENALTY ;
280+
281+ return bScore - aScore ;
250282
251283 case 'downloads' :
252284 return ( b . downloads || 0 ) - ( a . downloads || 0 ) ;
253285
254286 case 'dependents' :
255287 return ( b . dependents || 0 ) - ( a . dependents || 0 ) ;
256288
289+ case 'github_stars' :
290+ return ( b . github_stars || 0 ) - ( a . github_stars || 0 ) ;
291+
257292 case 'updated' :
258293 // Sort by most recently updated
259294 const aDate = a . updated ? new Date ( a . updated ) . getTime ( ) : 0 ;
@@ -265,10 +300,32 @@ function sortPackages(a: Package, b: Package, criterion: SortCriterion): number
265300 return a . name . localeCompare ( b . name ) ;
266301
267302 default :
268- // Default to popularity if an invalid criterion is provided
269- const aDefaultPop = ( a . downloads || 1 ) * ( a . dependents || 1 ) ;
270- const bDefaultPop = ( b . downloads || 1 ) * ( b . dependents || 1 ) ;
271- return bDefaultPop - aDefaultPop ;
303+ // Default to balanced popularity scoring if an invalid criterion is provided
304+ const aDef = a . dependents || 0 ;
305+ const bDef = b . dependents || 0 ;
306+ const aDefStars = a . github_stars || 0 ;
307+ const bDefStars = b . github_stars || 0 ;
308+ const aDefDownloads = a . downloads || 0 ;
309+ const bDefDownloads = b . downloads || 0 ;
310+
311+ // Use log scale with appropriate weights
312+ let aDefScore =
313+ Math . log10 ( aDef + 1 ) * 3 +
314+ Math . log10 ( aDefStars + 1 ) * 2 +
315+ Math . log10 ( aDefDownloads + 1 ) * 1 ;
316+
317+ let bDefScore =
318+ Math . log10 ( bDef + 1 ) * 3 +
319+ Math . log10 ( bDefStars + 1 ) * 2 +
320+ Math . log10 ( bDefDownloads + 1 ) * 1 ;
321+
322+ // Apply penalties for outdated or deprecated packages
323+ if ( a . outdated ) aDefScore += OUTDATED_PENALTY ;
324+ if ( a . deprecated ) aDefScore += DEPRECATED_PENALTY ;
325+ if ( b . outdated ) bDefScore += OUTDATED_PENALTY ;
326+ if ( b . deprecated ) bDefScore += DEPRECATED_PENALTY ;
327+
328+ return bDefScore - aDefScore ;
272329 }
273330}
274331
@@ -296,13 +353,21 @@ export function groupByTags(packages: Package[]): PackageGroup[] {
296353
297354 // Convert to final format and sort groups by highest package popularity
298355 return Object . values ( grouped ) . sort ( ( a , b ) => {
299- // Sort groups by highest package popularity
300- const maxPopularityA = Math . max (
301- ...a . packages . map ( ( p ) => ( p . downloads || 1 ) * ( p . dependents || 1 ) )
302- ) ;
303- const maxPopularityB = Math . max (
304- ...b . packages . map ( ( p ) => ( p . downloads || 1 ) * ( p . dependents || 1 ) )
305- ) ;
356+ // Calculate balanced popularity scores with logarithmic scaling
357+ const getPopularity = ( pkg : Package ) => {
358+ const dependents = pkg . dependents || 0 ;
359+ const stars = pkg . github_stars || 0 ;
360+ const downloads = pkg . downloads || 1 ;
361+
362+ // Use balanced logarithmic formula with proper weighting
363+ return (
364+ Math . log10 ( dependents + 1 ) * 3 + Math . log10 ( stars + 1 ) * 2 + Math . log10 ( downloads + 1 ) * 1
365+ ) ;
366+ } ;
367+
368+ const maxPopularityA = Math . max ( ...a . packages . map ( getPopularity ) ) ;
369+ const maxPopularityB = Math . max ( ...b . packages . map ( getPopularity ) ) ;
370+
306371 return maxPopularityB - maxPopularityA ;
307372 } ) ;
308373}
@@ -321,20 +386,6 @@ export function getAllPackages(): Package[] {
321386 return Array . from ( packagesMap . values ( ) ) ;
322387}
323388
324- /**
325- * Get packages by tag
326- */
327- export function getPackagesByTag ( tag : string ) : Package [ ] {
328- return Array . from ( packagesMap . values ( ) )
329- . filter ( ( pkg ) => pkg . tags && pkg . tags . includes ( tag ) )
330- . sort ( ( a , b ) => {
331- // Sort by Downloads × Dependents formula
332- const aFormula = ( a . downloads || 1 ) * ( a . dependents || 1 ) ;
333- const bFormula = ( b . downloads || 1 ) * ( b . dependents || 1 ) ;
334- return bFormula - aFormula ;
335- } ) ;
336- }
337-
338389/**
339390 * Get all available tags with package counts
340391 */
@@ -361,9 +412,25 @@ export function getPackagesByAuthor(author: string): Package[] {
361412 return Array . from ( packagesMap . values ( ) )
362413 . filter ( ( pkg ) => pkg . author === author )
363414 . sort ( ( a , b ) => {
364- // Sort by Downloads × Dependents formula
365- const aFormula = ( a . downloads || 1 ) * ( a . dependents || 1 ) ;
366- const bFormula = ( b . downloads || 1 ) * ( b . dependents || 1 ) ;
367- return bFormula - aFormula ;
415+ // Sort with balanced logarithmic scoring
416+ const aDependents = a . dependents || 0 ;
417+ const bDependents = b . dependents || 0 ;
418+ const aStars = a . github_stars || 0 ;
419+ const bStars = b . github_stars || 0 ;
420+ const aDownloads = a . downloads || 1 ;
421+ const bDownloads = b . downloads || 1 ;
422+
423+ // Calculate balanced scores using logarithmic scale
424+ const aScore =
425+ Math . log10 ( aDependents + 1 ) * 3 +
426+ Math . log10 ( aStars + 1 ) * 2 +
427+ Math . log10 ( aDownloads + 1 ) * 1 ;
428+
429+ const bScore =
430+ Math . log10 ( bDependents + 1 ) * 3 +
431+ Math . log10 ( bStars + 1 ) * 2 +
432+ Math . log10 ( bDownloads + 1 ) * 1 ;
433+
434+ return bScore - aScore ;
368435 } ) ;
369436}
0 commit comments