[{"data":1,"prerenderedAt":525},["ShallowReactive",2],{"navigation_docs_ru":3,"-ru-reference-validation-strategy":167,"-ru-reference-validation-strategy-surround":520},[4,61,101,146],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":60},"Руководство","i-lucide-book-open","\u002Fru\u002Fguide","ru\u002F1.guide",[10,15,20,25,30,35,40,45,50,55],{"title":11,"path":12,"stem":13,"icon":14},"Введение","\u002Fru\u002Fguide\u002Fintroduction","ru\u002F1.guide\u002F01.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Установка","\u002Fru\u002Fguide\u002Finstallation","ru\u002F1.guide\u002F02.installation","i-lucide-smartphone",{"title":21,"path":22,"stem":23,"icon":24},"Авторизация","\u002Fru\u002Fguide\u002Fauth","ru\u002F1.guide\u002F03.auth","i-lucide-lock",{"title":26,"path":27,"stem":28,"icon":29},"Кошельки","\u002Fru\u002Fguide\u002Fwallets","ru\u002F1.guide\u002F04.wallets","i-lucide-wallet",{"title":31,"path":32,"stem":33,"icon":34},"Категории","\u002Fru\u002Fguide\u002Fcategories","ru\u002F1.guide\u002F05.categories","i-lucide-tags",{"title":36,"path":37,"stem":38,"icon":39},"Транзакции","\u002Fru\u002Fguide\u002Ftransactions","ru\u002F1.guide\u002F06.transactions","i-lucide-receipt",{"title":41,"path":42,"stem":43,"icon":44},"Переводы","\u002Fru\u002Fguide\u002Ftransfers","ru\u002F1.guide\u002F07.transfers","i-lucide-arrow-left-right",{"title":46,"path":47,"stem":48,"icon":49},"Статистика","\u002Fru\u002Fguide\u002Fstatistics","ru\u002F1.guide\u002F08.statistics","i-lucide-bar-chart-3",{"title":51,"path":52,"stem":53,"icon":54},"Тема","\u002Fru\u002Fguide\u002Ftheme","ru\u002F1.guide\u002F09.theme","i-lucide-palette",{"title":56,"path":57,"stem":58,"icon":59},"Настройки","\u002Fru\u002Fguide\u002Fsettings","ru\u002F1.guide\u002F10.settings","i-lucide-settings",false,{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":60},"Разработка","i-lucide-code","\u002Fru\u002Fdevelopment","ru\u002F2.development",[67,71,76,81,86,91,96],{"title":16,"path":68,"stem":69,"icon":70},"\u002Fru\u002Fdevelopment\u002Finstallation","ru\u002F2.development\u002F01.installation","i-lucide-download",{"title":72,"path":73,"stem":74,"icon":75},"Граф кодовой базы","\u002Fru\u002Fdevelopment\u002Funderstand-anything","ru\u002F2.development\u002F02.understand-anything","i-lucide-network",{"title":77,"path":78,"stem":79,"icon":80},"Офлайн и PWA","\u002Fru\u002Fdevelopment\u002Foffline","ru\u002F2.development\u002F03.offline","i-lucide-wifi-off",{"title":82,"path":83,"stem":84,"icon":85},"История миграций данных","\u002Fru\u002Fdevelopment\u002Fmigration","ru\u002F2.development\u002F04.migration","i-lucide-database",{"title":87,"path":88,"stem":89,"icon":90},"Деплой","\u002Fru\u002Fdevelopment\u002Fdeployment","ru\u002F2.development\u002F05.deployment","i-lucide-rocket",{"title":92,"path":93,"stem":94,"icon":95},"Тестирование","\u002Fru\u002Fdevelopment\u002Ftesting","ru\u002F2.development\u002F06.testing","i-lucide-flask-conical",{"title":97,"path":98,"stem":99,"icon":100},"Дата-утилиты","\u002Fru\u002Fdevelopment\u002Fdate-utilities","ru\u002F2.development\u002F07.date-utilities","i-lucide-calendar",{"title":102,"icon":103,"path":104,"stem":105,"children":106,"page":60},"Справочник","i-lucide-file-code","\u002Fru\u002Freference","ru\u002F3.reference",[107,112,116,121,126,131,136,141],{"title":108,"path":109,"stem":110,"icon":111},"Архитектура","\u002Fru\u002Freference\u002Farchitecture","ru\u002F3.reference\u002F01.architecture","i-lucide-boxes",{"title":113,"path":114,"stem":115,"icon":44},"Типы транзакций","\u002Fru\u002Freference\u002Ftransaction-types","ru\u002F3.reference\u002F02.transaction-types",{"title":117,"path":118,"stem":119,"icon":120},"Синхронизация","\u002Fru\u002Freference\u002Fsync","ru\u002F3.reference\u002F03.sync","i-lucide-refresh-cw",{"title":122,"path":123,"stem":124,"icon":125},"Офлайн-first","\u002Fru\u002Freference\u002Foffline-first","ru\u002F3.reference\u002F04.offline-first","i-lucide-list-ordered",{"title":127,"path":128,"stem":129,"icon":130},"Тех. решения","\u002Fru\u002Freference\u002Ftech-decisions","ru\u002F3.reference\u002F05.tech-decisions","i-lucide-lightbulb",{"title":132,"path":133,"stem":134,"icon":135},"Валидация","\u002Fru\u002Freference\u002Fvalidation-strategy","ru\u002F3.reference\u002F06.validation-strategy","i-lucide-shield-check",{"title":137,"path":138,"stem":139,"icon":140},"Что изменилось со времён Firebase","\u002Fru\u002Freference\u002Ffirebase-migration","ru\u002F3.reference\u002F07.firebase-migration","i-lucide-hamburger",{"title":142,"path":143,"stem":144,"icon":145},"Производительность","\u002Fru\u002Freference\u002Fperformance","ru\u002F3.reference\u002F08.performance","i-lucide-gauge",{"title":147,"icon":148,"path":149,"stem":150,"children":151,"page":60},"Премиум","i-lucide-star","\u002Fru\u002Fpremium","ru\u002F4.premium",[152,157,162],{"title":153,"path":154,"stem":155,"icon":156},"Обзор","\u002Fru\u002Fpremium\u002Foverview","ru\u002F4.premium\u002F01.overview","i-lucide-layers",{"title":158,"path":159,"stem":160,"icon":161},"Telegram-бот","\u002Fru\u002Fpremium\u002Ftelegram-bot","ru\u002F4.premium\u002F02.telegram-bot","i-lucide-send",{"title":163,"path":164,"stem":165,"icon":166},"AI Chat","\u002Fru\u002Fpremium\u002Fai-chat","ru\u002F4.premium\u002F03.ai-chat","i-lucide-sparkles",{"id":168,"title":132,"body":169,"description":511,"extension":512,"links":513,"meta":514,"navigation":515,"path":133,"seo":516,"stem":134,"__hash__":519},"docs_ru\u002Fru\u002F3.reference\u002F06.validation-strategy.md",{"type":170,"value":171,"toc":500},"minimark",[172,177,186,237,242,345,349,354,397,402,452,458,462,465,468,483,487],[173,174,176],"h2",{"id":175},"текущий-подход-только-клиентская-валидация","Текущий подход: только клиентская валидация",[178,179,180,181,185],"p",{},"Валидация выполняется ",[182,183,184],"strong",{},"только на клиенте",". Серверных валидаторов бизнес-правил нет. Сервер (Supabase Postgres) обеспечивает только типы колонок, ограничения NOT NULL, политики RLS для владельца строк и правила синхронизации по пользователям.",[187,188,189,205],"table",{},[190,191,192],"thead",{},[193,194,195,199,202],"tr",{},[196,197,198],"th",{},"Слой",[196,200,201],{},"Фреймворк",[196,203,204],{},"Назначение",[206,207,208,223],"tbody",{},[193,209,210,217,220],{},[211,212,213,216],"td",{},[182,214,215],{},"Клиент"," (браузер)",[211,218,219],{},"Zod v4",[211,221,222],{},"UX форм - дефолты, трим, мгновенные ошибки; проверка формата данных на рантайме",[193,224,225,231,234],{},[211,226,227,230],{},[182,228,229],{},"Сервер"," (Supabase Postgres)",[211,232,233],{},"Типы колонок + RLS",[211,235,236],{},"Типы колонок, NOT NULL, владелец строки (политика RLS)",[238,239,241],"h3",{"id":240},"где-живёт-валидация","Где живёт валидация",[187,243,244,254],{},[190,245,246],{},[193,247,248,251],{},[196,249,250],{},"Сущность \u002F применение",[196,252,253],{},"Zod-схема",[206,255,256,279,293,306,319,332],{},[193,257,258,261],{},[211,259,260],{},"Форма транзакции",[211,262,263,267,268,271,272,275,276],{},[264,265,266],"code",{},"app\u002Fapp\u002Fcomponents\u002Ftrns\u002Ftypes.ts"," (",[264,269,270],{},"trnItemSchema",", ",[264,273,274],{},"transactionSchema",") + ",[264,277,278],{},"app\u002Fapp\u002Fcomponents\u002FtrnForm\u002Futils\u002Fvalidate.ts",[193,280,281,284],{},[211,282,283],{},"Конфиг статистики",[211,285,286,267,289,292],{},[264,287,288],{},"app\u002Fapp\u002Fcomponents\u002Fstat\u002FuseStatConfig.ts",[264,290,291],{},"ConfigSchema",")",[193,294,295,298],{},[211,296,297],{},"Дата \u002F query-параметры",[211,299,300,267,303,292],{},[264,301,302],{},"app\u002Fapp\u002Fcomponents\u002Fdate\u002FstatDateParams.ts",[264,304,305],{},"queryParamsSchema",[193,307,308,311],{},[211,309,310],{},"Настройки пользователя",[211,312,313,267,316,292],{},[264,314,315],{},"app\u002Fapp\u002Fcomponents\u002Fuser\u002Ftypes.ts",[264,317,318],{},"userSettingsSchema",[193,320,321,324],{},[211,322,323],{},"Формат курсов валют",[211,325,326,267,329,292],{},[264,327,328],{},"app\u002Fapp\u002Fcomponents\u002Fcurrencies\u002Ftypes.ts",[264,330,331],{},"ratesSchema",[193,333,334,337],{},[211,335,336],{},"Локаль",[211,338,339,267,342,292],{},[264,340,341],{},"app\u002Fapp\u002Fcomponents\u002Flocale\u002Ftypes.ts",[264,343,344],{},"localeSchema",[238,346,348],{"id":347},"что-проверяет-каждая-сторона","Что проверяет каждая сторона",[178,350,351],{},[182,352,353],{},"Zod (клиент):",[355,356,357,364,370,376,382,391],"ul",{},[358,359,360,363],"li",{},[264,361,362],{},".trim().min(1)"," - не пропускает пустые строки после трима",[358,365,366,369],{},[264,367,368],{},".positive()"," на суммах",[358,371,372,375],{},[264,373,374],{},".default()"," для начальных значений форм",[358,377,378,381],{},[264,379,380],{},".transform()"," для парсинга query-параметров (строка в число \u002F булево)",[358,383,384,385,387,388,292],{},"Discriminated union для типа транзакции (",[264,386,270],{}," = ",[264,389,390],{},"transactionSchema | transferSchema",[358,392,393,394],{},"Проверка одинаковых кошельков перевода: ",[264,395,396],{},"incomeWalletId !== expenseWalletId",[178,398,399],{},[182,400,401],{},"Supabase Postgres (сервер):",[355,403,404,422,428,435,445],{},[358,405,406,407,271,410,271,413,271,416,271,419],{},"Типы колонок: ",[264,408,409],{},"text",[264,411,412],{},"numeric",[264,414,415],{},"integer",[264,417,418],{},"boolean",[264,420,421],{},"jsonb",[358,423,424,427],{},[264,425,426],{},"NOT NULL"," на обязательных колонках",[358,429,430,431,434],{},"RLS: каждая пользовательская таблица имеет ",[264,432,433],{},"(select auth.uid())::text = \"userId\""," - строки других пользователей невидимы, запись отклоняется",[358,436,437,438,441,442],{},"Таблица ",[264,439,440],{},"rates"," доступна только для чтения аутентифицированным пользователям (без записи с клиента); строки пишет серверная edge-функция ",[264,443,444],{},"fetch-rates",[358,446,447,448,451],{},"Триггер автоматически создаёт ",[264,449,450],{},"user_settings"," при регистрации (начальная инициализация на сервере не нужна)",[178,453,454,457],{},[182,455,456],{},"Серверных валидаторов бизнес-правил нет"," (нет серверных ограничений длины строк, диапазонов чисел, кросс-полевой валидации). Эти правила живут только в Zod-схемах на клиенте.",[238,459,461],{"id":460},"компромисс","Компромисс",[178,463,464],{},"Целенаправленный клиент, обходящий форму, может записать произвольные данные в локальную SQLite, которые PowerSync загрузит в Supabase. Postgres примет их, если они проходят типы колонок и RLS. Это осознанный компромисс для однопользовательского приложения личных финансов - дублирование каждого бизнес-правила в виде Postgres-ограничений или политик RLS не оправдано по трудозатратам.",[178,466,467],{},"Если в будущем потребуется более строгая проверка, варианты:",[355,469,470,477,480],{},[358,471,472,473,476],{},"Postgres ",[264,474,475],{},"CHECK"," ограничения для диапазонов и длин",[358,478,479],{},"Supabase Edge Function как прокси загрузки с серверной Zod-валидацией",[358,481,482],{},"Оценка, покрывает ли встроенная валидация Supabase (pg_jsonschema, check constraints) критические правила",[173,484,486],{"id":485},"следующие-шаги","Следующие шаги",[355,488,489,495],{},[358,490,491,494],{},[492,493,127],"a",{"href":128}," - связанные архитектурные решения и их обоснование",[358,496,497,499],{},[492,498,108],{"href":109}," - структура проекта и паттерн хранилищ",{"title":501,"searchDepth":502,"depth":502,"links":503},"",2,[504,510],{"id":175,"depth":502,"text":176,"children":505},[506,508,509],{"id":240,"depth":507,"text":241},3,{"id":347,"depth":507,"text":348},{"id":460,"depth":507,"text":461},{"id":485,"depth":502,"text":486},"Клиентская валидация и компромиссы.","md",null,{},{"icon":135},{"title":517,"description":518},"Стратегия валидации","В Финапке используется только клиентская валидация с Zod v4. Где живут схемы, что каждая из них проверяет и что обеспечивает сервер.","hKH2biZ-EeeA0iOPiYOh_8fE8mCv4BAhRx_4cxk-jao",[521,523],{"title":127,"path":128,"stem":129,"description":522,"icon":130,"children":-1},"Обоснование ключевых решений.",{"title":137,"path":138,"stem":139,"description":524,"icon":140,"children":-1},"Что изменилось между v6 (Firebase) и текущим приложением на Supabase + PowerSync - бэкенд, производительность, безопасность, тесты, архитектура.",1782114341054]