11import * as v from 'valibot'
22import { hash } from 'ohash'
3+ import { fetchNpmVersionDownloadsFromApi } from '#server/utils/npm-website-versions'
34
45/**
56 * Raw response from npm downloads API
@@ -15,6 +16,7 @@ interface NpmVersionDownloadsResponse {
1516 */
1617const QuerySchema = v . object ( {
1718 mode : v . optional ( v . picklist ( [ 'major' , 'minor' ] as const ) , 'major' ) ,
19+ packages : v . optional ( v . union ( [ v . string ( ) , v . array ( v . string ( ) ) ] ) ) ,
1820 filterThreshold : v . optional (
1921 v . pipe (
2022 v . string ( ) ,
@@ -25,10 +27,19 @@ const QuerySchema = v.object({
2527 filterOldVersions : v . optional ( v . picklist ( [ 'true' , 'false' ] as const ) , 'false' ) ,
2628} )
2729
30+ function normalizePackages ( packages : string | string [ ] | undefined ) : string [ ] {
31+ if ( ! packages ) return [ ]
32+
33+ const values = Array . isArray ( packages ) ? packages : [ packages ]
34+ return [ ...new Set ( values . flatMap ( value => value . split ( ',' ) . map ( pkg => pkg . trim ( ) ) ) . filter ( Boolean ) ) ]
35+ }
36+
2837/**
2938 * GET /api/registry/downloads/:name/versions or /api/registry/downloads/@scope/name/versions
39+ * GET /api/registry/downloads/versions?packages=pkg-a,pkg-b
3040 *
31- * Fetch per-version download statistics and group by major or minor version.
41+ * Fetch per-version download statistics and group by major or minor version,
42+ * or fetch raw per-version download lists for one or more packages.
3243 * Data is cached for 1 hour with stale-while-revalidate.
3344 *
3445 * Query parameters:
@@ -50,6 +61,47 @@ export default defineCachedEventHandler(
5061 } )
5162 }
5263
64+ try {
65+ const query = getQuery ( event )
66+ const parsed = v . parse ( QuerySchema , query )
67+ // Supports: /downloads/versions?packages=a,b and repeated ?packages=a&packages=b
68+ if ( pkgParamSegments . length === 1 ) {
69+ const packageNames = normalizePackages ( parsed . packages )
70+
71+ if ( packageNames . length === 0 ) {
72+ throw createError ( {
73+ statusCode : 400 ,
74+ message : 'At least one package is required via query `packages`' ,
75+ } )
76+ }
77+
78+ try {
79+ const packages = await Promise . all (
80+ packageNames . map ( async packageName => ( {
81+ packageName,
82+ versions : await fetchNpmVersionDownloadsFromApi ( packageName ) ,
83+ } ) ) ,
84+ )
85+
86+ return {
87+ packages,
88+ timestamp : new Date ( ) . toISOString ( ) ,
89+ }
90+ } catch ( error : unknown ) {
91+ handleApiError ( error , {
92+ statusCode : 502 ,
93+ message : 'Failed to fetch version download data from npm API' ,
94+ } )
95+ }
96+ }
97+ } catch ( error : unknown ) {
98+ handleApiError ( error , {
99+ statusCode : 502 ,
100+ message : 'Failed to fetch version download data from npm API' ,
101+ } )
102+ }
103+
104+
53105 const segments = pkgParamSegments . slice ( 0 , - 1 )
54106
55107 const { rawPackageName } = parsePackageParams ( segments )
0 commit comments