export type TNpmRequestRoute = | { type: 'root' } | { type: 'search' } | { type: 'userAuth'; username: string } | { type: 'tokens'; path: string } | { type: 'distTags'; packageName: string; tag?: string } | { type: 'tarball'; packageName: string; filename: string } | { type: 'unpublishVersion'; packageName: string; version: string } | { type: 'unpublishPackage'; packageName: string; rev: string } | { type: 'packageVersion'; packageName: string; version: string } | { type: 'package'; packageName: string }; function decodePackageName(rawPackageName: string): string { return decodeURIComponent(rawPackageName); } export function parseNpmRequestRoute(path: string, method: string): TNpmRequestRoute | null { if (path === '/' || path === '') { return { type: 'root' }; } if (path.startsWith('/-/v1/search')) { return { type: 'search' }; } const userMatch = path.match(/^\/-\/user\/org\.couchdb\.user:(.+)$/); if (userMatch) { return { type: 'userAuth', username: userMatch[1], }; } if (path.startsWith('/-/npm/v1/tokens')) { return { type: 'tokens', path, }; } const distTagsMatch = path.match(/^\/-\/package\/(@?[^\/]+(?:\/[^\/]+)?)\/dist-tags(?:\/(.+))?$/); if (distTagsMatch) { const [, rawPackageName, tag] = distTagsMatch; return { type: 'distTags', packageName: decodePackageName(rawPackageName), tag, }; } const tarballMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-\/(.+\.tgz)$/); if (tarballMatch) { const [, rawPackageName, filename] = tarballMatch; return { type: 'tarball', packageName: decodePackageName(rawPackageName), filename, }; } if (method === 'DELETE') { const unpublishVersionMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-\/([^\/]+)$/); if (unpublishVersionMatch) { const [, rawPackageName, version] = unpublishVersionMatch; return { type: 'unpublishVersion', packageName: decodePackageName(rawPackageName), version, }; } const unpublishPackageMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/-rev\/([^\/]+)$/); if (unpublishPackageMatch) { const [, rawPackageName, rev] = unpublishPackageMatch; return { type: 'unpublishPackage', packageName: decodePackageName(rawPackageName), rev, }; } } const unencodedScopedPackageMatch = path.match(/^\/@[^\/]+\/[^\/]+$/); if (unencodedScopedPackageMatch) { return { type: 'package', packageName: decodePackageName(path.substring(1)), }; } const versionMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)\/([^\/]+)$/); if (versionMatch) { const [, rawPackageName, version] = versionMatch; return { type: 'packageVersion', packageName: decodePackageName(rawPackageName), version, }; } const packageMatch = path.match(/^\/(@?[^\/]+(?:\/[^\/]+)?)$/); if (packageMatch) { return { type: 'package', packageName: decodePackageName(packageMatch[1]), }; } return null; }