111 lines
3.0 KiB
TypeScript
111 lines
3.0 KiB
TypeScript
|
|
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;
|
||
|
|
}
|