Files
frontend/.ai/conventions/data.md
T
ksv741 69997075e5
Frontend verify / commits (pull_request) Failing after 27s
Frontend verify / security (pull_request) Skipped
Frontend verify / verify (pull_request) Skipped
refactor!: simplify query and session ownership
Use Query cancellation and native mutation results instead of HTTP
registries, access epochs and copied scope state.
Keep local auth ordering and mutation identity fences.
Allow staged generated files to remain excluded from ESLint.

Backend contract: WealthLedger/backend@d698ed9

BREAKING CHANGE: drop investment stateVersion/progress/resources
from the client contract and use data/metadata readiness instead of
rewritten query status. Cross-tab auth may require re-login.
2026-10-03 11:45:04 +03:00

7.3 KiB
Raw Permalink Blame History

Frontend data

Выбор owner

Server state принадлежит TanStack Query, form state — React Hook Form, локальное UI — React state. Не копируй успешный query result в useState/context для второго кэша. Domain-specific queries, keys и policies принадлежат вызывающему slice; общий transport — shared/api. Page VM выбирает inputs и собирает presentation data.

Используй apiClient/apiQueryClient из shared public API. Прямой fetch в pages/features/entities и прямой T-Bank call запрещены. Сначала найди существующий query того же endpoint; расширяй его owner, не создавай второй key или глобальную query policy для одной страницы.

Типы и contract

Backend-owned OpenAPI contract поступает во frontend как предоставленный актуальный snapshot; generated schema.d.ts производен от него. Тип request/response бери из generated paths, например:

type User = paths["/api/v1/auth/me"]["get"]["responses"][200]["content"]["application/json"];

Domain/UI type допустим, когда он отличается по смыслу, а не копирует transport DTO. Generated TS вручную не исправляется. Не добавляй параллельный fetch/axios client, runtime schema, hash/checksum registry или дополнительный drift checker без отдельной текущей потребности; existing OpenAPI flow уже владеет генерацией. Frontend-команды не читают соседний backend checkout и не проверяют backend runtime OpenAPI; изменение wire shape/status/route сохраняет публичный контракт предоставленного snapshot. Одна UI-модель не требует менять API. openapi:generate работает offline от snapshot; openapi:check генерирует во временный каталог и сравнивает, не меняя tracked files.

Auth и invalidation

Bearer header принадлежит shared authenticated fetch; query/mutation не задают Authorization. Auth endpoints исключены из Bearer/retry middleware. Refresh координируется single-flight; после 401 с UNAUTHORIZED возможен один retry. Не добавляй собственный refresh/retry loop в caller.

Cookie-writing requests упорядочены только в текущей вкладке. Cross-tab auth synchronization не требуется; допустим reload/re-login. Web Locks и отдельные token stores не добавляются.

Protected queries используют existing session-aware boundary; logout очищает session-scoped cache. Access token хранится только в памяти, refresh передаётся httpOnly cookie с credentials: include. Не сохраняй tokens в localStorage, URL, логах или fixtures. Logout очищает локальную session/cache и при ошибке сети; до окончания auth bootstrap показывай neutral state. Перед auth change прочитай shared client, session owner и их tests. Успешная connection mutation коммитит подтверждённый status и сбрасывает financial GET subtree; ошибка mutation не меняет generation/cache. Это отдельная граница от обычного фонового refetch. Даже повторное сохранение того же token начинает новую generation; поздний ответ прежней generation не попадает в новый кэш.

Отмена/reset принадлежат TanStack Query и generated queryFn с AbortSignal, без HTTP request registry, URL-wide latest-read counters и access epochs. Успешный HTTP с data: null остаётся query success; readiness выбирается по data/metadata, без подмены query status и отдельного transport state. Status синхронизации содержит только состояние работы. Resources читаются через sync=observe во время работы и после завершения, без hash comparison. Согласованность income entries/groups обеспечивает backend; frontend не дублирует её runtime validator.

Mutations используют существующий generated client и query-key owner; после успеха обновляй/invalidируй только связанные данные. Optimistic update нужен конкретному UX и обязан иметь rollback/error behavior.

Default focus refetch выключен; freshness и retry задаются существующими query options у entity/feature owner. После staleTime повторное открытие экрана может запустить background refetch; focus сам по себе его не запускает. При обычном refetch сохраняй Client Cached Snapshot, при session/connection смене не показывай данные прежнего владельца. UI presentation — в UI convention.

Errors

Извлекай code через existing apiErrorCode из shared/api. Backend Problem Details code задаёт machine behavior; user-facing текст принадлежит i18n. Network/unknown error получает безопасное локальное сообщение и retry, не raw exception text. Input/domain failure сохраняет ввод и показывает ошибку у ответственного form/section owner; не маскируй её успешным результатом.

Если одна группа API codes требует одинакового recovery в нескольких views, сведи классификацию у ближайшего общего owner. Не размножай magic comparisons и используй ApiErrorCode для известных значений: generated code остаётся string. Неизвестный новый code должен сохранять fallback. Рефакторинг существующих разных recovery paths требует behavior proof, а не общей обработки всех ошибок.

Примеры и проверка

usePortfolio — entity query; shared client — transport/auth boundary. Изменяя header/retry/invalidation, проверяй наблюдаемое поведение через MSW и Testing; не обращайся к реальному T-Bank. npm run openapi:check, npm run typecheck и targeted tests проверяют разные части решения. Для поведения API-клиента используй сценарии MSW на публичной HTTP-границе; реализация сервера не является входом frontend-проверок.