Архитектура
Кто чем владеет
TSX / хуки → Mutation Stream → Scene / Layout / Paint
(оболочка TypeScript) бинарный, пакетами (ядро на Rust, wasm)
↓
Проигрыватель Canvas2D ← DisplayList ← PictureОболочка владеет деревом компонентов, ядро владеет Scene. Они не разделяют изменяемые объекты. Всё общение через эту границу — версионированные бинарные потоки: little-endian, выравнивание по четыре байта, в виде инструкций. Получатель проверяет опкод, длину, выравнивание, идентификаторы и арифметику до обращения к памяти, а некорректный ввод отклоняется атомарно, а не применяется частично.
Эта граница — не оптимизация производительности, а граница корректности: даже когда байты обычно приходят от собственного кодировщика проекта, декодер считает их недоверенным вводом и покрыт фаззингом.
Две тактовые оси
Часы интерфейса (главный поток) и часы рендеринга (Worker) независимы:
- Главный поток собирает ввод, выполняет дерево компонентов и коммитит кадры Mutation.
- Worker ведёт физику прокрутки, анимацию, раскладку и композицию.
Установившаяся прокрутка не вызывает оболочку. Отсутствующие данные рисуются заглушками и дополняются на следующих кадрах. Поэтому, если прикладной код блокирует главный поток на 200 мс, прокрутка и анимация остаются непрерывными; этот сценарий защищён автоматическими тестами с внедрением отказов.
Цепочка отката
Определение возможностей выбирает транспорт по порядку; все три ступени функционально эквивалентны:
- SharedArrayBuffer — требует изоляции между источниками (COOP/COEP)
- postMessage — когда SAB недоступен
- Canvas2D в главном потоке — когда недоступны Worker или OffscreenCanvas
const root = await createHostedCanvasRoot(canvas, {
transport: { preference: "sab" }, // это лишь предпочтение; если оно недостижимо, произойдёт откат
});
console.log(root.mode); // "sab" | "post-message" | "main-thread"Playground этого сайта — живой пример: GitHub Pages не может отдавать заголовки COOP/COEP, поэтому опубликованная версия работает по postMessage, и значок транспорта наверху страницы честно это показывает.
Модель инвалидации
Семантика свойства определяет домен инвалидации. Вызывающий код ничего не помечает грязным вручную, и лазейки вроде forceUpdate нет.
Каждое свойство в схеме с единственным источником объявляет, на что оно влияет: раскладку, отрисовку, попадания или семантику. Смена opacity не вызывает переразметку, а смена width вызывает. Битовые карты «грязного» ведутся по доменам, а onFrame показывает число грязных узлов в каждом.
Выбор такой: «инвалидировать максимально узко, а страховкой служат property-тесты». Результат инкрементального рендеринга обязан совпадать с полным попиксельно, а дифференциальные тесты сжимают любой контрпример до минимального падающего случая.
Представление Scene
Внутри ядра Scene хранится как SoA (структура массивов вместо массива структур):
- Идентификаторы узлов содержат поколение: повторное использование слота никогда не оживляет устаревший идентификатор.
- После коммита сохраняется топологический порядок: родитель всегда идёт раньше детей.
- Уплотнение структурных правок выполняется один раз за коммит, а не на каждую мутацию.
- Результаты раскладки сравниваются пакетно по SoA с двойной буферизацией, без выделения замыканий и слушателей на узел в горячем пути.
Сменный бэкенд
Ядро выдаёт плоский бинарный DisplayList, а бэкенд — всего лишь проигрыватель. Бэкенд Canvas2D — это экономный к аллокациям цикл по типизированным массивам: вызов wasm→JS на каждую операцию рисования не является приемлемым путём рендеринга.
Тот же DisplayList подаётся в изолированный прототип на wgpu, и оба вывода сравниваются попиксельно. Принимать ли WebGPU — решение по данным, см. ADR-0006.
Детерминизм
Время, источник случайности и потоки ввода внедряемы или воспроизводимы, а вывод ядра не зависит от порядка планирования потоков. Архив DOPR записывает потоки Mutation и Input в исходном порядке и детерминированно воспроизводится без браузера, в headless-среде: благодаря этому проблему из продакшена можно повторить локально, а чувствительные потоки редактирования явно исключены из записи.
Углубиться
Полные алгоритмы, структуры данных и критерии приёмки собраны в документе технического дизайна.