37 lines
1.3 KiB
TypeScript
37 lines
1.3 KiB
TypeScript
export class LinkError extends Error {
|
|
override name = "LinkError";
|
|
}
|
|
|
|
/** Parses a pasted link; a missing scheme means https. */
|
|
export function parseLink(input: string): URL {
|
|
const s = input.trim();
|
|
if (!s) throw new LinkError("Paste a link first.");
|
|
try {
|
|
return new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(s) ? s : `https://${s}`);
|
|
} catch {
|
|
throw new LinkError(`That doesn't look like a link: ${s}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accepts a bare id or any mangabaka.org link to a series. `/9999` redirects to the canonical
|
|
* `/manhwa/9999/Tower-of-God` and `/series/9999` is a 404 (spec §4.5), so we take the first
|
|
* all-digits path segment.
|
|
*/
|
|
export function parseMangaBakaSeriesId(input: string): number {
|
|
const s = input.trim();
|
|
if (/^\d+$/.test(s)) return positive(Number(s), s);
|
|
const url = parseLink(s);
|
|
if (!/^(www\.)?mangabaka\.org$/i.test(url.hostname)) {
|
|
throw new LinkError(`Not a MangaBaka link: ${url.hostname}. Paste a mangabaka.org series link or its number.`);
|
|
}
|
|
const segment = url.pathname.split("/").find((p) => /^\d+$/.test(p));
|
|
if (!segment) throw new LinkError("That MangaBaka link has no series number in it.");
|
|
return positive(Number(segment), s);
|
|
}
|
|
|
|
function positive(n: number, input: string): number {
|
|
if (!Number.isSafeInteger(n) || n <= 0) throw new LinkError(`Not a MangaBaka series number: ${input}`);
|
|
return n;
|
|
}
|