// Top-level path segments that already have meaning in the SPA router // (see src/App.tsx) plus a few reserved for infrastructure. Category slugs // live at the URL root (e.g. /music), so they must never collide with these. export const RESERVED_SLUGS: readonly string[] = [ "~", "dumps", "users", "playlists", "search", "login", "register", "notifications", "reset-password", "manage", "api", "assets", ]; // Lowercase alphanumerics, hyphen-separated, no leading/trailing/double hyphen. export const CATEGORY_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; export function isValidSlugFormat(slug: string): boolean { return CATEGORY_SLUG_RE.test(slug); } export function isReservedSlug(slug: string): boolean { return RESERVED_SLUGS.includes(slug); } /** A slug is available when it is well-formed and not reserved. */ export function isSlugAvailable(slug: string): boolean { return isValidSlugFormat(slug) && !isReservedSlug(slug); }