Debug режим

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

Ключевая особенность заключается в том, что ошибка не является конечным объектом — она выступает контейнером для последовательности «сбоев», каждый из которых описывает конкретный участок структуры данных.


Объект ошибки и генератор failures

При использовании функции validate(value, struct) результатом становится кортеж:

  • error — объект ошибки или undefined
  • result — преобразованные данные (если валидация успешна)

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

Каждая запись описывает отдельное нарушение:

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

Пример логики извлечения:

const [error, data] = validate(input, User);

if (error) {
  for (const failure of error.failures()) {
    console.log(failure.path, failure.message, failure.value);
  }
}

Трассировка пути (path tracing)

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

Путь представляет собой массив сегментов, отражающих навигацию по вложенным структурам:

  • объектные ключи
  • индексы массивов
  • вложенные структуры

Например:

['user', 'addresses', 0, 'zip']

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

Особенность реализации заключается в том, что путь формируется лениво — на этапе обхода структуры, а не постфактум.


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

Superstruct поддерживает агрегацию ошибок на всех уровнях вложенности. Это означает, что при валидации сложного объекта не происходит «раннего выхода» на первой ошибке (если не используется assert).

Рассмотрим поведение:

  • при использовании validate собираются все ошибки
  • при использовании assert выбрасывается первая обнаруженная ошибка
  • при использовании is ошибки полностью игнорируются

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


Режим assert и генерация исключений

Функция assert(value, struct) переводит результат в режим исключений.

В этом случае ошибка содержит расширенную диагностическую информацию, но выбрасывается как исключение, что удобно для сценариев строгой проверки входных данных.

Структура ошибки включает:

  • список failures
  • сообщение верхнего уровня
  • исходное значение
  • ссылку на struct

При отладке важно учитывать, что assert не предназначен для накопления ошибок, а только для немедленного прерывания исполнения.


Кастомизация сообщений ошибок

Каждый примитивный тип и комбинированная структура могут содержать пользовательские сообщения.

Пример определения:

import { string } from 'superstruct';

const Name = string({
  message: 'Имя должно быть строкой'
});

В диагностическом режиме такие сообщения подставляются в failure вместо стандартных описаний типов.

Это влияет на:

  • читаемость логов
  • интеграцию с UI-валидацией
  • трассировку пользовательских ошибок

Refine и расширенная диагностика

Функция refine добавляет пользовательские условия проверки поверх базовой структуры.

import { refine, string } from 'superstruct';

const NonEmpty = refine(string(), 'NonEmpty', (value) => {
  return value.length > 0;
});

При включении debug-режима refine добавляет дополнительный уровень контекста в failure:

  • имя проверки
  • причина провала
  • значение на входе

Таким образом, ошибки становятся не только синтаксическими, но и семантическими.


Coercion и mask в контексте отладки

Механизмы преобразования данных (coerce, mask) влияют на диагностический вывод.

  • coerce изменяет входные данные до проверки
  • mask принудительно приводит структуру к типу

В debug-режиме важно различать:

  1. исходное значение (до преобразования)
  2. нормализованное значение (после coercion)
  3. финальный результат проверки

Failure-объекты могут содержать оба представления, что позволяет анализировать цепочку трансформаций.


Агрегация ошибок в массивах и объектах

При работе с коллекциями ошибок поведение отличается в зависимости от структуры:

  • в массивах каждый элемент проверяется независимо
  • в объектах каждая ветка проверяется отдельно
  • вложенные структуры формируют иерархический список failures

Пример диагностического результата:

users[2].email → invalid format
users[4].age → expected number, received string

Такой формат позволяет использовать ошибки напрямую в логировании без дополнительной обработки.


Контекст выполнения и расширение диагностики

Superstruct позволяет сохранять контекст выполнения в процессе валидации.

Контекст может включать:

  • имя структуры
  • пользовательские метаданные
  • состояние промежуточных преобразований

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


Поведение при глубокой вложенности

При глубокой вложенности debug-режим сохраняет стек прохождения структуры.

Каждый уровень добавляет новый сегмент пути, формируя полную трассировку:

  • корневой объект
  • промежуточные структуры
  • конечное значение

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


Ленивая генерация ошибок

Failures не вычисляются заранее. Они создаются только при итерации через failures().

Это означает:

  • отсутствие лишних вычислений при успешной валидации
  • возможность частичного обхода ошибок
  • контроль над объемом диагностической информации

Такой подход снижает накладные расходы при работе с большими структурами данных.


Использование диагностических данных в логировании

Структурированные ошибки легко интегрируются в системы логирования благодаря предсказуемому формату:

  • path
  • message
  • expected
  • received

Это позволяет:

  • строить отчёты о валидации
  • группировать ошибки по полям
  • анализировать повторяющиеся сбои

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

При использовании объединений (union) debug-режим фиксирует все неудачные попытки сопоставления.

Каждая альтернатива сохраняет собственный набор failures, что позволяет определить:

  • какие ветки были проверены
  • почему они не подошли
  • на каком этапе произошло расхождение типов

Это особенно полезно при сложных схемах с несколькими допустимыми форматами данных.