Live-режим

По умолчанию проверка срабатывает один раз, на границе, и отдаёт вам обычный объект. Ничто не мешает этому объекту «уехать» мгновением позже:

const user: User = await fetch('/api/me').then(r => r.json())
// проверено ✓
user.age = 'двадцать'   // ✗ повторно не проверяется — это никто не поймает

Zod, Valibot и остальные работают так же: проверяют один раз и отдают обычный объект.

Live-режим держит тип живым всю жизнь объекта. Вместо одноразовой проверки плагин эмитит guard, который валидирует так же, но возвращает рекурсивный Proxy:

  • запись значения не того типа в известное поле — ловится;
  • запись поля, которого нет в типе, — ловится;
  • удаление обязательного поля — ловится;
  • вложенные объекты, массивы, record’ы и кортежи оборачиваются лениво при первом чтении (identity сохраняется — user.address === user.address).
const user: User = await fetch('/api/me').then(r => r.json())

user.age = 'двадцать'   // 💥 ValidationError на "user.age" — поймано на лету

Как включить

Управляется опцией live у плагина:

// vite.config.ts
t12n({
  mode: 'auto',
  live: true,   // форсим guard на каждой сборке
})
liveПоведение
trueЭмитить Proxy-guard — мутации тоже под наблюдением
falseЭмитить одноразовую проверку — только на границе
(не задано)По умолчанию: true на dev-сервере (vite serve), false для прод-сборки

Live ортогонален auto / manual / off: режим решает, куда ставятся проверки, а liveпродолжают ли они наблюдать после. Поставьте true, чтобы следить за мутациями и в проде (в паре с обработчиком — см. Ошибки и configure), или false, чтобы Proxy не использовался никогда.

Цена

У этого есть реальная цена: каждый доступ к свойству guarded-объекта идёт через trap. Поэтому по умолчанию Proxy работает только в dev, а в прод уходит дешёвая компилируемая проверка. Proxy к тому же не переживает structured-clone границы (postMessage, воркеры) — принимающая сторона просто заново оборачивает на своей границе.