Создание плагинов

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

Плагин в контексте Vest — это функция или набор функций, которые внедряются в процесс выполнения тест-сьюта и влияют на один или несколько этапов жизненного цикла валидации:

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

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


Модель плагина

Базовая форма плагина сводится к функции, принимающей контекст выполнения и возвращающей расширение поведения.

Контекст обычно содержит:

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

Типовая структура:

const myPlugin = (context) => {
  return {
    name: 'my-plugin',
    setup() {
      // инициализация
    },
    onRun(rule, value) {
      // логика перед выполнением правила
    },
    onResult(result) {
      // модификация результата
    }
  };
};

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


Механизм подключения плагинов

Vest не предполагает глобального состояния как обязательного условия работы. Плагины подключаются локально к конкретному тест-сьюту или набору правил.

Пример интеграции:

import { create, test } from 'vest';

const suite = create((data, use) => {
  use(myPlugin);

  test('username', 'required', () => {
    // проверка
  });
});

В момент вызова use плагин регистрируется в текущем контексте выполнения и становится частью цепочки обработки.

Каждый последующий test проходит через слой расширений, что позволяет влиять на:

  • поведение валидаторов
  • формат ошибок
  • условия выполнения

Перехват жизненного цикла валидации

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

Этапы, доступные для плагинов:

  1. Инициализация сьюта

    • подготовка контекста
    • установка глобальных параметров
  2. Регистрация теста

    • анализ правила
    • модификация конфигурации проверки
  3. Выполнение теста

    • доступ к значению поля
    • возможность отмены выполнения
  4. Формирование результата

    • добавление ошибок
    • изменение статуса
  5. Финализация отчёта

    • постобработка
    • агрегация данных

Перехват этих этапов реализуется через хуки:

const plugin = () => ({
  onStart() {},
  onTestStart(test) {},
  onTestEnd(testResult) {},
  onFinish(report) {}
});

Такая модель делает плагины сопоставимыми с middleware-подходом.


Расширение системы правил

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

Плагин может регистрировать собственные правила:

const customRulePlugin = () => ({
  rules: {
    isUUID: (value) => {
      return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
    }
  }
});

После регистрации такие правила становятся доступны внутри тестов:

test('id', 'invalid uuid', () => {
  enforce(data.id).isUUID();
});

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


Модификация отчётов валидации

Результат работы Vest представляет собой структурированный отчёт, содержащий:

  • список полей
  • статусы проверок
  • сообщения об ошибках
  • метаданные выполнения

Плагины могут изменять этот отчёт до его возврата:

const reportPlugin = () => ({
  onFinish(report) {
    return {
      ...report,
      timestamp: Date.now(),
      formattedErrors: Object.entries(report.errors)
    };
  }
});

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


Асинхронные плагины

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

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

const asyncPlugin = () => ({
  async onTestStart(test) {
    const result = await fetch(`/api/rules/${test.name}`);
    test.metadata.remoteRule = await result.json();
  }
});

Асинхронные плагины влияют на порядок выполнения и могут вводить дополнительные ожидания между тестами.


Композиция нескольких плагинов

Несколько плагинов могут работать одновременно, формируя цепочку обработки.

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

use(pluginA);
use(pluginB);
use(pluginC);

Возможные конфликты решаются через:

  • приоритеты выполнения
  • контроль мутаций контекста
  • изоляцию состояния

Интеграция с внешними фреймворками

Плагины часто используются для адаптации Vest под конкретные экосистемы.

Типовые сценарии:

  • React: синхронизация с состоянием компонентов
  • Node.js: серверная валидация запросов
  • TypeScript: усиление типизации правил
  • REST API: преобразование отчётов в JSON-формат ответа

Пример адаптера:

const apiPlugin = () => ({
  onFinish(report) {
    return {
      status: report.valid ? 'ok' : 'error',
      errors: report.errors
    };
  }
});

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

Главный принцип работы плагинов заключается в доступе к общему контексту выполнения.

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

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

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


Ограничения модели расширений

Несмотря на гибкость, система плагинов имеет ограничения:

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

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


Паттерны проектирования плагинов

На практике выделяются несколько устойчивых подходов:

Декоратор

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

Middleware

Последовательная обработка тестов через цепочку функций.

Adapter

Приведение результатов Vest к внешним форматам.

Strategy

Замена алгоритмов валидации в зависимости от условий.


Безопасность и изоляция

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

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

Архитектурно Vest предполагает, что ответственность за безопасное поведение расширений лежит на уровне их реализации.