// Slug normalization for app slugs, mirrored against the backend's // validate_slug rules in crates/manager-core/src/apps_api.rs: // - regex: ^[a-z0-9][a-z0-9-]{0,62}$ // - 1..=63 chars, lowercase ascii alphanumerics + `-` // - must start with [a-z0-9] // - reserved words are enforced server-side only // // Normalization rules are GitLab-style (close to `Babosa::Latin#to_slug`): // 1. NFKD-decompose Unicode and drop combining marks (é → e, ñ → n, // ü → u, etc.). // 2. ß → ss (a single common case the strip-marks pass misses). // 3. Lowercase. // 4. Replace any run of non-[a-z0-9] with a single `-`. // 5. Trim leading/trailing `-`. // 6. Truncate to 63 chars. export const SLUG_MAX = 63; export function slugify(input: string): string { if (!input) return ''; let s = input.normalize('NFKD').replace(/[\u0300-\u036f]/g, ''); s = s.toLowerCase().replace(/ß/g, 'ss'); s = s.replace(/[^a-z0-9]+/g, '-'); s = s.replace(/^-+|-+$/g, ''); if (s.length > SLUG_MAX) { // Truncate, then re-trim in case the cut landed on a `-`. s = s.slice(0, SLUG_MAX).replace(/-+$/g, ''); } return s; }