Перейти к содержимому
Star

Введение ​

deffector — инструменты разработчика для effector. Он показывает каждое действие пользователя как одну транзакцию — событие и всё, что оно вызвало, — позволяет перемещаться между ними, подставлять своё состояние в любой стор и записывать трейсы, по которым другой разработчик воспроизводит ваш баг, как в отладчике Elm.

Панель deffector: лог слева, инспектор транзакции #4 справаПанель deffector: лог слева, инспектор транзакции #4 справа

Зачем ​

effector быстрый и декларативный, но из-за тех же свойств некоторые баги трудно увидеть:

  • событие, вызванное из setTimeout или сокета без scopeBind, меняет глобальный граф, а приложение читает свой scope — на экране ничего не происходит;
  • combine и sample выполняются в порядке объявления, а не в порядке графа, поэтому стор может пересчитаться дважды, а sample — прочитать значение, которое вот-вот изменится;
  • filter, вернувший false, не оставляет никаких следов;
  • исключение в .map останавливает одну ветку, а остальные ветки уже применились;
  • баг, для которого нужны двадцать кликов и медленный ответ сервера, трудно передать коллеге.

deffector записывает то, что на самом деле сделало ядро, и объясняет это в панели: Log показывает шаги, Health превращает известные ловушки в предупреждения с исправлением, Traces делают баг воспроизводимым.

Как это устроено ​

Вкладка браузераваше приложение + effector@deffector/agentхуки инспектораРасширение браузера (Chrome, Edge, Firefox)content scriptbackgroundDevTools → deffectorpostMessageportportNode · SSR · React Nativeсервер / приложение + effector@deffector/agent/nodeОтдельное приложение: npx @deffector/standaloneретранслятор (server.mjs)панель на :8686ws /agentws /panel

Агент (@deffector/agent) работает внутри вашей страницы. Он должен быть первым импортом, чтобы видеть юниты в момент их создания. Агент подключается к инспектору ядра и инспектору графа effector — setInspector и setGraphInspector, тем самым функциям, на которых построен effector/inspect. Они отдают сырые узлы графа, точного родителя каждого шага и scope каждого вызова — сразу во всех scope:

  • каждый юнит регистрируется с именем, sid, файлом, фабрикой, доменом и моделью;
  • каждый шаг ядра попадает в свою транзакцию — с лейном, значением до и после и причиной остановки (filter, то же значение, исключение);
  • изменения записываемых сторов хранятся по транзакциям, поэтому состояние любого момента в прошлом можно восстановить;
  • команды из панели — переход, правка, вызов, подмена, воспроизведение — выполняются в странице.

Адаптер (DeffectorProvider для React и Solid, withDeffector / DeffectorScope для Vue) — прямая замена собственного Provider из effector. Он регистрирует scope под понятным именем и даёт агенту возможность отрисовать приложение с другим scope: форком прошлого для перемещения во времени или свежим scope для воспроизведения трейса.

Панель — один и тот же интерфейс на React, используется в двух местах:

  • расширение браузера (Chrome, Edge, Firefox): агент общается с content script через window.postMessage, content script — с фоновым скриптом, фоновый скрипт — с панелью DevTools. Content script открывает соединение, только когда на странице есть агент, поэтому обычные страницы ничего не теряют;
  • отдельное приложение (npx @deffector/standalone) — для кода вне вкладки браузера: SSR-серверов, скриптов Node, тестов, React Native. Агенты подключаются по WebSocket к /agent, панели в любой вкладке браузера — к /panel.

В продакшен-сборке каждая точка входа @deffector/agent через условие экспорта production превращается в пустышку, так что импорт можно оставить в коде.

Термины ​

ТерминЗначение
ТранзакцияОдин корневой вызов (событие из UI, таймер, завершившийся эффект, правка из панели) и все шаги ядра, которые он синхронно вызвал.
ШагОдин узел, выполненный ядром: обновление стора, оператор (sample, map, on…), вызов эффекта.
ЛейнПриоритет ядра, в котором выполнился шаг: pure, read, child, barrier, sampler, effect.
ScopeРезультат fork(), отдельная копия состояния приложения. global — глобальный граф (без scope).
ВеткаScope, созданный deffector: форк прошлого после перехода, scope с «тихими» правками или воспроизведение трейса. В списке scope помечается ⑂.
ТрейсJSON-файл (deffector-trace@1) с начальным состоянием, внешними вызовами и результатами эффектов одного scope — достаточно, чтобы воспроизвести ту же историю.

Статус ​

deffector — версия 0.1, активно развивается. Изменения, которые нужны ему в самом effector, описаны в разделе PR в effector; пока они не приняты, deffector работает на обходных путях.