Структура объекта ошибки

В библиотеке Superstruct все ошибки валидации данных представлены единым специализированным типом — StructError. Это не просто сообщение о сбое проверки, а структурированный объект, содержащий полную информацию о том, где, почему и каким образом входные данные не соответствуют ожидаемой структуре.

Такой подход позволяет не только фиксировать факт ошибки, но и точно локализовать её внутри вложенных структур, массивов и сложных объектов.


Общая структура StructError

Экземпляр ошибки содержит несколько ключевых полей:

  • value — исходное значение, которое не прошло проверку
  • key — ключ объекта, в котором произошла ошибка (если применимо)
  • path — полный путь к проблемному полю
  • type — ожидаемый тип или структурное правило
  • refinement — имя уточняющего правила (если использовались refinements)
  • branch — цепочка значений от корня структуры до конкретного узла
  • failures() — функция-генератор всех нарушений
  • message — итоговое текстовое описание ошибки

Каждое из этих полей играет роль в построении диагностической информации, необходимой для обработки ошибок валидации.


Поле value

value содержит исходные данные, которые были переданы в валидатор. Это может быть как простое значение, так и сложный объект или массив.

Особенность заключается в том, что value всегда отражает реальное состояние данных на момент ошибки, без преобразований и маскировки.


path: навигация к источнику ошибки

Поле path представляет собой массив, описывающий путь до элемента, вызвавшего ошибку.

Пример структуры:

['user', 'address', 'street']

Такой путь позволяет однозначно определить, где именно произошёл сбой:

  • первый элемент — ключ верхнего уровня
  • последующие элементы — вложенные свойства
  • индексы массивов также включаются в path

Путь формируется динамически во время рекурсивной проверки структуры.


key: локальный идентификатор

Поле key содержит имя текущего проверяемого свойства.

Если ошибка произошла внутри объекта, key указывает на конкретное поле. Если структура представляет собой массив, key может быть числовым индексом.


type: ожидаемая структура

Поле type описывает правило, которое нарушено.

Это может быть:

  • примитивный тип (string, number, boolean)
  • объектная структура
  • массив
  • union-тип
  • пользовательская схема

Type не содержит фактического значения — только описание ожидаемого формата.


refinement: уточняющие правила

В Superstruct можно задавать дополнительные проверки через refinement-функции. Если ошибка возникает именно на этом уровне, поле refinement содержит имя соответствующего правила.

Пример использования:

  • проверка диапазона чисел
  • валидность формата строки
  • логические ограничения между полями

Если refinement отсутствует, значит нарушение произошло на базовом уровне структуры.


branch: цепочка значений

Поле branch представляет собой массив значений, проходящих от корня структуры до точки ошибки.

Пример:

[
  { user: {...} },
  { address: {...} },
  'Main Street'
]

Каждый элемент branch отражает промежуточное состояние данных на пути к ошибке. Это позволяет анализировать контекст, а не только финальный узел.


failures(): генератор нарушений

Метод failures() возвращает итератор всех обнаруженных ошибок валидации.

Каждый элемент содержит:

  • path — путь к ошибке
  • value — некорректное значение
  • type — ожидаемая структура
  • refinement — уточнение (если есть)
  • message — локальное описание проблемы
  • branch — контекст выполнения проверки

В отличие от самого StructError, который может агрегировать ошибки, failures() раскрывает их по отдельности, позволяя выполнять постобработку.


Структура отдельного нарушения

Каждое нарушение внутри failures() имеет следующую форму:

  • path: точная позиция ошибки
  • value: проблемный фрагмент данных
  • type: ожидаемая спецификация
  • refinement: правило проверки (опционально)
  • message: человекочитаемое описание
  • branch: контекстная цепочка значений

Эта структура используется при построении пользовательских сообщений и систем логирования.


message: итоговое описание

Поле message формируется автоматически на основе остальных данных.

Оно объединяет:

  • тип ошибки
  • путь
  • ожидаемую структуру
  • контекст значения

Однако message не является единственным источником истины — он предназначен для быстрого восприятия, тогда как path и failures() используются для точного анализа.


Вложенные структуры и накопление пути

При работе с глубоко вложенными объектами Superstruct постепенно накапливает информацию о пути ошибки.

Пример логики формирования:

  1. Проверка объекта верхнего уровня
  2. Переход в поле
  3. Проверка вложенного объекта
  4. Фиксация нарушения
  5. Формирование path и branch

Таким образом обеспечивается точная трассировка даже в сложных иерархиях данных.


Поведение при множественных ошибках

Если структура содержит несколько нарушений, Superstruct:

  • агрегирует их в одном StructError
  • предоставляет полный список через failures()
  • сохраняет общий value и контекст

Это позволяет избегать остановки проверки на первой ошибке и получать полную картину состояния данных.


Особенности сериализации ошибки

StructError можно преобразовывать в JSON-подобные структуры, однако:

  • branch может содержать несерилизуемые объекты
  • value может включать сложные типы
  • функции (failures) теряются при сериализации

Поэтому для передачи между слоями системы чаще используют результат failures(), а не сам объект ошибки.


Практическая интерпретация структуры

При обработке StructError обычно выделяются три уровня анализа:

  1. Идентификация — type и message
  2. Локализация — path и key
  3. Контекст — branch и value

Такое разделение позволяет строить как пользовательские сообщения, так и внутренние механизмы диагностики данных без потери информации о контексте выполнения проверки.