code-ai-installer 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/a11y_baseline/SKILL.md +41 -0
- package/.agents/adr_log/SKILL.md +69 -0
- package/.agents/api_contract_compliance_review/SKILL.md +18 -0
- package/.agents/api_contracts/SKILL.md +42 -0
- package/.agents/architecture_compliance_review/SKILL.md +17 -0
- package/.agents/architecture_doc/SKILL.md +92 -0
- package/.agents/board/SKILL.md +43 -0
- package/.agents/cloud_infrastructure_security/SKILL.md +68 -0
- package/.agents/code_review_checklist/SKILL.md +47 -0
- package/.agents/current_state_analysis/SKILL.md +44 -0
- package/.agents/data_model/SKILL.md +40 -0
- package/.agents/dependency_supply_chain_review/SKILL.md +20 -0
- package/.agents/deployment_ci_plan/SKILL.md +51 -0
- package/.agents/design_intake/SKILL.md +71 -0
- package/.agents/design_parity_review/SKILL.md +73 -0
- package/.agents/design_systems/SKILL.md +15 -0
- package/.agents/dev_reference_snippets/SKILL.md +397 -0
- package/.agents/docker_kubernetes_architecture/SKILL.md +145 -0
- package/.agents/es2025_beast_practices/SKILL.md +15 -0
- package/.agents/gates/SKILL.md +35 -0
- package/.agents/go_beast_practices/SKILL.md +23 -0
- package/.agents/handoff/SKILL.md +52 -0
- package/.agents/k8s_manifests_conventions/SKILL.md +176 -0
- package/.agents/memory/SKILL.md +29 -0
- package/.agents/mongodb_mongoose_best_practices/SKILL.md +236 -0
- package/.agents/node_express_beast_practices/SKILL.md +30 -0
- package/.agents/observability_logging/SKILL.md +16 -0
- package/.agents/observability_plan/SKILL.md +38 -0
- package/.agents/observability_review/SKILL.md +20 -0
- package/.agents/performance_review_baseline/SKILL.md +17 -0
- package/.agents/pm_backlog/SKILL.md +32 -0
- package/.agents/pm_interview/SKILL.md +56 -0
- package/.agents/pm_prd/SKILL.md +56 -0
- package/.agents/qa_api_contract_tests/SKILL.md +16 -0
- package/.agents/qa_e2e_playwright/SKILL.md +0 -0
- package/.agents/qa_manual_run/SKILL.md +16 -0
- package/.agents/qa_security_smoke_tests/SKILL.md +14 -0
- package/.agents/qa_test_plan/SKILL.md +20 -0
- package/.agents/qa_ui_a11y_smoke/SKILL.md +12 -0
- package/.agents/react_15_3_wix_iframe/SKILL.md +20 -0
- package/.agents/react_beast_practices/SKILL.md +29 -0
- package/.agents/release_gate/SKILL.md +77 -0
- package/.agents/release_gate_checklist_template/SKILL.md +68 -0
- package/.agents/review_reference_snippets/SKILL.md +437 -0
- package/.agents/security_baseline_dev/SKILL.md +16 -0
- package/.agents/security_review/SKILL.md +55 -0
- package/.agents/security_review_baseline/SKILL.md +25 -0
- package/.agents/state_rtk_beast_practices/SKILL.md +15 -0
- package/.agents/state_zustand_beast_practices/SKILL.md +11 -0
- package/.agents/styling_css_stack/SKILL.md +12 -0
- package/.agents/system_design_checklist/SKILL.md +48 -0
- package/.agents/tanstack_beast_practices/SKILL.md +19 -0
- package/.agents/tdd_workflow/SKILL.md +34 -0
- package/.agents/testing_strategy_js/SKILL.md +30 -0
- package/.agents/tests_quality_review/SKILL.md +18 -0
- package/.agents/threat_model_baseline/SKILL.md +57 -0
- package/.agents/tooling_bun_biome/SKILL.md +17 -0
- package/.agents/typescript_beast_practices/SKILL.md +15 -0
- package/.agents/ui_a11y_smoke_review/SKILL.md +15 -0
- package/.agents/ui_inventory/SKILL.md +50 -0
- package/.agents/ux_discovery/SKILL.md +48 -0
- package/.agents/ux_spec/SKILL.md +56 -0
- package/.agents/wix_self_hosted_embedded_script/SKILL.md +88 -0
- package/AGENTS.md +120 -0
- package/README.md +6 -1
- package/agents/architect.md +242 -0
- package/agents/conductor.md +207 -0
- package/agents/product_manager.md +121 -0
- package/agents/reviewer.md +201 -0
- package/agents/senior_full_stack.md +218 -0
- package/agents/tester.md +187 -0
- package/agents/ux_ui_designer.md +145 -0
- package/dist/index.js +62 -23
- package/dist/sourceResolver.d.ts +10 -0
- package/dist/sourceResolver.js +36 -0
- package/package.json +5 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design_intake
|
|
3
|
+
description: Найти и проанализировать предоставленные дизайн-материалы (файлы/ссылки), определить “источник правды” по UI и извлечь проверяемые правила (экраны, компоненты, токены, состояния).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Design Intake (сбор дизайн-источников)
|
|
7
|
+
|
|
8
|
+
## Цель
|
|
9
|
+
Зафиксировать, какие дизайн-артефакты являются источником правды, и извлечь из них проверяемые правила для UX Spec и разработки.
|
|
10
|
+
|
|
11
|
+
## Когда использовать
|
|
12
|
+
- В репозиторий добавили дизайн-файлы (PDF/PNG/SVG/FIG и т.п.)
|
|
13
|
+
- Пользователь дал ссылку на Figma/другие макеты
|
|
14
|
+
- Перед подготовкой UX Spec или перед финальной сверкой UI
|
|
15
|
+
|
|
16
|
+
## Входы
|
|
17
|
+
- Пути к файлам в репозитории (если есть)
|
|
18
|
+
- Ссылки (если есть, например Figma)
|
|
19
|
+
- PRD/UX Spec (если уже существуют)
|
|
20
|
+
|
|
21
|
+
## Поиск дизайн-артефактов (эвристика)
|
|
22
|
+
Проверь наличие (если репозиторий доступен):
|
|
23
|
+
- Папки: `design/`, `docs/design/`, `assets/design/`, `.figma/`, `ui/`
|
|
24
|
+
- Файлы: `*.fig`, `*.pdf`, `*.png`, `*.jpg`, `*.jpeg`, `*.svg`
|
|
25
|
+
- Документы: `README.md`, `docs/*` на наличие слова “Figma” и ссылок
|
|
26
|
+
|
|
27
|
+
## Алгоритм
|
|
28
|
+
1) **Собери источники**:
|
|
29
|
+
- список файлов/папок (пути)
|
|
30
|
+
- список ссылок (если даны)
|
|
31
|
+
2) **Определи “источник правды”** (Source of Truth):
|
|
32
|
+
- главная страница/файл для экранов MVP
|
|
33
|
+
- отдельные страницы/разделы для компонентов/токенов (если есть)
|
|
34
|
+
3) **Извлеки спецификацию**:
|
|
35
|
+
- список экранов и их назначение
|
|
36
|
+
- структура экрана (основные секции)
|
|
37
|
+
- компоненты и варианты (primary/secondary, sizes, states)
|
|
38
|
+
- состояния: loading/empty/error/success (если отражены)
|
|
39
|
+
- ключевые правила контента (ошибки, подсказки, тексты кнопок)
|
|
40
|
+
4) **Собери Design Reference Map**:
|
|
41
|
+
- Screen → где описан (файл/страница/кадр)
|
|
42
|
+
- Component → где описан
|
|
43
|
+
5) **Сформируй “Design Rules Summary”**:
|
|
44
|
+
- 5–20 правил, которые можно проверять (без субъективщины)
|
|
45
|
+
|
|
46
|
+
## Выход (Deliverables)
|
|
47
|
+
### Design Sources
|
|
48
|
+
- Files:
|
|
49
|
+
- Links:
|
|
50
|
+
|
|
51
|
+
### Source of Truth
|
|
52
|
+
- Screens:
|
|
53
|
+
- Components:
|
|
54
|
+
- Tokens/Guidelines:
|
|
55
|
+
|
|
56
|
+
### Design Reference Map
|
|
57
|
+
- Screens:
|
|
58
|
+
- Components:
|
|
59
|
+
|
|
60
|
+
### Design Rules Summary
|
|
61
|
+
- Rule 1:
|
|
62
|
+
- Rule 2:
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
## Формат ответа
|
|
66
|
+
### Summary
|
|
67
|
+
### Deliverables (Design Sources + Reference Map + Rules)
|
|
68
|
+
### Decisions
|
|
69
|
+
### Risks/Blockers
|
|
70
|
+
### Open Questions
|
|
71
|
+
### Next Actions (IDs: UX-xx)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design_parity_review
|
|
3
|
+
description: Сравнить дизайн-артефакты с UX Spec или с реализованным UI. Выдать отчёт о расхождениях (Design Parity Report) с приоритетами P0/P1/P2 и конкретными рекомендациями.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Design Parity Review (сверка дизайна)
|
|
7
|
+
|
|
8
|
+
## Цель
|
|
9
|
+
Проверить соответствие UX Spec и/или реализованного UI предоставленным дизайн-материалам и зафиксировать расхождения.
|
|
10
|
+
|
|
11
|
+
## Когда использовать
|
|
12
|
+
- После подготовки UX Spec (Design ↔ UX Spec)
|
|
13
|
+
- После реализации UI (Design ↔ Implemented UI)
|
|
14
|
+
- Перед закрытием релизного инкремента (как часть DoD при наличии дизайна)
|
|
15
|
+
|
|
16
|
+
## Входы
|
|
17
|
+
- Design Reference Map и Rules (из `$design_intake`)
|
|
18
|
+
- UX Spec (если проверяем спеку)
|
|
19
|
+
- Реализация UI (если проверяем код):
|
|
20
|
+
- ссылки/роуты, как воспроизвести состояния
|
|
21
|
+
- скриншоты/видео/Storybook (если есть)
|
|
22
|
+
- результаты e2e/visual (если есть)
|
|
23
|
+
|
|
24
|
+
## Режимы проверки
|
|
25
|
+
### Режим A: Design ↔ UX Spec (кода ещё нет)
|
|
26
|
+
Проверь по каждому экрану:
|
|
27
|
+
- состав секций и их порядок
|
|
28
|
+
- основные действия/CTA
|
|
29
|
+
- формы: поля, обязательность, валидация, тексты ошибок
|
|
30
|
+
- состояния: loading/empty/error/success
|
|
31
|
+
- совпадение компонентов с inventory/гайдами
|
|
32
|
+
Выход: список правок к UX Spec + вопросы/конфликты.
|
|
33
|
+
|
|
34
|
+
### Режим B: Design ↔ Implemented UI (UI уже реализован)
|
|
35
|
+
Проверь по каждому ключевому экрану и состоянию:
|
|
36
|
+
- layout/иерархия (что где расположено, что видно первым)
|
|
37
|
+
- наличие и варианты компонентов (кнопки, инпуты, таблицы, модалки)
|
|
38
|
+
- тексты/лейблы/плейсхолдеры/сообщения об ошибках (если дизайн их задаёт)
|
|
39
|
+
- UI состояния (loading/empty/error/success)
|
|
40
|
+
- базовая a11y (keyboard/focus/labels/ARIA) по `$a11y_baseline`
|
|
41
|
+
|
|
42
|
+
## Классификация расхождений
|
|
43
|
+
- **P0 (Blocker)**: ломает критический flow, вводит в заблуждение, нарушает доступность, приводит к ошибкам/потере данных, противоречит PRD.
|
|
44
|
+
- **P1 (Important)**: нарушает консистентность, ухудшает UX, но не блокирует использование.
|
|
45
|
+
- **P2 (Nice-to-have)**: косметика/полировка.
|
|
46
|
+
|
|
47
|
+
## Выход: Design Parity Report (обязательная структура)
|
|
48
|
+
### ✅ Совпадает
|
|
49
|
+
- ...
|
|
50
|
+
|
|
51
|
+
### ⚠️ Расхождения
|
|
52
|
+
#### P0 (Blockers)
|
|
53
|
+
- [ ] <что не так> — Screen/Component: ... — Expected: ... — Actual: ... — Fix: ...
|
|
54
|
+
|
|
55
|
+
#### P1 (Important)
|
|
56
|
+
- [ ] ...
|
|
57
|
+
|
|
58
|
+
#### P2 (Nice-to-have)
|
|
59
|
+
- [ ] ...
|
|
60
|
+
|
|
61
|
+
### Рекомендации по исправлению
|
|
62
|
+
- DEV-xx: ...
|
|
63
|
+
- UX-xx: ...
|
|
64
|
+
|
|
65
|
+
### Конфликты (если есть)
|
|
66
|
+
- Конфликт: <design vs PRD/архитектура> → варианты решения
|
|
67
|
+
|
|
68
|
+
## Формат ответа
|
|
69
|
+
### Summary
|
|
70
|
+
### Deliverables (Design Parity Report)
|
|
71
|
+
### Findings (P0/P1/P2)
|
|
72
|
+
### Risks/Blockers
|
|
73
|
+
### Next Actions (IDs: UX-xx / DEV-xx)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design_systems
|
|
3
|
+
description: Интеграция дизайн-систем: shadcn/ui, mantine, Wix Design System; соблюдение UI inventory/UX spec и единых компонентов.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Design Systems Integration
|
|
7
|
+
|
|
8
|
+
## Цель
|
|
9
|
+
Собрать UI из готовых, консистентных компонентов и не “рисовать велосипед”.
|
|
10
|
+
|
|
11
|
+
## Правила
|
|
12
|
+
- Предпочитать shadcn/ui для новых проектов (если подходит стек)
|
|
13
|
+
- Mantine — если выбран проектом/есть причины
|
|
14
|
+
- Wix Design System — для Wix-экосистемы (если указано)
|
|
15
|
+
- Компоненты должны поддерживать состояния из UX Spec (loading/empty/error/disabled)
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev_reference_snippets
|
|
3
|
+
description: Единый файл со сниппетами и анти-примерами (do/don’t) для Senior Full Stack: TDD, API, валидация, ошибки, логирование, React, state (Zustand/RTK), безопасность, legacy React 15.3 (Wix), DoD-скрипты.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Dev Reference Snippets (Do/Don't)
|
|
7
|
+
|
|
8
|
+
## Цель
|
|
9
|
+
Дать копипаст-эталоны и анти-паттерны, чтобы код был консистентным, тестируемым (TDD), безопасным и удобным в поддержке.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1) TDD: RED → GREEN → REFACTOR (пример)
|
|
14
|
+
|
|
15
|
+
### ✅ DO: тест сначала (Vitest)
|
|
16
|
+
```ts
|
|
17
|
+
// src/lib/slugify.test.ts
|
|
18
|
+
import { describe, it, expect } from "vitest";
|
|
19
|
+
import { slugify } from "./slugify";
|
|
20
|
+
|
|
21
|
+
describe("slugify", () => {
|
|
22
|
+
it("makes lowercase and replaces spaces with hyphens", () => {
|
|
23
|
+
expect(slugify("Hello World")).toBe("hello-world");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("removes non-alphanumeric characters", () => {
|
|
27
|
+
expect(slugify("Hi, John!")).toBe("hi-john");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("collapses multiple spaces", () => {
|
|
31
|
+
expect(slugify(" Hello World ")).toBe("hello-world");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ✅ GREEN: минимальная реализация
|
|
37
|
+
```ts
|
|
38
|
+
// src/lib/slugify.ts
|
|
39
|
+
export function slugify(input: string): string {
|
|
40
|
+
return input
|
|
41
|
+
.trim()
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
44
|
+
.replace(/\s+/g, "-");
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### ❌ DON'T: “сначала код, потом тесты” + тестирование деталей
|
|
49
|
+
```ts
|
|
50
|
+
// Плохо: тест проверяет внутренние переменные/шаги реализации, а не поведение.
|
|
51
|
+
// Итог: тесты ломаются при рефакторинге без изменения поведения.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 2) API: слои Route → Controller → Service → Repo (Express)
|
|
57
|
+
|
|
58
|
+
### ✅ DO: структура проекта (пример)
|
|
59
|
+
```txt
|
|
60
|
+
src/
|
|
61
|
+
http/
|
|
62
|
+
routes/
|
|
63
|
+
controllers/
|
|
64
|
+
middleware/
|
|
65
|
+
domain/
|
|
66
|
+
services/
|
|
67
|
+
models/
|
|
68
|
+
data/
|
|
69
|
+
repos/
|
|
70
|
+
lib/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### ✅ DO: Routes тонкие, логика в controller/service
|
|
74
|
+
```ts
|
|
75
|
+
// src/http/routes/users.routes.ts
|
|
76
|
+
import { Router } from "express";
|
|
77
|
+
import { createUser } from "../controllers/users.create";
|
|
78
|
+
import { requireAuth } from "../middleware/requireAuth";
|
|
79
|
+
|
|
80
|
+
export const usersRouter = Router();
|
|
81
|
+
|
|
82
|
+
usersRouter.post("/", requireAuth, createUser);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### ❌ DON'T: всё в одном файле роутов
|
|
86
|
+
```ts
|
|
87
|
+
// Плохо: в routes файл запихана валидация, бизнес-логика и доступ к БД.
|
|
88
|
+
// Тестирование и переиспользование становится болью.
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 3) Валидация на границе + безопасные ошибки (Zod)
|
|
94
|
+
|
|
95
|
+
### ✅ DO: parse на входе, безопасная обработка ошибок
|
|
96
|
+
```ts
|
|
97
|
+
// src/http/controllers/users.create.ts
|
|
98
|
+
import { z } from "zod";
|
|
99
|
+
import type { Request, Response, NextFunction } from "express";
|
|
100
|
+
import { usersService } from "../../domain/services/users.service";
|
|
101
|
+
import { AppError } from "../middleware/errors";
|
|
102
|
+
|
|
103
|
+
const CreateUserSchema = z.object({
|
|
104
|
+
email: z.string().email(),
|
|
105
|
+
name: z.string().min(1).max(100),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export async function createUser(req: Request, res: Response, next: NextFunction) {
|
|
109
|
+
try {
|
|
110
|
+
const input = CreateUserSchema.parse(req.body); // ✅ boundary validation
|
|
111
|
+
const user = await usersService.create(input);
|
|
112
|
+
res.status(201).json({ id: user.id });
|
|
113
|
+
} catch (err) {
|
|
114
|
+
// zod errors -> 422
|
|
115
|
+
next(err instanceof z.ZodError ? AppError.validation(err.flatten()) : err);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### ❌ DON'T: доверять req.body
|
|
121
|
+
```ts
|
|
122
|
+
// Плохо: нет валидации, можно словить мусор/инъекции/500
|
|
123
|
+
export async function createUser(req: any, res: any) {
|
|
124
|
+
const user = await usersService.create(req.body);
|
|
125
|
+
res.json(user);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 4) Централизованный error handler (единый формат ошибок)
|
|
132
|
+
|
|
133
|
+
### ✅ DO: AppError + единый формат { error_code, message, details? }
|
|
134
|
+
```ts
|
|
135
|
+
// src/http/middleware/errors.ts
|
|
136
|
+
import type { ErrorRequestHandler } from "express";
|
|
137
|
+
|
|
138
|
+
export class AppError extends Error {
|
|
139
|
+
constructor(
|
|
140
|
+
public readonly status: number,
|
|
141
|
+
public readonly code: string,
|
|
142
|
+
message: string,
|
|
143
|
+
public readonly details?: unknown
|
|
144
|
+
) {
|
|
145
|
+
super(message);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static validation(details: unknown) {
|
|
149
|
+
return new AppError(422, "VALIDATION_ERROR", "Invalid input", details);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static forbidden() {
|
|
153
|
+
return new AppError(403, "FORBIDDEN", "Not enough permissions");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const errorHandler: ErrorRequestHandler = (err, _req, res, _next) => {
|
|
158
|
+
const isApp = err instanceof AppError;
|
|
159
|
+
const status = isApp ? err.status : 500;
|
|
160
|
+
const code = isApp ? err.code : "INTERNAL_ERROR";
|
|
161
|
+
|
|
162
|
+
// Без утечек: для 500 — нейтральное сообщение
|
|
163
|
+
const message = status === 500 ? "Unexpected error" : err.message;
|
|
164
|
+
|
|
165
|
+
res.status(status).json({
|
|
166
|
+
error_code: code,
|
|
167
|
+
message,
|
|
168
|
+
...(isApp && err.details ? { details: err.details } : {}),
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### ❌ DON'T: отдавать “err” целиком (утечка стека/SQL/секретов)
|
|
174
|
+
```ts
|
|
175
|
+
// Плохо: утечка внутренностей и нестабильный контракт
|
|
176
|
+
res.status(500).json({ err });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 5) Логирование: request_id, структурированные логи, запрет PII/секретов
|
|
182
|
+
|
|
183
|
+
### ✅ DO: request_id middleware + структурированные логи
|
|
184
|
+
```ts
|
|
185
|
+
// src/http/middleware/requestContext.ts
|
|
186
|
+
import { randomUUID } from "node:crypto";
|
|
187
|
+
import type { RequestHandler } from "express";
|
|
188
|
+
|
|
189
|
+
export const requestContext: RequestHandler = (req, res, next) => {
|
|
190
|
+
const id = req.header("x-request-id") ?? randomUUID();
|
|
191
|
+
res.setHeader("x-request-id", id);
|
|
192
|
+
(req as any).requestId = id;
|
|
193
|
+
next();
|
|
194
|
+
};
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
// usage example
|
|
199
|
+
logger.info(
|
|
200
|
+
{ request_id: (req as any).requestId, user_id: ctx.user?.id },
|
|
201
|
+
"user_created"
|
|
202
|
+
);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### ❌ DON'T: логировать body целиком (там пароли/токены/PII)
|
|
206
|
+
```ts
|
|
207
|
+
// Плохо: утечка секретов в логи
|
|
208
|
+
logger.info({ body: req.body }, "incoming_request");
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 6) React: loading/empty/error/success (TanStack Query)
|
|
214
|
+
|
|
215
|
+
### ✅ DO: явные состояния
|
|
216
|
+
```tsx
|
|
217
|
+
import { useQuery } from "@tanstack/react-query";
|
|
218
|
+
|
|
219
|
+
type User = { id: string; name: string };
|
|
220
|
+
|
|
221
|
+
async function fetchUsers(): Promise<User[]> {
|
|
222
|
+
const r = await fetch("/api/users");
|
|
223
|
+
if (!r.ok) throw new Error("Failed to load users");
|
|
224
|
+
return r.json();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function UsersList() {
|
|
228
|
+
const q = useQuery({ queryKey: ["users"], queryFn: fetchUsers });
|
|
229
|
+
|
|
230
|
+
if (q.isLoading) return <div aria-busy="true">Loading…</div>;
|
|
231
|
+
if (q.isError) return <div role="alert">Error: {String(q.error)}</div>;
|
|
232
|
+
if (!q.data || q.data.length === 0) return <div>No users yet</div>;
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
<ul>
|
|
236
|
+
{q.data.map((u) => (
|
|
237
|
+
<li key={u.id}>{u.name}</li>
|
|
238
|
+
))}
|
|
239
|
+
</ul>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### ❌ DON'T: fetch внутри render / без error/empty
|
|
245
|
+
```tsx
|
|
246
|
+
// Плохо: нет обработки ошибок/пустоты, легко словить гонки и “мерцания”.
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 7) Zustand: маленькие стооры по доменам + селекторы
|
|
252
|
+
|
|
253
|
+
### ✅ DO: доменный стор + селектор
|
|
254
|
+
```ts
|
|
255
|
+
import { create } from "zustand";
|
|
256
|
+
|
|
257
|
+
type AuthState = {
|
|
258
|
+
token: string | null;
|
|
259
|
+
setToken: (t: string | null) => void;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
export const useAuthStore = create<AuthState>((set) => ({
|
|
263
|
+
token: null,
|
|
264
|
+
setToken: (t) => set({ token: t }),
|
|
265
|
+
}));
|
|
266
|
+
|
|
267
|
+
// usage: подписываемся только на нужное
|
|
268
|
+
const token = useAuthStore((s) => s.token);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### ❌ DON'T: “бог-стор” на всё приложение
|
|
272
|
+
```ts
|
|
273
|
+
// Плохо: один огромный store с десятками полей и действий → лишние ререндеры и каша доменов.
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 8) RTK: slices по доменам + нормализация (EntityAdapter)
|
|
279
|
+
|
|
280
|
+
### ✅ DO: entityAdapter для списка пользователей
|
|
281
|
+
```ts
|
|
282
|
+
// src/state/usersSlice.ts
|
|
283
|
+
import { createSlice, createAsyncThunk, createEntityAdapter } from "@reduxjs/toolkit";
|
|
284
|
+
import type { RootState } from "./store";
|
|
285
|
+
|
|
286
|
+
type User = { id: string; name: string };
|
|
287
|
+
|
|
288
|
+
export const usersAdapter = createEntityAdapter<User>();
|
|
289
|
+
|
|
290
|
+
export const fetchUsers = createAsyncThunk("users/fetch", async () => {
|
|
291
|
+
const r = await fetch("/api/users");
|
|
292
|
+
if (!r.ok) throw new Error("Failed to load users");
|
|
293
|
+
return (await r.json()) as User[];
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const slice = createSlice({
|
|
297
|
+
name: "users",
|
|
298
|
+
initialState: usersAdapter.getInitialState({ status: "idle" as "idle" | "loading" | "failed" }),
|
|
299
|
+
reducers: {},
|
|
300
|
+
extraReducers: (b) => {
|
|
301
|
+
b.addCase(fetchUsers.pending, (s) => {
|
|
302
|
+
s.status = "loading";
|
|
303
|
+
});
|
|
304
|
+
b.addCase(fetchUsers.fulfilled, (s, a) => {
|
|
305
|
+
s.status = "idle";
|
|
306
|
+
usersAdapter.setAll(s, a.payload);
|
|
307
|
+
});
|
|
308
|
+
b.addCase(fetchUsers.rejected, (s) => {
|
|
309
|
+
s.status = "failed";
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
export const usersReducer = slice.reducer;
|
|
315
|
+
|
|
316
|
+
export const usersSelectors = usersAdapter.getSelectors<RootState>((st) => st.users);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### ❌ DON'T: side effects в компонентах + ручные “простыни” редьюсеров
|
|
320
|
+
```ts
|
|
321
|
+
// Плохо: компонент сам дергает fetch, сам хранит кэш, сам нормализует.
|
|
322
|
+
// Итог: непредсказуемость и дублирование.
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 9) Безопасность: authz на сервере (не доверять клиенту)
|
|
328
|
+
|
|
329
|
+
### ✅ DO: проверка прав на сервере
|
|
330
|
+
```ts
|
|
331
|
+
import { AppError } from "../http/middleware/errors";
|
|
332
|
+
|
|
333
|
+
export function requireRole(user: { role: string } | null, role: string) {
|
|
334
|
+
if (!user) throw new AppError(401, "UNAUTHORIZED", "Authentication required");
|
|
335
|
+
if (user.role !== role) throw AppError.forbidden();
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### ❌ DON'T: “если кнопку спрятали — значит безопасно”
|
|
340
|
+
```ts
|
|
341
|
+
// Плохо: безопасность только на UI.
|
|
342
|
+
// Сервер обязан проверять авторизацию/права.
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 10) Legacy: React 15.3 (Wix iFrame) + DoD scripts
|
|
348
|
+
|
|
349
|
+
### ✅ DO: React 15.3 class component + lifecycle
|
|
350
|
+
```jsx
|
|
351
|
+
class Widget extends React.Component {
|
|
352
|
+
constructor(props) {
|
|
353
|
+
super(props);
|
|
354
|
+
this.state = { loading: true, data: null, error: null };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
componentDidMount() {
|
|
358
|
+
this.load();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
load() {
|
|
362
|
+
apiFetch()
|
|
363
|
+
.then((data) => this.setState({ loading: false, data: data }))
|
|
364
|
+
.catch(() => this.setState({ loading: false, error: "Failed" }));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
render() {
|
|
368
|
+
if (this.state.loading) return React.createElement("div", null, "Loading…");
|
|
369
|
+
if (this.state.error) return React.createElement("div", { role: "alert" }, this.state.error);
|
|
370
|
+
return React.createElement("div", null, "OK");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### ❌ DON'T: hooks/современные API (в React 15.3 нельзя)
|
|
376
|
+
```tsx
|
|
377
|
+
// Нельзя в React 15.3
|
|
378
|
+
// function Widget(){ const [x,setX]=useState(...) }
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### ✅ DO: DoD scripts (package.json)
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"scripts": {
|
|
385
|
+
"dev": "node ./src/index.js",
|
|
386
|
+
"test": "vitest",
|
|
387
|
+
"test:coverage": "vitest --coverage",
|
|
388
|
+
"lint": "biome lint .",
|
|
389
|
+
"format": "biome format . --write"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### ❌ DON'T: смешивать форматтеры/линтеры без причины
|
|
395
|
+
```txt
|
|
396
|
+
// Плохо: eslint + prettier + biome одновременно без согласованных правил → вечные конфликты и шум в PR.
|
|
397
|
+
```
|