0af5c5696f
РАЗМЕТКА ДОКУМЕНТАЦИИ. Проверка сторожа давала 237 замечаний в 21 файле — стало
0 во всех файлах под учётом git. Пустые строки вокруг списков и заголовков
поправлены автоматом в 12 файлах, два файла пришлось делать руками, потому что
автоправка там навредила:
- в описании починки инцидента номера шагов 1…7 — это ИМЕНА, на них ссылается
сам текст «из шага 2»; автоправка перенумеровала их в 1,1,1,1,2 и сломала
ссылки. Вывел ровно этот файл из-под одного правила, объяснение — в самом
файле, остальные правила действуют.
- в протоколе архитектуры автоправка склеила «R-* / DR-*» в «R-*/ DR-*».
Вместо этого обозначения взяты в обратные кавычки — смысл сохранён.
Семь файлов не тронуты: они вне учёта git — два в папке второй смены и старые
черновики в корне.
ИНТЕРФЕЙС. «Неизвестный тип» убран целиком: было 123 замечания, стало 0.
- Навигация автоподбора описана ОДИН раз — app/resources/js/views/autopodbor/nav.ts.
Раньше девять экранов повторяли описание с «неизвестным типом», а ещё три
описывали его каждый по-своему и не полностью.
- Чтение ошибки от сервера собрано в один помощник — app/resources/js/api/errors.ts,
с проверками. Он намеренно читает ФОРМУ ответа, а не полагается на axios:
тесты экранов подсовывают ошибку простым объектом той же формы, и экран обязан
вести себя в тестах так же, как в бою.
- В тестах вместо «неизвестного типа» — именованный доступ exposed<T> и полные
заготовки: app/tests/Frontend/support/. Каждый тест теперь объявляет ровно те
поля, до которых дотягивается, и опечатка в имени снова становится ошибкой,
а не молчаливым undefined. Заодно сняты 14 построчных отключений правила.
- Разведены задвоенные имена в шаблонах: в таблице сделок в одном шаблоне жили
ДВЕ разные функции isSelected — одна берёт номер сделки, другая строку таблицы.
- Убрана мёртвая функция dirLabel и три неиспользуемые переменные.
ПРОВЕРКА ТИПОВ: было 7 ошибок, стало 5. Две унаследованные закрылись попутно —
более строгие заготовки вскрыли нехватку обязательного поля elements. Оставшиеся
пять были до меня и не в моих строках.
ПРИЁМКА. Разметка проверена вырезанием: подложил поломку обратно — сторож её
поймал, вернул — снова чисто. Полный набор тестов интерфейса: 233 файла,
1750 тестов, 3 пропущено, 0 падений. Рабочий код на PHP не тронут.
NB: LEFTHOOK_EXCLUDE=cspell — тем же приёмом и по той же причине, что и в записи
e0223de4, которой эти шесть файлов исследования заводились в репозиторий: сырой
внешний материал с чужой терминологией. Замерено: до моих правок сторож
орфографии находил в них ровно столько же слов, сколько после — я не добавил
ни одного. Разметка в этих файлах теперь проходит начисто, отключена ТОЛЬКО
орфография и только на эту запись; остальные семнадцать сторожей отработали.
Решение владельца, спрошено отдельно.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 lines
2.0 KiB
TypeScript
42 lines
2.0 KiB
TypeScript
/**
|
|
* Чтение ошибки, пришедшей от бэкенда.
|
|
*
|
|
* До 02.08.2026 экраны читали её так: `(e as any)?.response?.data?.error` — «неизвестный тип»
|
|
* в четырёх местах подряд. Опечатка в имени поля при этом не ловилась ничем.
|
|
*
|
|
* 🪤 Намеренно НЕ через `axios.isAxiosError`: тесты экранов подсовывают ошибку простым объектом
|
|
* той же формы (`mockRejectedValue({ response: { data: { error: ... } } })`). Проверка на
|
|
* настоящую ошибку axios отсекла бы их, и экран вёл бы себя в тестах не так, как в бою.
|
|
* Читаем форму — она одна и та же в обоих случаях.
|
|
*/
|
|
|
|
/** Тело ответа с ошибкой — то, что бэкенд кладёт в `data`. */
|
|
export interface ApiErrorBody {
|
|
error?: string;
|
|
message?: string;
|
|
errors?: Record<string, string[]>;
|
|
}
|
|
|
|
/** Тело ошибки целиком, либо undefined, если пришло что-то другой формы. */
|
|
export function apiErrorBody(error: unknown): ApiErrorBody | undefined {
|
|
if (typeof error !== 'object' || error === null) return undefined;
|
|
|
|
const response = (error as { response?: unknown }).response;
|
|
if (typeof response !== 'object' || response === null) return undefined;
|
|
|
|
const data = (response as { data?: unknown }).data;
|
|
if (typeof data !== 'object' || data === null) return undefined;
|
|
|
|
return data as ApiErrorBody;
|
|
}
|
|
|
|
/** Код ошибки (`balance_insufficient`, `run_in_flight` и т.п.). */
|
|
export function apiErrorCode(error: unknown): string | undefined {
|
|
return apiErrorBody(error)?.error;
|
|
}
|
|
|
|
/** Первая ошибка по конкретному полю формы. */
|
|
export function apiFieldError(error: unknown, field: string): string | undefined {
|
|
return apiErrorBody(error)?.errors?.[field]?.[0];
|
|
}
|