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.
Files changed (76) hide show
  1. package/.agents/a11y_baseline/SKILL.md +41 -0
  2. package/.agents/adr_log/SKILL.md +69 -0
  3. package/.agents/api_contract_compliance_review/SKILL.md +18 -0
  4. package/.agents/api_contracts/SKILL.md +42 -0
  5. package/.agents/architecture_compliance_review/SKILL.md +17 -0
  6. package/.agents/architecture_doc/SKILL.md +92 -0
  7. package/.agents/board/SKILL.md +43 -0
  8. package/.agents/cloud_infrastructure_security/SKILL.md +68 -0
  9. package/.agents/code_review_checklist/SKILL.md +47 -0
  10. package/.agents/current_state_analysis/SKILL.md +44 -0
  11. package/.agents/data_model/SKILL.md +40 -0
  12. package/.agents/dependency_supply_chain_review/SKILL.md +20 -0
  13. package/.agents/deployment_ci_plan/SKILL.md +51 -0
  14. package/.agents/design_intake/SKILL.md +71 -0
  15. package/.agents/design_parity_review/SKILL.md +73 -0
  16. package/.agents/design_systems/SKILL.md +15 -0
  17. package/.agents/dev_reference_snippets/SKILL.md +397 -0
  18. package/.agents/docker_kubernetes_architecture/SKILL.md +145 -0
  19. package/.agents/es2025_beast_practices/SKILL.md +15 -0
  20. package/.agents/gates/SKILL.md +35 -0
  21. package/.agents/go_beast_practices/SKILL.md +23 -0
  22. package/.agents/handoff/SKILL.md +52 -0
  23. package/.agents/k8s_manifests_conventions/SKILL.md +176 -0
  24. package/.agents/memory/SKILL.md +29 -0
  25. package/.agents/mongodb_mongoose_best_practices/SKILL.md +236 -0
  26. package/.agents/node_express_beast_practices/SKILL.md +30 -0
  27. package/.agents/observability_logging/SKILL.md +16 -0
  28. package/.agents/observability_plan/SKILL.md +38 -0
  29. package/.agents/observability_review/SKILL.md +20 -0
  30. package/.agents/performance_review_baseline/SKILL.md +17 -0
  31. package/.agents/pm_backlog/SKILL.md +32 -0
  32. package/.agents/pm_interview/SKILL.md +56 -0
  33. package/.agents/pm_prd/SKILL.md +56 -0
  34. package/.agents/qa_api_contract_tests/SKILL.md +16 -0
  35. package/.agents/qa_e2e_playwright/SKILL.md +0 -0
  36. package/.agents/qa_manual_run/SKILL.md +16 -0
  37. package/.agents/qa_security_smoke_tests/SKILL.md +14 -0
  38. package/.agents/qa_test_plan/SKILL.md +20 -0
  39. package/.agents/qa_ui_a11y_smoke/SKILL.md +12 -0
  40. package/.agents/react_15_3_wix_iframe/SKILL.md +20 -0
  41. package/.agents/react_beast_practices/SKILL.md +29 -0
  42. package/.agents/release_gate/SKILL.md +77 -0
  43. package/.agents/release_gate_checklist_template/SKILL.md +68 -0
  44. package/.agents/review_reference_snippets/SKILL.md +437 -0
  45. package/.agents/security_baseline_dev/SKILL.md +16 -0
  46. package/.agents/security_review/SKILL.md +55 -0
  47. package/.agents/security_review_baseline/SKILL.md +25 -0
  48. package/.agents/state_rtk_beast_practices/SKILL.md +15 -0
  49. package/.agents/state_zustand_beast_practices/SKILL.md +11 -0
  50. package/.agents/styling_css_stack/SKILL.md +12 -0
  51. package/.agents/system_design_checklist/SKILL.md +48 -0
  52. package/.agents/tanstack_beast_practices/SKILL.md +19 -0
  53. package/.agents/tdd_workflow/SKILL.md +34 -0
  54. package/.agents/testing_strategy_js/SKILL.md +30 -0
  55. package/.agents/tests_quality_review/SKILL.md +18 -0
  56. package/.agents/threat_model_baseline/SKILL.md +57 -0
  57. package/.agents/tooling_bun_biome/SKILL.md +17 -0
  58. package/.agents/typescript_beast_practices/SKILL.md +15 -0
  59. package/.agents/ui_a11y_smoke_review/SKILL.md +15 -0
  60. package/.agents/ui_inventory/SKILL.md +50 -0
  61. package/.agents/ux_discovery/SKILL.md +48 -0
  62. package/.agents/ux_spec/SKILL.md +56 -0
  63. package/.agents/wix_self_hosted_embedded_script/SKILL.md +88 -0
  64. package/AGENTS.md +120 -0
  65. package/README.md +6 -1
  66. package/agents/architect.md +242 -0
  67. package/agents/conductor.md +207 -0
  68. package/agents/product_manager.md +121 -0
  69. package/agents/reviewer.md +201 -0
  70. package/agents/senior_full_stack.md +218 -0
  71. package/agents/tester.md +187 -0
  72. package/agents/ux_ui_designer.md +145 -0
  73. package/dist/index.js +62 -23
  74. package/dist/sourceResolver.d.ts +10 -0
  75. package/dist/sourceResolver.js +36 -0
  76. 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
+ ```