forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
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>