--- paths: frontend/**/*.{ts,tsx,jsx,js} --- # Frontend conventions — Next.js 15 / React 19 / TypeScript 5 ## Critical - **App router only** — НЕ `pages/` router - **TanStack Query** для data-fetching — никакого `useEffect` для HTTP запросов - **TS strict** — никаких `any`; для unknown shape → `unknown` + narrow - TanStack Query keys — массивы: `["scrape-logs", scraperType, runIdFilter]` - Tailwind 4 утилитарные классы; inline styles только для динамических значений - Server / Client components разделены явно — `"use client"` в начале client'а ## XSS prevention (recurring bug class) При рендере markdown / user-supplied content: - **URL validation**: allowlist schemes перед инъекцией в `href=` / `src=`: - ✅ `https://`, `http://`, `/`, `#`, `mailto:` - ❌ `javascript:`, `data:`, `vbscript:`, `file:` → fallback `#` - Pattern: helper `safeUrl(url: string): string` перед записью в attr Reference: vault `Bug_RenderMarkdown_JavascriptUrl_May14` (`renderMarkdown.ts:safeUrl`). ## After backend schema change ``` cd frontend && npm run codegen ``` Обновляет `src/types/openapi.ts` из live OpenAPI. Без этого frontend build упадёт на missing types. ## Prettier / lint - 2 spaces, trailing comma, double quotes (config) - `prettier` hook auto-rewrites файл → нужен повторный `git add` - ESLint в pre-commit: no unused imports, no console.log, exhaustive deps ## Запреты - ❌ `any` в TypeScript — `unknown` + type guard - ❌ `useEffect` для HTTP — TanStack Query - ❌ `pages/` router — только `app/` - ❌ `console.log` в prod-коде - ❌ Inline в `href`/`src` без validation (XSS vector) - ❌ Hardcoded URLs — `process.env.NEXT_PUBLIC_API_URL`