# Coding Conventions — Vue 3 / Nuxt 4 (Nitro) / Node.js These rules are mandatory for all agents and developers. For high-level agent behavior, see `AGENTS.md`. ## 1. General Principles - Be conservative, explicit, and boring. Prefer the predictable solution. - When unsure, ask — don't guess. - Make minimal, targeted changes. Never refactor unrelated code. - Preserve existing structure, conventions, and tooling. - TypeScript strict mode (`strict: true` + `noUncheckedIndexedAccess`). - Composition API + ` ``` ## 5. Nitro / Server (Backend) - API routes: `server/api/**/*.ts` (file-based). - Validate all incoming data with Zod at the boundary (start of the handler). - Business logic lives in `server/utils/` or `server/services/`. - Handlers stay thin: validate → call service → return. - Errors: use `apiError()` from `~~/server/utils/api/error`, never raw `createError`. - No side effects at module top level. - Schemas: import from `~~/shared/schemas`, not from `server/utils/schemas`. ### Error format All API errors use a standard envelope: ```ts import { apiError } from '~~/server/utils/api/error' throw apiError(404, 'IDEA_NOT_FOUND', 'Идея не найдена') throw apiError(409, 'IDEA_LIMIT_REACHED', 'Достигнут лимит', { activeCount: 10 }) ``` Response: `{ error: { code: string, message: string, details?: unknown } }` ### Example API route ```ts // server/api/users/[id].get.ts import { parseUuid } from '~~/shared/schemas' import { getUserById } from '~~/server/utils/users' export default defineEventHandler(async (event) => { const id = parseUuid(getRouterParam(event, 'id')) return getUserById(id) }) ``` ## 6. TypeScript - `interface` for object shapes and public API shapes. - `type` for unions, utility types, mapped types. - Explicit return types on all exported functions. - `import type` for type-only imports. - Prefer `readonly` / `ReadonlyArray` where practical. - Narrow with type guards; avoid `as` assertions and `!` except as a last resort. - Prefer exhaustive handling of unions with `never` checks. - Treat caught errors as `unknown` and narrow before use. ## 7. Async & Error Handling - `async/await` only. Floating promises are forbidden. - Rethrow with context; preserve `cause` when available. Never throw strings. - Never swallow rejections or errors. - Client: handle via `useError` / `showError` or the project error boundary. - Server: `createError({ statusCode, statusMessage, data })`. ## 8. Runtime & Environment (Node.js) - Target the repo's supported Node LTS (check config/docs; don't assume versions). - No top-level side effects (I/O, network, env reads, global mutations) unless explicitly intended. - Env vars: validate centrally once at startup via Zod (`server/utils/env.ts`); read at runtime, not import time; never mutate env in app code (tests only, with scoped setup/teardown). - Library code must not log. CLIs may log intentionally, with consistent exit codes. ## 9. Logging & Security - Never log secrets (tokens, keys, passwords, personal data). - Validate/sanitize all external inputs: paths, URLs, user data (Zod at boundaries). - No `console.log` in production code. ## 10. Testing (Vitest) - New business logic requires tests. Exceptions: types-only code, re-exports, comments/formatting. - Tests must be deterministic and isolated; no shared mutable state. - Prefer behavioral tests; mock sparingly. - Cover failure paths, not only happy paths. - Bug fixes must include a regression test. - No committed `.only` / `.skip` unless explicitly justified. - Avoid snapshots unless they add clear value and are stable. - Unit: Vitest. Components: `@vue/test-utils` + Vitest. API: `nitro-test` or plain HTTP tests. - Tests are co-located: `*.test.ts` / `*.spec.ts`. ## 11. Accessibility (a11y) Accessibility is enforced at three levels. All three must pass before merging frontend changes. ### Editor-time: eslint-plugin-vuejs-accessibility ESLint rules catch common a11y mistakes in Vue templates as you type: - Missing `alt` on `` - Missing labels on form controls - Click events without keyboard equivalents - Invalid ARIA attributes Run with `pnpm lint`. Fix all `vuejs-accessibility/*` errors before committing. ### Runtime: @nuxt/a11y (DevTools) The `@nuxt/a11y` module runs axe-core in the browser during development: - Open Nuxt DevTools → "Nuxt a11y" tab - Click "Scan" to check the current page - Violations are grouped by severity (critical → minor) - Click a violation to highlight affected elements with numbered badges - Enable "Auto-Scan" for continuous monitoring Use this when building new pages or components — catch issues visually before they reach CI. ### CI: @axe-core/playwright Automated regression tests in `e2e/accessibility.spec.ts`: - Run via `pnpm e2e` - Tests scan key pages (homepage, ideas list, idea detail) against WCAG 2.0/2.1 AA - Any violation fails the test — blocks merge When adding a new page, add a corresponding test: ```ts test('new-page has no accessibility violations', async ({ page }) => { await page.goto('/new-page') const results = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']) .analyze() expect(results.violations).toEqual([]) }) ``` ## 12. Comments & Docs - Update docs/comments whenever behavior changes. - Comments explain "why", not "what". ## 12. Dependencies - Never add dependencies without explicit approval. - A justification must cover: need, alternatives, maintenance burden, license, security impact. ## 13. Strictly Prohibited - Options API - `any`, `@ts-ignore`, unjustified `// @ts-expect-error` - Default exports in components and utilities (except pages/layouts) - Editing already-applied migrations - Adding dependencies without approval - `console.log` in production code - Deep relative imports (`../../../`) - Module-level side effects - Duplicating types between `app/` and `server/` - Changing public APIs or introducing breaking changes without explicit instruction - Stylistic rewrites and micro-optimizations ## 14. Verify Before Committing - Typecheck + lint + tests pass. - New behavior has coverage, including failure paths. - No unintended snapshot changes. - No unnecessary diff churn. - No accidental top-level side effects. - Env usage is validated and intentional.