/** Нормализует URL: убирает page=1, sort=trending, пустые query, сортирует категории, приводит к нижнему регистру, заменяет пробелы на дефисы. */ export function normalizeUrl(to: { path: string; query: Record }) { let path = to.path; let query = { ...to.query }; // 1) Удаляем page=1 if (query.page === '1') { const { page, ...rest } = query; query = rest; } // 2) Удаляем sort=trending if (query.sort === 'trending') { const { sort, ...rest } = query; query = rest; } // Удаляем query без значений query = Object.fromEntries( Object.entries(query).filter(([_, v]) => v !== '' && v !== null && v !== undefined) ); // Приводим query к нижнему регистру (ключи и строковые значения) const loweredQuery: Record = {}; for (const [key, value] of Object.entries(query)) { const lowerKey = key.toLowerCase(); loweredQuery[lowerKey] = typeof value === 'string' ? value.toLowerCase() : value; } query = loweredQuery; // Сортируем категории: их порядок задаёт пользователь кликами, набор от перестановки // не меняется — иначе одна и та же выборка живёт по нескольким адресам if (typeof query.categories === 'string') { query.categories = query.categories.split(',').sort().join(','); } // 2) Декодируем путь перед lowercase: иначе .toLowerCase() опускает регистр // у hex-цифр в %XX (напр. %D0%B6 → %d0%b6), роутер нормализует их обратно // в верхний регистр, и редирект зацикливается на кириллице. let decoded = path; try { decoded = decodeURI(path); } catch { // битое перкодирование — оставляем путь как есть } path = decoded.toLowerCase(); // 3) Заменяем пробелы и %20 на дефисы path = path .replace(/%20/gi, '-') // encoded .replace(/ /g, '-') // raw .replace(/\+/g, '-'); // some browsers encode spaces as + // 4) Убираем двойные дефисы path = path.replace(/--+/g, '-'); // 5) Убираем хвостовой / if (path.length > 1) { path = path.replace(/\/+$/, ''); } // 6) Кодируем обратно — стабильная RFC-форма (uppercase hex), совпадающая // с тем, что отдаёт vue-router в to.path, поэтому редирект не зациклится. try { path = encodeURI(path); } catch { // no-op } return { path, query }; }