[READ-ONLY] a fast, modern browser for the npm registry
at main 79 lines 2.4 kB view raw
1import * as v from 'valibot' 2import validateNpmPackageName from 'validate-npm-package-name' 3 4/** 5 * Enforces only valid NPM package names 6 * Accepts both new and legacy package name formats 7 * Leverages 'validate-npm-package-name' 8 */ 9export const PackageNameSchema = v.pipe( 10 v.string(), 11 v.nonEmpty('Package name is required'), 12 v.check(input => { 13 const result = validateNpmPackageName(input) 14 return result.validForNewPackages || result.validForOldPackages 15 }, 'Invalid package name format'), 16) 17 18/** 19 * Enforces a SemVer-like pattern to prevent directory traversal or complex injection attacks 20 * includes: alphanumeric, dots, underscores, dashes, and plus signs (for build metadata) 21 */ 22export const VersionSchema = v.pipe( 23 v.string(), 24 v.nonEmpty('Version is required'), 25 v.regex(/^[\w.+-]+$/, 'Invalid version format'), 26) 27 28/** 29 * 30 * Allows standard subdirectories and extensions but prevents directory traversal 31 */ 32export const FilePathSchema = v.pipe( 33 v.string(), 34 v.nonEmpty('File path is required'), 35 v.check(input => !input.includes('..'), 'Invalid path: directory traversal not allowed'), 36 v.check(input => !input.startsWith('/'), 'Invalid path: must be relative to package root'), 37) 38 39/** 40 * Schema for search queries, limits length to guard against DoS attacks 41 */ 42export const SearchQuerySchema = v.pipe( 43 v.string(), 44 v.trim(), 45 v.maxLength(100, 'Search query is too long'), 46) 47 48/** 49 * Schema for package fetching where version is not required 50 */ 51export const PackageRouteParamsSchema = v.object({ 52 packageName: PackageNameSchema, 53 version: v.optional(VersionSchema), 54}) 55 56/** 57 * Schema for package fetching where packageName and version are required 58 */ 59export const PackageVersionQuerySchema = v.object({ 60 packageName: PackageNameSchema, 61 version: VersionSchema, 62}) 63 64/** 65 * Schema for file fetching where version and filePath are required 66 */ 67export const PackageFileQuerySchema = v.object({ 68 packageName: PackageNameSchema, 69 version: VersionSchema, 70 filePath: FilePathSchema, 71}) 72 73/** 74 * Automatically infer types for routes 75 * Usage - prefer this over manually defining interfaces 76 */ 77export type PackageRouteParams = v.InferOutput<typeof PackageRouteParamsSchema> 78export type PackageVersionQuery = v.InferOutput<typeof PackageVersionQuerySchema> 79export type PackageFileQuery = v.InferOutput<typeof PackageFileQuerySchema>