11import type {
22 Packument ,
3- PackumentVersion ,
43 SlimPackument ,
54 NpmSearchResponse ,
65 NpmSearchResult ,
76 NpmDownloadCount ,
87 NpmPerson ,
98 PackageVersionInfo ,
109} from '#shared/types'
10+ import { getVersions } from 'fast-npm-meta'
11+ import type { ResolvedPackageVersion } from 'fast-npm-meta'
1112import type { ReleaseType } from 'semver'
1213import { mapWithConcurrency } from '#shared/utils/async'
1314import { maxSatisfying , prerelease , major , minor , diff , gt , compare } from 'semver'
14- import { isExactVersion } from '~/utils/versions'
1515import { extractInstallScriptsInfo } from '~/utils/install-scripts'
1616import type { CachedFetchFunction } from '#shared/utils/fetch-cache-config'
1717
@@ -93,17 +93,6 @@ async function fetchBulkDownloads(
9393 return downloads
9494}
9595
96- /**
97- * Encode a package name for use in npm registry URLs.
98- * Handles scoped packages (e.g., @scope/name -> @scope%2Fname).
99- */
100- export function encodePackageName ( name : string ) : string {
101- if ( name . startsWith ( '@' ) ) {
102- return `@${ encodeURIComponent ( name . slice ( 1 ) ) } `
103- }
104- return encodeURIComponent ( name )
105- }
106-
10796/** Number of recent versions to include in initial payload */
10897const RECENT_VERSIONS_COUNT = 5
10998
@@ -138,20 +127,28 @@ function transformPackument(pkg: Packument, requestedVersion?: string | null): S
138127 }
139128
140129 // Build filtered versions object with install scripts info per version
141- const filteredVersions : Record < string , PackumentVersion > = { }
130+ const filteredVersions : Record < string , SlimVersion > = { }
131+ let versionData : SlimPackumentVersion | null = null
142132 for ( const v of includedVersions ) {
143133 const version = pkg . versions [ v ]
144134 if ( version ) {
145- // Strip readme from each version, extract install scripts info
146- const { readme : _readme , scripts, ...slimVersion } = version
147-
148- // Extract install scripts info (which scripts exist + npx deps)
149- const installScripts = scripts ? extractInstallScriptsInfo ( scripts ) : null
150-
135+ if ( version . version === requestedVersion ) {
136+ // Strip readme from each version, extract install scripts info
137+ const { readme : _readme , scripts, ...slimVersion } = version
138+
139+ // Extract install scripts info (which scripts exist + npx deps)
140+ const installScripts = scripts ? extractInstallScriptsInfo ( scripts ) : null
141+ versionData = {
142+ ...slimVersion ,
143+ installScripts : installScripts ?? undefined ,
144+ }
145+ }
151146 filteredVersions [ v ] = {
152- ...slimVersion ,
153- installScripts : installScripts ?? undefined ,
154- } as PackumentVersion
147+ ...( ( version ?. dist as { attestations ?: unknown } ) ? { hasProvenance : true } : { } ) ,
148+ version : version . version ,
149+ deprecated : version . deprecated ,
150+ tags : version . tags as string [ ] ,
151+ }
155152 }
156153 }
157154
@@ -177,10 +174,28 @@ function transformPackument(pkg: Packument, requestedVersion?: string | null): S
177174 'keywords' : pkg . keywords ,
178175 'repository' : pkg . repository ,
179176 'bugs' : pkg . bugs ,
177+ 'requestedVersion' : versionData ,
180178 'versions' : filteredVersions ,
181179 }
182180}
183181
182+ export function useResolvedVersion (
183+ packageName : MaybeRefOrGetter < string > ,
184+ requestedVersion : MaybeRefOrGetter < string | null > ,
185+ ) {
186+ return useFetch (
187+ ( ) => {
188+ const version = toValue ( requestedVersion )
189+ return version
190+ ? `https://npm.antfu.dev/${ toValue ( packageName ) } @${ version } `
191+ : `https://npm.antfu.dev/${ toValue ( packageName ) } `
192+ } ,
193+ {
194+ transform : ( data : ResolvedPackageVersion ) => data . version ,
195+ } ,
196+ )
197+ }
198+
184199export function usePackage (
185200 name : MaybeRefOrGetter < string > ,
186201 requestedVersion ?: MaybeRefOrGetter < string | null > ,
@@ -196,8 +211,7 @@ export function usePackage(
196211 } )
197212 const reqVer = toValue ( requestedVersion )
198213 const pkg = transformPackument ( r , reqVer )
199- const resolvedVersion = getResolvedVersion ( pkg , reqVer )
200- return { ...pkg , resolvedVersion, isStale }
214+ return { ...pkg , isStale }
201215 } ,
202216 )
203217
@@ -210,26 +224,6 @@ export function usePackage(
210224 return asyncData
211225}
212226
213- function getResolvedVersion ( pkg : SlimPackument , reqVer ?: string | null ) : string | null {
214- if ( ! pkg || ! reqVer ) return null
215-
216- // 1. Check if it's already an exact version in pkg.versions
217- if ( isExactVersion ( reqVer ) && pkg . versions [ reqVer ] ) {
218- return reqVer
219- }
220-
221- // 2. Check if it's a dist-tag (latest, next, beta, etc.)
222- const tagVersion = pkg [ 'dist-tags' ] ?. [ reqVer ]
223- if ( tagVersion ) {
224- return tagVersion
225- }
226-
227- // 3. Try to resolve as a semver range
228- const versions = Object . keys ( pkg . versions )
229- const resolved = maxSatisfying ( versions , reqVer )
230- return resolved
231- }
232-
233227export function usePackageDownloads (
234228 name : MaybeRefOrGetter < string > ,
235229 period : MaybeRefOrGetter < 'last-day' | 'last-week' | 'last-month' | 'last-year' > = 'last-week' ,
@@ -600,32 +594,28 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
600594const allVersionsCache = new Map < string , Promise < PackageVersionInfo [ ] > > ( )
601595
602596/**
603- * Fetch all versions of a package from the npm registry .
597+ * Fetch all versions of a package using fast- npm-meta API .
604598 * Returns version info sorted by version (newest first).
605599 * Results are cached to avoid duplicate requests.
606600 *
607601 * Note: This is a standalone async function for use in event handlers.
608602 * For composable usage, use useAllPackageVersions instead.
603+ *
604+ * @see https://github.com/antfu/fast-npm-meta
609605 */
610606export async function fetchAllPackageVersions ( packageName : string ) : Promise < PackageVersionInfo [ ] > {
611607 const cached = allVersionsCache . get ( packageName )
612608 if ( cached ) return cached
613609
614610 const promise = ( async ( ) => {
615- const encodedName = encodePackageName ( packageName )
616- // Use regular $fetch for client-side calls (this is called on user interaction)
617- const data = await $fetch < {
618- versions : Record < string , { deprecated ?: string } >
619- time : Record < string , string >
620- } > ( `${ NPM_REGISTRY } /${ encodedName } ` )
621-
622- return Object . entries ( data . versions )
623- . filter ( ( [ v ] ) => data . time [ v ] )
624- . map ( ( [ version , versionData ] ) => ( {
611+ const data = await getVersions ( packageName , { metadata : true } )
612+
613+ return Object . entries ( data . versionsMeta )
614+ . map ( ( [ version , meta ] ) => ( {
625615 version,
626- time : data . time [ version ] ,
627- hasProvenance : false , // Would need to check dist.attestations for each version
628- deprecated : versionData . deprecated ,
616+ time : meta . time ,
617+ hasProvenance : meta . provenance === 'trustedPublisher' || meta . provenance === true ,
618+ deprecated : meta . deprecated ,
629619 } ) )
630620 . sort ( ( a , b ) => compare ( b . version , a . version ) )
631621 } ) ( )
0 commit comments