[READ-ONLY] a fast, modern browser for the npm registry
at main 65 lines 2.1 kB view raw
1import { getLatestVersion } from 'fast-npm-meta' 2import { createError } from 'h3' 3import validatePackageName from 'validate-npm-package-name' 4 5const NPM_USERNAME_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i 6const NPM_USERNAME_MAX_LENGTH = 50 7 8/** 9 * Encode package name for URL usage. 10 * Scoped packages need special handling (@scope/name → @scope%2Fname) 11 */ 12export function encodePackageName(name: string): string { 13 if (name.startsWith('@')) { 14 return `@${encodeURIComponent(name.slice(1))}` 15 } 16 return encodeURIComponent(name) 17} 18 19/** 20 * Fetch the latest version of a package using fast-npm-meta API. 21 * This is a lightweight alternative to fetching the full packument. 22 * 23 * @param name Package name 24 * @returns Latest version string or null if not found 25 * @see https://github.com/antfu/fast-npm-meta 26 */ 27export async function fetchLatestVersion(name: string): Promise<string | null> { 28 try { 29 const meta = await getLatestVersion(name) 30 return meta.version 31 } catch { 32 return null 33 } 34} 35 36/** 37 * Validate an npm package name and throw an HTTP error if invalid. 38 * Uses validate-npm-package-name to check against npm naming rules. 39 */ 40export function assertValidPackageName(name: string): void { 41 const result = validatePackageName(name) 42 if (!result.validForNewPackages && !result.validForOldPackages) { 43 const errors = [...(result.errors ?? []), ...(result.warnings ?? [])] 44 throw createError({ 45 // TODO: throwing 404 rather than 400 as it's cacheable 46 statusCode: 404, 47 message: `Invalid package name: ${errors[0] ?? 'unknown error'}`, 48 }) 49 } 50} 51 52/** 53 * Validate an npm username and throw an HTTP error if invalid. 54 * Uses a regular expression to check against npm naming rules. 55 * @public 56 */ 57export function assertValidUsername(username: string): void { 58 if (!username || username.length > NPM_USERNAME_MAX_LENGTH || !NPM_USERNAME_RE.test(username)) { 59 throw createError({ 60 // TODO: throwing 404 rather than 400 as it's cacheable 61 statusCode: 404, 62 message: `Invalid username: ${username}`, 63 }) 64 } 65}