kodu 2.1.1 → 2.1.3
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.md +23 -1
- package/__tests__/core/fs/fs.service.test.ts +72 -0
- package/__tests__/shared/cleaner/cleaner.service.test.ts +102 -0
- package/__tests__/shared/git/git.service.test.ts +84 -0
- package/__tests__/shared/tokenizer/tokenizer.service.test.ts +45 -0
- package/dist/package.json +14 -3
- package/dist/src/commands/init/init.command.d.ts +1 -0
- package/dist/src/commands/init/init.command.js +34 -1
- package/dist/src/commands/init/init.command.js.map +1 -1
- package/dist/src/core/config/config.service.js +2 -4
- package/dist/src/core/config/config.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/lefthook.yml +9 -2
- package/package.json +14 -3
- package/skills/doc-gen/SKILL.md +490 -0
- package/skills/doc-gen/scripts/doc_gen.py +911 -0
- package/skills/implement-project/SKILL.md +409 -0
- package/skills/liteend-init/SKILL.md +84 -0
- package/skills/litefront-init/SKILL.md +96 -0
- package/skills/litefront-prototype/SKILL.md +484 -0
- package/skills/project-setup-standardizer/SKILL.md +285 -0
- package/skills/start/SKILL.md +319 -0
- package/skills/tech-blueprint/SKILL.md +890 -0
- package/skills/tech-blueprint/scripts/blueprint_validator.py +417 -0
- package/src/commands/init/init.command.ts +43 -1
- package/src/core/config/config.service.ts +3 -6
- package/tsconfig.build.json +3 -0
- package/tsconfig.json +5 -2
- package/dist/scripts/generate-json-schema.d.ts +0 -1
- package/dist/scripts/generate-json-schema.js +0 -17
- package/dist/scripts/generate-json-schema.js.map +0 -1
- package/skills/kodu-ops/SKILL.md +0 -184
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tech-blueprint
|
|
3
|
+
description: Генерирует технические контракты (Prisma-схема, GraphQL API, архитектура, план тестирования) на основе SPEC.md и UI-прототипа. Также проверяет уже созданный blueprint по запросу. Запускай когда SPEC.md утверждён и нужно перейти к техническому проектированию, или когда пользователь говорит «проверь ТЗ», «валидируй blueprint». НЕ запускай если SPEC.md ещё черновик или задача — рефакторинг существующего кода.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
metadata:
|
|
7
|
+
level: multi
|
|
8
|
+
output: папка 3_TECH_BLUEPRINT/
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Назначение
|
|
12
|
+
|
|
13
|
+
Скилл создаёт пять документов, полностью описывающих техническую реализацию продукта:
|
|
14
|
+
- **IMPLEMENTATION_GUIDE.md** — онбординг разработчика: стек, что уже готово, как запустить
|
|
15
|
+
- **DATABASE_MODEL.md** — Prisma-схема, отношения, индексы
|
|
16
|
+
- **API_CONTRACTS.md** — GraphQL-схема с типами, операциями и правилами доступа
|
|
17
|
+
- **ARCHITECTURE.md** — NestJS-модули, FSD-слайсы, управление состоянием
|
|
18
|
+
- **TESTING_PLAN.md** — Unit-тесты и E2E-сценарии Playwright
|
|
19
|
+
|
|
20
|
+
**Когда запускать:**
|
|
21
|
+
- SPEC.md имеет статус «на ревью» или «утверждён»
|
|
22
|
+
- UI-прототип существует в папке `prototype/`
|
|
23
|
+
- Команда готова начинать разработку
|
|
24
|
+
|
|
25
|
+
**Когда НЕ запускать:**
|
|
26
|
+
- SPEC.md ещё черновик
|
|
27
|
+
- Нет понимания бизнес-сущностей
|
|
28
|
+
- Задача — рефакторинг или фикс бага в существующем проекте
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Входные данные
|
|
33
|
+
|
|
34
|
+
Перед генерацией **обязательно прочитать и проанализировать**:
|
|
35
|
+
|
|
36
|
+
1. **`1_PRODUCT_VISION/VISION.md`** — цель, границы, метрики успеха
|
|
37
|
+
2. **`2_PRODUCT_SPEC/SPEC.md`** — сущности, операции, страницы, тестирование (**главный источник истины**)
|
|
38
|
+
3. **`prototype/`** — только для понимания скопа: какие страницы существуют, какие потоки между ними, какие формы. **Не копировать из прототипа технические решения.** Прототип сделан быстро и без учёта best practices — он показывает «что», но не «как». Всё архитектурное — по лучшим практикам стека, независимо от того, как это реализовано в прототипе.
|
|
39
|
+
4. **Существующие схемы** — если в проекте уже есть `schema.prisma` или `schema.graphql`: прочитать перед генерацией, это означает режим обновления
|
|
40
|
+
|
|
41
|
+
Если входных данных недостаточно — задать **не более 2 уточняющих вопросов**.
|
|
42
|
+
|
|
43
|
+
Перед генерацией составить внутренний список:
|
|
44
|
+
- бизнес-сущности (из SPEC.md §Сущности)
|
|
45
|
+
- ключевые операции (из SPEC.md §Ключевые операции)
|
|
46
|
+
- страницы (из SPEC.md §Страницы, прототип — для сверки скопа)
|
|
47
|
+
- роли пользователей (из SPEC.md §Глоссарий или §Сущности)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Стартовые шаблоны: что уже реализовано
|
|
52
|
+
|
|
53
|
+
Проекты строятся на основе двух предготовленных стартовых шаблонов (boilerplate — готовая кодовая база с решёнными базовыми задачами): шаблон для бэкенда (NestJS) и шаблон для фронтенда (React). В них уже реализованы авторизация, работа с пользователями и тестовая инфраструктура. **Не проектировать заново** — только расширять.
|
|
54
|
+
|
|
55
|
+
### Backend
|
|
56
|
+
|
|
57
|
+
**Prisma-модель `Profile` (уже существует, не дублировать):**
|
|
58
|
+
```prisma
|
|
59
|
+
model Profile {
|
|
60
|
+
id Int @id @default(autoincrement())
|
|
61
|
+
createdAt DateTime @default(now())
|
|
62
|
+
updatedAt DateTime @updatedAt
|
|
63
|
+
oidcSub String @unique // связь с OIDC-провайдером
|
|
64
|
+
roles ProfileRole[] @default([USER])
|
|
65
|
+
avatarUrl String?
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
enum ProfileRole { ADMIN USER }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**GraphQL-операции (уже реализованы, не включать в Blueprint):**
|
|
72
|
+
| Операция | Тип | Доступ |
|
|
73
|
+
|---------|-----|--------|
|
|
74
|
+
| `me` | Query | JwtAuthGuard |
|
|
75
|
+
| `updateProfile(input)` | Mutation | JwtAuthGuard |
|
|
76
|
+
| `profileUpdated` | Subscription | по userId |
|
|
77
|
+
| `debug` | Query | JwtOptionalAuthGuard |
|
|
78
|
+
| `echo` | Query + Mutation | публичный |
|
|
79
|
+
|
|
80
|
+
**Инфраструктура (настроена, просто использовать):**
|
|
81
|
+
- Auth guards: `JwtAuthGuard`, `JwtOptionalAuthGuard`, `RolesGuard`, `@CurrentUser()`, `@Roles()`
|
|
82
|
+
- GraphQL: **MercuriusDriver** (Fastify) — не Apollo Server
|
|
83
|
+
- Subscriptions: WebSocket + Redis pub/sub (mqemitter-redis)
|
|
84
|
+
- Error handling: `gqlErrorFormatter` — маппит `ZodValidationException` и `HttpException` в GraphQL-ошибки
|
|
85
|
+
- 404 backend: `@All()` → `NotFoundException`
|
|
86
|
+
- Health check: `GET /health`
|
|
87
|
+
- Queue: BullMQ + Redis
|
|
88
|
+
- i18n: nestjs-i18n (en/ru)
|
|
89
|
+
|
|
90
|
+
**Backend тестовая инфраструктура:**
|
|
91
|
+
- `E2EClient.loginAs(profile)` — auth через `x-mock-sub` header
|
|
92
|
+
- `clearDatabase()` + `clearRedis()` — обязательно в `beforeEach()`
|
|
93
|
+
- `OIDC_MOCK_ENABLED=true` — режим мок-авторизации в `.env.test`
|
|
94
|
+
|
|
95
|
+
### Frontend
|
|
96
|
+
|
|
97
|
+
**FSD-слайсы** (FSD — Feature-Sliced Design, методология структуры фронтенд-кода по бизнес-слоям) **уже реализованы, не включать в Blueprint:**
|
|
98
|
+
| Слой | Слайс | Что содержит |
|
|
99
|
+
|------|-------|-------------|
|
|
100
|
+
| `features` | `auth` | OIDC PKCE конфигурация, `AuthGuard`, `MockAuthProvider` |
|
|
101
|
+
| `widgets` | `Header` | Навигация + кнопки авторизации (состояние auth) |
|
|
102
|
+
| `pages` | `404` | Полностью реализована с кнопками «Назад» и «На главную» |
|
|
103
|
+
| `shared/api` | `graphql-client` | URQL client factory с Bearer-token |
|
|
104
|
+
|
|
105
|
+
**Роуты (уже реализованы):**
|
|
106
|
+
| Роут | Назначение |
|
|
107
|
+
|------|-----------|
|
|
108
|
+
| `/callback` | OIDC redirect handler (после логина) |
|
|
109
|
+
| `*` (любой несуществующий) | 404 page через `defaultNotFoundComponent` |
|
|
110
|
+
|
|
111
|
+
**Frontend тестовая инфраструктура:**
|
|
112
|
+
- `VITE_MOCK_AUTH=true` — `MockAuthProvider` для Playwright e2e
|
|
113
|
+
- `tests/setup.ts` — глобальный мок `react-oidc-context` для Vitest unit/component-тестов
|
|
114
|
+
- Coverage: lines / functions / statements ≥ 80%, branches ≥ 70%
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Структура вывода
|
|
119
|
+
|
|
120
|
+
Технические контракты хранятся **отдельно** от продуктовой документации (doc-gen), чтобы не смешивать бизнес-артефакты с техническими:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
docs/<имя_проекта>/ ← продуктовая документация (doc-gen)
|
|
124
|
+
├── INDEX.md
|
|
125
|
+
├── 1_PRODUCT_VISION/
|
|
126
|
+
└── 2_PRODUCT_SPEC/
|
|
127
|
+
|
|
128
|
+
blueprint/<имя_проекта>/ ← технические контракты (этот скилл)
|
|
129
|
+
└── 3_TECH_BLUEPRINT/
|
|
130
|
+
├── IMPLEMENTATION_GUIDE.md ← онбординг разработчика (генерировать первым)
|
|
131
|
+
├── DATABASE_MODEL.md
|
|
132
|
+
├── API_CONTRACTS.md
|
|
133
|
+
├── ARCHITECTURE.md
|
|
134
|
+
└── TESTING_PLAN.md
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Создать папку перед генерацией:
|
|
138
|
+
```bash
|
|
139
|
+
mkdir -p blueprint/<ИмяПроекта>/3_TECH_BLUEPRINT
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Правила генерации: DATABASE_MODEL.md
|
|
145
|
+
|
|
146
|
+
### Структура файла
|
|
147
|
+
|
|
148
|
+
```markdown
|
|
149
|
+
# Модель данных: <Название продукта>
|
|
150
|
+
|
|
151
|
+
**Статус:** черновик | **Дата:** YYYY-MM-DD
|
|
152
|
+
|
|
153
|
+
## Ссылки
|
|
154
|
+
- Спецификация: [SPEC.md](../2_PRODUCT_SPEC/SPEC.md)
|
|
155
|
+
|
|
156
|
+
## Контекст реализации
|
|
157
|
+
- **ORM:** Prisma (TypeScript ORM) — схема в `prisma/schema.prisma`, миграции через `prisma migrate dev`
|
|
158
|
+
- **БД:** PostgreSQL
|
|
159
|
+
- **`Profile` — модель текущего пользователя** (уже существует в стартовом шаблоне, не дублировать): содержит `id`, `oidcSub @unique` (идентификатор из OIDC-провайдера), `roles`, `avatarUrl`. Для связи бизнес-сущности с пользователем: `profileId Int` + `@relation(fields: [profileId], references: [id])`.
|
|
160
|
+
- **Авторизация через OIDC** (OpenID Connect — протокол входа через внешний сервис, например Logto или Keycloak): таблицы `User`, `Session`, `AuthToken` — **не создавать**. Авторизация целиком вынесена на сторону внешнего провайдера.
|
|
161
|
+
- **Soft delete:** `deletedAt DateTime?` — сущность не удаляется физически. Запросы к таким моделям всегда фильтруют: `WHERE deletedAt IS NULL`.
|
|
162
|
+
|
|
163
|
+
## Сущности и отношения
|
|
164
|
+
<для каждой модели: что хранит, ключевые поля, связи — язык бизнеса, не базы данных>
|
|
165
|
+
|
|
166
|
+
## Схема базы данных
|
|
167
|
+
|
|
168
|
+
```prisma
|
|
169
|
+
// schema.prisma — <Название продукта>
|
|
170
|
+
// Сгенерировано на основе SPEC.md
|
|
171
|
+
|
|
172
|
+
generator client {
|
|
173
|
+
provider = "prisma-client-js"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
datasource db {
|
|
177
|
+
provider = "postgresql"
|
|
178
|
+
url = env("DATABASE_URL")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// <модели и энумы>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Энумы
|
|
185
|
+
<для каждого enum: название, значения, когда и почему используется>
|
|
186
|
+
|
|
187
|
+
## Индексы и ограничения
|
|
188
|
+
| Модель | Поле(я) | Тип | Причина |
|
|
189
|
+
|--------|---------|-----|---------|
|
|
190
|
+
|
|
191
|
+
## Каскадные операции
|
|
192
|
+
| Модель | Поле связи | onDelete | onUpdate | Обоснование |
|
|
193
|
+
|--------|-----------|----------|----------|-------------|
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Обязательные правила
|
|
197
|
+
|
|
198
|
+
**Трассируемость.** В комментарии к каждой модели добавлять ссылку:
|
|
199
|
+
```prisma
|
|
200
|
+
// Сущность «Заказ» — SPEC.md §Сущности
|
|
201
|
+
model Order {
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Технические поля.** Каждая модель (кроме join-таблиц, см. ниже) обязана содержать:
|
|
205
|
+
```prisma
|
|
206
|
+
createdAt DateTime @default(now())
|
|
207
|
+
updatedAt DateTime @updatedAt
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Мягкое удаление.** Для критичных бизнес-сущностей (Пользователи, Заказы, Проекты и аналоги по SPEC.md) добавлять `deletedAt DateTime?` если SPEC.md не запрещает. Все запросы к таким моделям учитывают `WHERE deletedAt IS NULL`.
|
|
211
|
+
|
|
212
|
+
**Энумы вместо строк.** Для перечислимых значений — только `enum`, никаких `String`:
|
|
213
|
+
```prisma
|
|
214
|
+
// Правильно:
|
|
215
|
+
enum OrderStatus { DRAFT CONFIRMED COMPLETED CANCELLED }
|
|
216
|
+
|
|
217
|
+
// Запрещено:
|
|
218
|
+
status String // вместо enum
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Связи.** Каждая связь — с явным `@relation`, `onDelete`, `onUpdate`.
|
|
222
|
+
|
|
223
|
+
**Уникальные индексы.** `@@unique` для бизнес-уникальных комбинаций полей.
|
|
224
|
+
|
|
225
|
+
**Join-таблицы.** Именовать со знаком `_` в названии (например, `_UserToRole`, `Post_Tag`). Технические поля для них опциональны.
|
|
226
|
+
|
|
227
|
+
**Profile уже существует.** Не добавлять модель `User`, `Account`, `Session`, `AuthToken` — авторизация полностью на стороне внешнего OIDC-провайдера. Для связи бизнес-сущностей с пользователем использовать `profileId`:
|
|
228
|
+
```prisma
|
|
229
|
+
// Правильно:
|
|
230
|
+
model Order {
|
|
231
|
+
// ...
|
|
232
|
+
profileId Int
|
|
233
|
+
profile Profile @relation(fields: [profileId], references: [id], onDelete: Cascade)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Запрещено:
|
|
237
|
+
model User { /* дублирует Profile */ }
|
|
238
|
+
model Session { /* OIDC — внешний провайдер */ }
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Запрещено:**
|
|
242
|
+
- `String` для перечислимых значений (статусы, роли, типы)
|
|
243
|
+
- Внешние ключи без явного `onDelete`
|
|
244
|
+
- Модели без поля `id`
|
|
245
|
+
- Модели `User`, `Account`, `Session`, `AuthToken` (дублируют boilerplate)
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Правила генерации: API_CONTRACTS.md
|
|
250
|
+
|
|
251
|
+
### Структура файла
|
|
252
|
+
|
|
253
|
+
```markdown
|
|
254
|
+
# API-контракты: <Название продукта>
|
|
255
|
+
|
|
256
|
+
**Статус:** черновик | **Дата:** YYYY-MM-DD
|
|
257
|
+
|
|
258
|
+
## Ссылки
|
|
259
|
+
- Спецификация: [SPEC.md](../2_PRODUCT_SPEC/SPEC.md)
|
|
260
|
+
- Модель данных: [DATABASE_MODEL.md](./DATABASE_MODEL.md)
|
|
261
|
+
|
|
262
|
+
## Контекст реализации
|
|
263
|
+
- **GraphQL-движок:** MercuriusDriver — реализация GraphQL для веб-фреймворка Fastify (используется вместо Apollo Server; поведение идентично, отличается только конфигурацией).
|
|
264
|
+
- **Гарды авторизации** (NestJS guard — декоратор, проверяющий права доступа перед выполнением резолвера):
|
|
265
|
+
- `@UseGuards(JwtAuthGuard)` — требует действующий JWT-токен
|
|
266
|
+
- `@UseGuards(JwtOptionalAuthGuard)` — авторизация необязательна (работает и с токеном, и без)
|
|
267
|
+
- `@UseGuards(JwtAuthGuard) @Roles(ProfileRole.ADMIN)` — только для роли ADMIN
|
|
268
|
+
- `@CurrentUser()` — декоратор параметра резолвера, возвращает текущий `Profile`
|
|
269
|
+
- **Операции стартового шаблона (уже готовы, не включать в Blueprint):** `me`, `updateProfile`, `profileUpdated`, `debug`, `echo`
|
|
270
|
+
- **Обработка ошибок:** `gqlErrorFormatter` автоматически преобразует `ZodValidationException` и `HttpException` в корректные GraphQL-ошибки — не нужно обрабатывать вручную.
|
|
271
|
+
- **Subscriptions:** работают через WebSocket + Redis pub/sub — инфраструктура уже настроена, просто использовать.
|
|
272
|
+
- **OIDC-операции** (вход/выход/регистрация): `login`, `register`, `logout`, `refreshToken` — **не описывать**, они находятся в OIDC-провайдере (внешний сервис).
|
|
273
|
+
|
|
274
|
+
## Обзор API
|
|
275
|
+
<количество операций, основные домены, принципиальные решения>
|
|
276
|
+
|
|
277
|
+
## Кастомные ошибки
|
|
278
|
+
| Код | Описание | Когда возникает |
|
|
279
|
+
|-----|----------|----------------|
|
|
280
|
+
|
|
281
|
+
## Схема GraphQL
|
|
282
|
+
|
|
283
|
+
```graphql
|
|
284
|
+
# schema.graphql — <Название продукта>
|
|
285
|
+
# Сгенерировано на основе SPEC.md
|
|
286
|
+
|
|
287
|
+
# <директивы, scalars, типы, inputs, queries, mutations>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Описание операций
|
|
291
|
+
<для нетривиальных операций: входные данные, результат, правило доступа, возможные ошибки>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Обязательные правила
|
|
295
|
+
|
|
296
|
+
**Трассируемость.** В комментарии к каждой Query и Mutation:
|
|
297
|
+
```graphql
|
|
298
|
+
# Описано в SPEC.md §Ключевые операции — создание заказа
|
|
299
|
+
createOrder(input: CreateOrderInput!): CreateOrderResult!
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Директивы доступа.** Каждая Query и Mutation помечается комментарием:
|
|
303
|
+
```graphql
|
|
304
|
+
# @public
|
|
305
|
+
products(limit: Int! = 20, offset: Int! = 0): ProductsPage!
|
|
306
|
+
|
|
307
|
+
# @auth
|
|
308
|
+
myOrders(limit: Int! = 20, offset: Int! = 0): OrdersPage!
|
|
309
|
+
|
|
310
|
+
# @auth @hasRole(ADMIN)
|
|
311
|
+
deleteUser(id: ID!): DeleteUserResult!
|
|
312
|
+
```
|
|
313
|
+
Если операция публичная — писать `# @public`. Без этого комментария операция считается непомеченной — ошибка.
|
|
314
|
+
|
|
315
|
+
**Строгая пагинация.** Любое поле, возвращающее список, обязано иметь пагинацию. Использовать wrapper-тип:
|
|
316
|
+
```graphql
|
|
317
|
+
# Правильно:
|
|
318
|
+
type Query {
|
|
319
|
+
orders(limit: Int! = 20, offset: Int! = 0, filter: OrderFilterInput): OrdersPage!
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
type OrdersPage {
|
|
323
|
+
items: [Order!]!
|
|
324
|
+
total: Int!
|
|
325
|
+
hasMore: Boolean!
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Запрещено:
|
|
329
|
+
type Query {
|
|
330
|
+
orders: [Order!]! # голый массив без пагинации — ошибка
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Input-типы.** Каждая мутация принимает отдельный Input-тип, не набор скалярных аргументов:
|
|
335
|
+
```graphql
|
|
336
|
+
# Правильно:
|
|
337
|
+
createOrder(input: CreateOrderInput!): CreateOrderResult!
|
|
338
|
+
|
|
339
|
+
# Запрещено:
|
|
340
|
+
createOrder(userId: ID!, productId: ID!, quantity: Int!): Order!
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Кастомные ошибки.** Для операций с бизнес-ошибками — union-тип результата:
|
|
344
|
+
```graphql
|
|
345
|
+
union CreateOrderResult = Order | OrderError | InsufficientStockError
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**GraphQL-движок: MercuriusDriver (Fastify).** Не описывать директивы и механизмы Apollo Server. Subscriptions — через WebSocket + Redis pub/sub (уже настроено в boilerplate).
|
|
349
|
+
|
|
350
|
+
**Синтаксис guards в комментариях.** Использовать NestJS-style для точности:
|
|
351
|
+
```graphql
|
|
352
|
+
# @UseGuards(JwtAuthGuard) — требует авторизации
|
|
353
|
+
# @UseGuards(JwtOptionalAuthGuard) — авторизация опциональна
|
|
354
|
+
# @UseGuards(JwtAuthGuard) @Roles(ADMIN) — только для роли ADMIN
|
|
355
|
+
# @public — без авторизации
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Операции boilerplate уже реализованы.** Не включать в Blueprint:
|
|
359
|
+
`me`, `updateProfile(input)`, `profileUpdated`, `debug`, `echo`
|
|
360
|
+
|
|
361
|
+
**Запрещено описывать:** операции авторизации (login, register, logout, refreshToken) — это OIDC-провайдер. Загрузку файлов и управление сессиями — если не несут уникальной бизнес-логики.
|
|
362
|
+
|
|
363
|
+
**Запрещено:**
|
|
364
|
+
- Голые массивы `[Entity!]!` в Query/Mutation
|
|
365
|
+
- Query/Mutation без директивы доступа
|
|
366
|
+
- Мутации с набором скалярных аргументов вместо Input-типа
|
|
367
|
+
- Операции `me`, `updateProfile`, `profileUpdated` (уже в boilerplate)
|
|
368
|
+
- GraphQL-типы `LoginInput`, `RegisterInput`, `SessionType` (OIDC — внешний провайдер)
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Правила генерации: ARCHITECTURE.md
|
|
373
|
+
|
|
374
|
+
### Структура файла
|
|
375
|
+
|
|
376
|
+
```markdown
|
|
377
|
+
# Архитектура: <Название продукта>
|
|
378
|
+
|
|
379
|
+
**Статус:** черновик | **Дата:** YYYY-MM-DD
|
|
380
|
+
|
|
381
|
+
## Ссылки
|
|
382
|
+
- Спецификация: [SPEC.md](../2_PRODUCT_SPEC/SPEC.md)
|
|
383
|
+
|
|
384
|
+
## Контекст реализации
|
|
385
|
+
|
|
386
|
+
**Стек:**
|
|
387
|
+
- Бэкенд: NestJS + Fastify, Prisma ORM, PostgreSQL, GraphQL (MercuriusDriver), Redis (BullMQ — очереди задач; pub/sub — GraphQL Subscriptions)
|
|
388
|
+
- Фронтенд: React + Vite, TanStack Router (файловый роутинг), FSD (Feature-Sliced Design — методология структуры кода по бизнес-слоям), URQL (GraphQL-клиент с кэшированием), Zustand (хранилище UI-состояния), ParaglideJS (i18n-библиотека для Vite)
|
|
389
|
+
|
|
390
|
+
**Уже реализовано в стартовых шаблонах (не включать в Blueprint):**
|
|
391
|
+
|
|
392
|
+
| Слой | Что готово |
|
|
393
|
+
|----------|------------|
|
|
394
|
+
| Бэкенд | Auth: гарды `JwtAuthGuard`, `JwtOptionalAuthGuard`, `RolesGuard`; OIDC JWT через внешний провайдер |
|
|
395
|
+
| Бэкенд | Profile GraphQL: операции `me`, `updateProfile`, `profileUpdated`; модель `Profile` в БД |
|
|
396
|
+
| Бэкенд | Инфраструктура: Health check `GET /health`, BullMQ, Redis pub/sub, i18n (en/ru), gqlErrorFormatter |
|
|
397
|
+
| Фронтенд | `features/auth` — OIDC PKCE-авторизация, `AuthGuard` (блокирует неавторизованных), `MockAuthProvider` (для тестов) |
|
|
398
|
+
| Фронтенд | `widgets/Header` (навигация + кнопки входа/выхода), `pages/404`, `shared/api/graphql-client` (URQL с Bearer-токеном) |
|
|
399
|
+
| Фронтенд | Роуты: `/callback` (OIDC redirect после входа), `*` (404 через `defaultNotFoundComponent`) |
|
|
400
|
+
|
|
401
|
+
**Protected routes (TanStack Router):** паттерн `beforeLoad` + `redirect()`. Контент страницы оборачивается в `AuthGuard` — компонент, перенаправляющий неавторизованного пользователя.
|
|
402
|
+
|
|
403
|
+
## Backend: NestJS-модули
|
|
404
|
+
| Модуль | Ответственность | Зависимости |
|
|
405
|
+
|--------|----------------|-------------|
|
|
406
|
+
|
|
407
|
+
## Frontend: FSD-слайсы
|
|
408
|
+
|
|
409
|
+
### Entities (бизнес-сущности)
|
|
410
|
+
| Сущность | Что представляет | Модель данных |
|
|
411
|
+
|---------|-----------------|---------------|
|
|
412
|
+
|
|
413
|
+
### Features (действия пользователя)
|
|
414
|
+
| Фича | Что делает пользователь | Связанная операция API |
|
|
415
|
+
|------|------------------------|----------------------|
|
|
416
|
+
|
|
417
|
+
### Widgets (составные блоки UI)
|
|
418
|
+
| Виджет | Из каких фич состоит | Где используется |
|
|
419
|
+
|--------|---------------------|-----------------|
|
|
420
|
+
|
|
421
|
+
### Pages (страницы приложения)
|
|
422
|
+
<иерархия страниц из prototype/ без файловых путей>
|
|
423
|
+
|
|
424
|
+
## Frontend: управление состоянием
|
|
425
|
+
|
|
426
|
+
### Серверный стейт (URQL-кэш)
|
|
427
|
+
<какие данные кэшируются через URQL: сущности, списки, их инвалидация>
|
|
428
|
+
|
|
429
|
+
### UI-стейт (Zustand)
|
|
430
|
+
<какие состояния в Zustand: открытые модалки, активные фильтры, тема, локальные флаги>
|
|
431
|
+
|
|
432
|
+
## Frontend: локализация (i18n)
|
|
433
|
+
<пространства имён ParaglideJS на основе прототипа или «i18n не требуется»>
|
|
434
|
+
|
|
435
|
+
## Переменные окружения
|
|
436
|
+
| Переменная | Тип | Назначение | BE/FE |
|
|
437
|
+
|-----------|-----|-----------|-------|
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Обязательные правила
|
|
441
|
+
|
|
442
|
+
**СТРОГИЙ ЗАПРЕТ файловых путей.** Запрещены любые строки вида `src/features/auth`, `src/entities/user`, `app/pages/dashboard`, `components/Button`. Только логические названия: фича `auth`, сущность `User`, страница `Dashboard`.
|
|
443
|
+
|
|
444
|
+
**Разделение состояния обязательно.** Явно указать для каждой области данных:
|
|
445
|
+
- Данные, пришедшие с сервера и кэшированные → URQL
|
|
446
|
+
- Локальное UI-состояние (не нужно сохранять на сервер) → Zustand
|
|
447
|
+
|
|
448
|
+
Нельзя оставить раздел «Управление состоянием» без конкретного содержимого или написать «используется URQL» без указания конкретных сущностей.
|
|
449
|
+
|
|
450
|
+
**FSD без деталей реализации.** Описывать только бизнес-смысл слайса. Не упоминать структуру файлов, barrel-exports, модель папок.
|
|
451
|
+
|
|
452
|
+
**ENV — только бизнес-переменные.** Не включать `PORT`, `NODE_ENV`, `DATABASE_URL`, `REDIS_URL`, стандартные переменные фреймворков. Только специфичные для данного продукта.
|
|
453
|
+
|
|
454
|
+
**i18n.** Если в прототипе есть тексты (labels, кнопки, сообщения, уведомления) — перечислить namespace'ы ParaglideJS. Если прототип без текстов — явно писать: «i18n не требуется».
|
|
455
|
+
|
|
456
|
+
**Роутер: TanStack Router (file-based).** Не описывать Next.js или React Router паттерны. Защищённые страницы используют `beforeLoad` + `redirect()`:
|
|
457
|
+
```
|
|
458
|
+
// Паттерн protected route (beforeLoad):
|
|
459
|
+
если не аутентифицирован → redirect на главную
|
|
460
|
+
контент страницы оборачивается в AuthGuard
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
**Уже существующие FSD-слайсы из стартового шаблона — не включать в Blueprint:**
|
|
464
|
+
- `features/auth` — OIDC-авторизация, `AuthGuard`, `MockAuthProvider`
|
|
465
|
+
- `widgets/Header` — навигация с кнопками входа/выхода
|
|
466
|
+
- `pages/404` — страница «не найдено»
|
|
467
|
+
- `shared/api/graphql-client` — URQL (GraphQL-клиент) с Bearer-токеном
|
|
468
|
+
|
|
469
|
+
**Zustand: паттерн со store.** Все новые store используют обёртку с devtools:
|
|
470
|
+
```typescript
|
|
471
|
+
// Паттерн нового Zustand-стора:
|
|
472
|
+
create(devtools<MyStore>((set) => ({ ... })))
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
**Запрещено:**
|
|
476
|
+
- Файловые пути FSD (`src/`, `app/`, `components/`)
|
|
477
|
+
- Раздел «Управление состоянием» без разделения URQL/Zustand
|
|
478
|
+
- Технические паттерны реализации (DI-контейнеры, репозитории, паттерны)
|
|
479
|
+
- Упоминание библиотек вне утверждённого стека
|
|
480
|
+
- Включать в Blueprint уже существующие FSD-слайсы (`auth`, `Header`, `404`)
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Правила генерации: TESTING_PLAN.md
|
|
485
|
+
|
|
486
|
+
### Структура файла
|
|
487
|
+
|
|
488
|
+
```markdown
|
|
489
|
+
# План тестирования: <Название продукта>
|
|
490
|
+
|
|
491
|
+
**Статус:** черновик | **Дата:** YYYY-MM-DD
|
|
492
|
+
|
|
493
|
+
## Ссылки
|
|
494
|
+
- Спецификация: [SPEC.md](../2_PRODUCT_SPEC/SPEC.md) — источник сценариев
|
|
495
|
+
|
|
496
|
+
## Контекст тестирования
|
|
497
|
+
|
|
498
|
+
**Бэкенд (Vitest):**
|
|
499
|
+
- `E2EClient` — встроенная тест-утилита из `test/utils/`, выполняет GraphQL-запросы с авторизацией через специальный HTTP-заголовок `x-mock-sub` (без реального OIDC-провайдера).
|
|
500
|
+
- Использование: `await client.loginAs(profile)` — выполнить запросы от имени пользователя.
|
|
501
|
+
- Обязательно в `beforeEach()`: `clearDatabase()` (очистить таблицы БД) + `clearRedis()` (сбросить кэш Redis) — чтобы тесты не влияли друг на друга.
|
|
502
|
+
- В `.env.test`: `OIDC_MOCK_ENABLED=true` — включает мок-режим авторизации, JWT проверяется через `x-mock-sub`, не через реальный OIDC.
|
|
503
|
+
|
|
504
|
+
**Фронтенд (Vitest + React Testing Library + Playwright):**
|
|
505
|
+
- Unit/component тесты: `tests/setup.ts` глобально мокает `react-oidc-context` (библиотека OIDC-авторизации) — OIDC-состояние доступно в тестах без дополнительной настройки.
|
|
506
|
+
- E2E тесты (Playwright): запускать с `VITE_MOCK_AUTH=true` — активирует `MockAuthProvider`, который автоматически авторизует пользователя в браузере без реального OIDC-провайдера.
|
|
507
|
+
|
|
508
|
+
**Coverage targets:** lines / functions / statements ≥ 80%, branches ≥ 70%
|
|
509
|
+
|
|
510
|
+
## Unit-тесты: сложная бизнес-логика
|
|
511
|
+
| Что тестируем | Входные данные | Ожидаемый результат | Почему нетривиально |
|
|
512
|
+
|--------------|----------------|--------------------|--------------------|
|
|
513
|
+
|
|
514
|
+
## E2E-сценарии (Playwright)
|
|
515
|
+
| Сценарий | Шаги | Ожидаемый результат | Источник в SPEC.md |
|
|
516
|
+
|----------|------|--------------------|--------------------|
|
|
517
|
+
|
|
518
|
+
## Критические пути (из SPEC.md §Тестирование)
|
|
519
|
+
<полный перенос критических сценариев без сокращений>
|
|
520
|
+
|
|
521
|
+
## Негативные сценарии (из SPEC.md §Тестирование)
|
|
522
|
+
<полный перенос негативных сценариев без сокращений>
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Обязательные правила
|
|
526
|
+
|
|
527
|
+
- Unit-тесты — только для нетривиальной логики: расчёты, валидации, конечные автоматы состояний. Не тестировать простой CRUD.
|
|
528
|
+
- E2E-сценарии берутся из SPEC.md §Тестирование. Каждый сценарий ссылается на источник.
|
|
529
|
+
- Критические пути и негативные сценарии из SPEC.md переносятся полностью.
|
|
530
|
+
|
|
531
|
+
### Backend: конкретные паттерны
|
|
532
|
+
|
|
533
|
+
**E2E-тесты** используют готовую инфраструктуру из `test/utils/`:
|
|
534
|
+
```typescript
|
|
535
|
+
// Аутентификация в тесте:
|
|
536
|
+
await client.loginAs(profile)
|
|
537
|
+
|
|
538
|
+
// Запрос:
|
|
539
|
+
const result = await client.requestGraphQL<MyQuery>(query, variables)
|
|
540
|
+
|
|
541
|
+
// Очистка (обязательно в beforeEach):
|
|
542
|
+
await clearDatabase()
|
|
543
|
+
await clearRedis()
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Мок авторизации:** `OIDC_MOCK_ENABLED=true` + `x-mock-sub` header. Не писать собственный механизм мока.
|
|
547
|
+
|
|
548
|
+
**Unit-тесты:** Vitest + NestJS Testing Module. Моки сервисов — через фабрики из `test/utils/mocks.ts`.
|
|
549
|
+
|
|
550
|
+
### Frontend: конкретные паттерны
|
|
551
|
+
|
|
552
|
+
**Три уровня тестирования:**
|
|
553
|
+
| Уровень | Инструмент | Что тестировать |
|
|
554
|
+
|---------|-----------|----------------|
|
|
555
|
+
| Unit | Vitest | Store-логика, утилиты, трансформации данных |
|
|
556
|
+
| Component | Vitest + React Testing Library | Рендер компонентов, взаимодействие, состояния |
|
|
557
|
+
| E2E | Playwright | Пользовательские сценарии из SPEC.md |
|
|
558
|
+
|
|
559
|
+
**Auth в тестах:**
|
|
560
|
+
- Unit/component: OIDC автоматически замокан через `tests/setup.ts` (unauthenticated по умолчанию). Переопределять локально в конкретном тесте.
|
|
561
|
+
- E2E: собирать приложение с `VITE_MOCK_AUTH=true` — `MockAuthProvider` авторизует автоматически.
|
|
562
|
+
|
|
563
|
+
**Coverage targets (обязательно):**
|
|
564
|
+
- lines / functions / statements: ≥ 80%
|
|
565
|
+
- branches: ≥ 70%
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## Правила генерации: IMPLEMENTATION_GUIDE.md
|
|
570
|
+
|
|
571
|
+
### Назначение
|
|
572
|
+
|
|
573
|
+
Онбординг-документ: позволяет новому разработчику взять Blueprint и начать работу **без знакомства с boilerplate**. Генерировать **последним** — после DATABASE_MODEL.md, API_CONTRACTS.md, ARCHITECTURE.md, TESTING_PLAN.md, так как суммирует их содержимое.
|
|
574
|
+
|
|
575
|
+
### Структура файла
|
|
576
|
+
|
|
577
|
+
```markdown
|
|
578
|
+
# Руководство по реализации: <Название продукта>
|
|
579
|
+
|
|
580
|
+
**Статус:** черновик | **Дата:** YYYY-MM-DD
|
|
581
|
+
|
|
582
|
+
## Стек
|
|
583
|
+
|
|
584
|
+
| Слой | Технология | Назначение |
|
|
585
|
+
|------|-----------|-----------|
|
|
586
|
+
| Бэкенд | NestJS + Fastify | Node.js-фреймворк для API-сервера |
|
|
587
|
+
| GraphQL | MercuriusDriver | GraphQL-сервер (адаптер Fastify) |
|
|
588
|
+
| ORM | Prisma | TypeScript ORM для работы с БД |
|
|
589
|
+
| БД | PostgreSQL | Реляционная база данных |
|
|
590
|
+
| Очереди | BullMQ + Redis | Фоновые задачи и очереди |
|
|
591
|
+
| Фронтенд | React + Vite | UI-библиотека + сборщик |
|
|
592
|
+
| Роутер | TanStack Router | Файловый роутинг для React |
|
|
593
|
+
| GraphQL-клиент | URQL | Запросы к API с кэшированием |
|
|
594
|
+
| UI-состояние | Zustand | Хранилище локального состояния |
|
|
595
|
+
| i18n | ParaglideJS | Локализация (Vite-плагин) |
|
|
596
|
+
| Тесты бэкенда | Vitest + E2EClient | Юнит и E2E для бэкенда |
|
|
597
|
+
| Тесты фронтенда | Vitest + RTL + Playwright | Юнит, компонент, E2E для фронтенда |
|
|
598
|
+
|
|
599
|
+
## Что уже реализовано
|
|
600
|
+
|
|
601
|
+
> Эти части **не нужно писать** — они готовы в стартовых шаблонах проекта.
|
|
602
|
+
|
|
603
|
+
### Бэкенд
|
|
604
|
+
- **Авторизация (OIDC JWT):** вход через внешний OIDC-провайдер (Logto/Keycloak). Гарды NestJS: `JwtAuthGuard` (требует токен), `JwtOptionalAuthGuard` (необязательно), `RolesGuard` (по роли). Декораторы резолвера: `@CurrentUser()` (получить текущий Profile), `@Roles(ProfileRole.ADMIN)` (ограничить по роли).
|
|
605
|
+
- **Profile — модель пользователя:** `Profile { id, oidcSub @unique, roles, avatarUrl }`. Уже реализованы GraphQL-операции: `me` (получить свой профиль), `updateProfile` (обновить), `profileUpdated` (подписка на изменения).
|
|
606
|
+
- **Инфраструктура:** Health check `GET /health`, BullMQ (очереди задач на Redis), Redis pub/sub (для GraphQL Subscriptions), nestjs-i18n en/ru, `gqlErrorFormatter` (автоматический маппинг ошибок в GraphQL-формат).
|
|
607
|
+
|
|
608
|
+
### Фронтенд
|
|
609
|
+
- **Авторизация (OIDC PKCE):** `react-oidc-context` — библиотека для OAuth2/OIDC в React. FSD-слайс `features/auth` содержит: `AuthGuard` (блокирует доступ неавторизованным), `MockAuthProvider` (автоматическая авторизация в тестах).
|
|
610
|
+
- **Готовые UI-компоненты:** `widgets/Header` (навигация с кнопками входа/выхода), `pages/404` (страница ошибки с кнопками «Назад» и «На главную»).
|
|
611
|
+
- **GraphQL API:** `shared/api/graphql-client` — настроенный URQL-клиент (GraphQL-клиент для React) с автоматической подстановкой Bearer-токена авторизации.
|
|
612
|
+
- **Роутинг (TanStack Router):** `/callback` — обработчик OIDC-редиректа после входа; `*` — 404-заглушка через `defaultNotFoundComponent`.
|
|
613
|
+
|
|
614
|
+
## Что нужно реализовать
|
|
615
|
+
|
|
616
|
+
> Всё описанное в данном Blueprint.
|
|
617
|
+
|
|
618
|
+
### Backend-модули
|
|
619
|
+
<список из ARCHITECTURE.md §Backend: NestJS-модули>
|
|
620
|
+
|
|
621
|
+
### Frontend-слайсы (новые)
|
|
622
|
+
<список из ARCHITECTURE.md §Frontend: FSD-слайсы — только те, которых нет в boilerplate>
|
|
623
|
+
|
|
624
|
+
## Локальный запуск
|
|
625
|
+
|
|
626
|
+
### Требования
|
|
627
|
+
- Node.js 20+, pnpm
|
|
628
|
+
- Docker (PostgreSQL + Redis)
|
|
629
|
+
- OIDC: для разработки используется mock-режим
|
|
630
|
+
|
|
631
|
+
### Backend
|
|
632
|
+
```bash
|
|
633
|
+
pnpm install
|
|
634
|
+
cp .env.example .env
|
|
635
|
+
# Заполнить: DATABASE_URL, REDIS_URL, JWT_SECRET, OIDC_ISSUER
|
|
636
|
+
|
|
637
|
+
docker compose up -d postgres redis
|
|
638
|
+
pnpm prisma migrate dev
|
|
639
|
+
pnpm start:dev
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Frontend
|
|
643
|
+
```bash
|
|
644
|
+
pnpm install
|
|
645
|
+
cp .env.example .env
|
|
646
|
+
# Заполнить: VITE_API_URL, VITE_OIDC_AUTHORITY, VITE_OIDC_CLIENT_ID
|
|
647
|
+
|
|
648
|
+
VITE_MOCK_AUTH=true pnpm dev # разработка с мок-авторизацией
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## Тестирование
|
|
652
|
+
|
|
653
|
+
### Backend
|
|
654
|
+
```bash
|
|
655
|
+
pnpm test # unit (Vitest)
|
|
656
|
+
pnpm test:e2e # e2e (требует Postgres + Redis)
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### Frontend
|
|
660
|
+
```bash
|
|
661
|
+
pnpm test # unit + component (Vitest + RTL)
|
|
662
|
+
VITE_MOCK_AUTH=true pnpm test:e2e # e2e (Playwright)
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Coverage:** lines / functions / statements ≥ 80%, branches ≥ 70%
|
|
666
|
+
|
|
667
|
+
## Ключевые решения
|
|
668
|
+
|
|
669
|
+
<Обоснование нетривиальных технических решений.
|
|
670
|
+
Пример: «Soft delete для Order — SPEC.md §Данные запрещает физическое удаление».
|
|
671
|
+
Если нетривиальных решений нет — написать «Особых нетривиальных решений нет».>
|
|
672
|
+
|
|
673
|
+
## Ссылки
|
|
674
|
+
|
|
675
|
+
- Спецификация: [SPEC.md](../../docs/<имя>/2_PRODUCT_SPEC/SPEC.md)
|
|
676
|
+
- [DATABASE_MODEL.md](./DATABASE_MODEL.md)
|
|
677
|
+
- [API_CONTRACTS.md](./API_CONTRACTS.md)
|
|
678
|
+
- [ARCHITECTURE.md](./ARCHITECTURE.md)
|
|
679
|
+
- [TESTING_PLAN.md](./TESTING_PLAN.md)
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Обязательные правила
|
|
683
|
+
|
|
684
|
+
- `## Стек` — полная таблица технологий без сокращений
|
|
685
|
+
- `## Что уже реализовано` — точный список boilerplate: разработчик видит, что **не нужно писать**
|
|
686
|
+
- `## Что нужно реализовать` — конкретный список модулей и слайсов из ARCHITECTURE.md, не общие фразы
|
|
687
|
+
- `## Локальный запуск` — реальные shell-команды с `.env` и docker; не «см. README»
|
|
688
|
+
- `## Тестирование` — команды запуска + coverage targets
|
|
689
|
+
- `## Ключевые решения` — не пустой раздел: либо обоснование решений, либо явная фраза «нет нетривиальных решений»
|
|
690
|
+
|
|
691
|
+
**Запрещено:**
|
|
692
|
+
- Пустые разделы
|
|
693
|
+
- `## Что нужно реализовать` без списка конкретных модулей/слайсов
|
|
694
|
+
- Общие фразы вместо команд в `## Локальный запуск`
|
|
695
|
+
- Дублирование полного содержимого других документов Blueprint
|
|
696
|
+
|
|
697
|
+
---
|
|
698
|
+
|
|
699
|
+
## Адаптивность: режим обновления проекта
|
|
700
|
+
|
|
701
|
+
Если в проекте уже существуют `schema.prisma` или `schema.graphql`:
|
|
702
|
+
|
|
703
|
+
1. **Не переписывать схему с нуля.** Прочитать существующие схемы, описать только дельту.
|
|
704
|
+
2. **DATABASE_MODEL.md обязан содержать раздел `## План миграции`:**
|
|
705
|
+
```markdown
|
|
706
|
+
## План миграции
|
|
707
|
+
|
|
708
|
+
### Новые таблицы
|
|
709
|
+
- `Notification` — уведомления пользователей
|
|
710
|
+
|
|
711
|
+
### Изменения существующих таблиц
|
|
712
|
+
- `User` — добавить поле `avatarUrl String?`
|
|
713
|
+
- `Order` — добавить enum-значение `OrderStatus.REFUNDED`
|
|
714
|
+
|
|
715
|
+
### Опасные операции
|
|
716
|
+
- Нет
|
|
717
|
+
```
|
|
718
|
+
3. **API_CONTRACTS.md** содержит разделы `## Новые операции` и `## Изменённые операции`.
|
|
719
|
+
4. Существующие схемы не ломать — только расширять.
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## Проверка ТЗ по запросу
|
|
724
|
+
|
|
725
|
+
Когда пользователь говорит **«проверь ТЗ»**, **«валидируй blueprint»**, **«check blueprint»**, **«посмотри что я изменил»** — выполнить следующую последовательность:
|
|
726
|
+
|
|
727
|
+
### Шаг 1. Запустить скрипт-валидатор
|
|
728
|
+
|
|
729
|
+
```bash
|
|
730
|
+
# Стандартный режим:
|
|
731
|
+
python3 {BLUEPRINT} validate "ИмяПроекта"
|
|
732
|
+
|
|
733
|
+
# Если проект — обновление существующего:
|
|
734
|
+
python3 {BLUEPRINT} validate "ИмяПроекта" --update-mode
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### Шаг 2. Прочитать все 5 файлов полностью
|
|
738
|
+
|
|
739
|
+
`IMPLEMENTATION_GUIDE.md`, `DATABASE_MODEL.md`, `API_CONTRACTS.md`, `ARCHITECTURE.md`, `TESTING_PLAN.md`
|
|
740
|
+
|
|
741
|
+
### Шаг 3. Ручная проверка противоречий с boilerplate
|
|
742
|
+
|
|
743
|
+
- [ ] Нет моделей `User`, `Session`, `AuthToken` — пользователь только через `Profile` (oidcSub)
|
|
744
|
+
- [ ] Нет GraphQL-операций `login`, `register`, `logout`, `refreshToken` — OIDC внешний
|
|
745
|
+
- [ ] Нет операций `me`, `updateProfile`, `profileUpdated` — уже в boilerplate
|
|
746
|
+
- [ ] В ARCHITECTURE.md нет `features/auth`, `widgets/Header`, `pages/404` — уже есть
|
|
747
|
+
- [ ] Protected routes описаны через `beforeLoad` паттерн
|
|
748
|
+
- [ ] Тесты упоминают `E2EClient.loginAs()` (BE) или `VITE_MOCK_AUTH=true` (FE)
|
|
749
|
+
- [ ] Нет несовместимых библиотек (Apollo вместо Mercurius, React Router вместо TanStack)
|
|
750
|
+
|
|
751
|
+
### Шаг 4. Проверка согласованности со SPEC.md
|
|
752
|
+
|
|
753
|
+
- [ ] Все сущности SPEC.md §Сущности покрыты в DATABASE_MODEL.md
|
|
754
|
+
- [ ] Все операции SPEC.md §Ключевые операции присутствуют в API_CONTRACTS.md
|
|
755
|
+
- [ ] Все страницы SPEC.md §Страницы отражены в ARCHITECTURE.md
|
|
756
|
+
- [ ] Сценарии SPEC.md §Тестирование перенесены в TESTING_PLAN.md
|
|
757
|
+
|
|
758
|
+
### Шаг 5. Выдать структурированный отчёт
|
|
759
|
+
|
|
760
|
+
```
|
|
761
|
+
## Проверка Blueprint: <ИмяПроекта>
|
|
762
|
+
|
|
763
|
+
### Скрипт-валидатор
|
|
764
|
+
✓ Прошёл без ошибок
|
|
765
|
+
— или —
|
|
766
|
+
✗ Ошибки: <список>
|
|
767
|
+
⚠ Предупреждения: <список>
|
|
768
|
+
|
|
769
|
+
### Противоречия с boilerplate
|
|
770
|
+
✓ Не обнаружены
|
|
771
|
+
— или —
|
|
772
|
+
✗ Обнаружены:
|
|
773
|
+
- <проблема> → <как исправить>
|
|
774
|
+
|
|
775
|
+
### Согласованность со SPEC.md
|
|
776
|
+
✓ Все требования покрыты
|
|
777
|
+
— или —
|
|
778
|
+
⚠ Не покрыто:
|
|
779
|
+
- <что отсутствует в Blueprint>
|
|
780
|
+
|
|
781
|
+
### Итог
|
|
782
|
+
Готов к разработке / Требует исправлений: <N проблем>
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
После исправлений — повторно запустить валидатор и создать git-коммит.
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Процесс работы
|
|
790
|
+
|
|
791
|
+
> **Скрипт:** `~/.config/opencode/skills/tech-blueprint/scripts/blueprint_validator.py`
|
|
792
|
+
> Скрипт НЕ копируется в проект — используется напрямую из папки скилла.
|
|
793
|
+
> Далее: `{BLUEPRINT}` = полный путь выше.
|
|
794
|
+
|
|
795
|
+
### Шаг 1. Анализ входных данных
|
|
796
|
+
|
|
797
|
+
1. Прочитать VISION.md и SPEC.md целиком — это **единственный источник истины**
|
|
798
|
+
2. Проверить `prototype/`: зафиксировать список страниц и переходов между ними. **Не переносить** реализацию — только скоп. Прототип — черновик, архитектурные решения принимаются независимо
|
|
799
|
+
3. Проверить наличие `schema.prisma`, `schema.graphql` → определить режим (новый / обновление)
|
|
800
|
+
4. Составить рабочий список: сущности, операции, страницы, роли
|
|
801
|
+
|
|
802
|
+
### Шаг 2. Генерация документов
|
|
803
|
+
|
|
804
|
+
Заполнять строго в порядке:
|
|
805
|
+
```
|
|
806
|
+
DATABASE_MODEL.md → API_CONTRACTS.md → ARCHITECTURE.md → TESTING_PLAN.md → IMPLEMENTATION_GUIDE.md
|
|
807
|
+
```
|
|
808
|
+
Каждый следующий документ опирается на предыдущий (API — на модели БД, архитектура — на API). IMPLEMENTATION_GUIDE.md — последним: он суммирует все четыре документа.
|
|
809
|
+
|
|
810
|
+
### Шаг 3. Самопроверка перед валидатором
|
|
811
|
+
|
|
812
|
+
- [ ] Каждая Prisma-модель (кроме join-таблиц с `_` в названии) имеет `createdAt`/`updatedAt`
|
|
813
|
+
- [ ] Для критичных сущностей добавлено `deletedAt DateTime?`
|
|
814
|
+
- [ ] Нет голых массивов `[Type!]!` в Query/Mutation без пагинации
|
|
815
|
+
- [ ] Каждая Query/Mutation имеет комментарий с директивой доступа
|
|
816
|
+
- [ ] В ARCHITECTURE.md нет ни одного файлового пути (`src/`, `app/`)
|
|
817
|
+
- [ ] В DATABASE_MODEL.md и API_CONTRACTS.md есть ссылки на SPEC.md
|
|
818
|
+
- [ ] Если режим обновления — раздел `## План миграции` существует
|
|
819
|
+
- [ ] IMPLEMENTATION_GUIDE.md содержит разделы `## Стек`, `## Что уже реализовано`, `## Локальный запуск`
|
|
820
|
+
|
|
821
|
+
### Шаг 4. Валидация (обязательно)
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
# Новый проект:
|
|
825
|
+
python3 {BLUEPRINT} validate "ИмяПроекта"
|
|
826
|
+
|
|
827
|
+
# Обновление существующего проекта:
|
|
828
|
+
python3 {BLUEPRINT} validate "ИмяПроекта" --update-mode
|
|
829
|
+
|
|
830
|
+
# Нестандартная папка вывода:
|
|
831
|
+
python3 {BLUEPRINT} validate "ИмяПроекта" --output /путь/к/blueprint
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
**Документация не считается готовой, пока валидация не пройдена без ошибок.**
|
|
835
|
+
|
|
836
|
+
Скрипт выполняет 8 или 9 проверок:
|
|
837
|
+
1. Наличие всех 5 файлов (включая IMPLEMENTATION_GUIDE.md)
|
|
838
|
+
2. Наличие блоков ` ```prisma ` и ` ```graphql `
|
|
839
|
+
3. Отсутствие FSD-путей в ARCHITECTURE.md
|
|
840
|
+
4. Кросс-чек: Prisma-модели покрыты в API_CONTRACTS.md (≥50%)
|
|
841
|
+
5. Трассируемость: ссылки на SPEC.md в DATABASE_MODEL.md и API_CONTRACTS.md
|
|
842
|
+
6. Пагинация: все Query/Mutation-поля с `[...]` имеют аргументы пагинации
|
|
843
|
+
7. Технические поля: `createdAt`/`updatedAt` в каждой Prisma-модели (кроме join-таблиц)
|
|
844
|
+
8. IMPLEMENTATION_GUIDE.md содержит обязательные разделы (`## Стек`, `## Что уже реализовано`, `## Локальный запуск`)
|
|
845
|
+
9. *(только с `--update-mode`)* Наличие раздела «План миграции» в DATABASE_MODEL.md
|
|
846
|
+
|
|
847
|
+
### Шаг 5. Git-коммит (обязательно)
|
|
848
|
+
|
|
849
|
+
```bash
|
|
850
|
+
git add blueprint/<ИмяПроекта>/3_TECH_BLUEPRINT/
|
|
851
|
+
git commit -m "feat(blueprint): технические контракты <ИмяПроекта>"
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
856
|
+
## Глоссарий
|
|
857
|
+
|
|
858
|
+
| Термин | Значение |
|
|
859
|
+
|--------|---------|
|
|
860
|
+
| Boilerplate | Стартовый шаблон проекта с готовыми базовыми решениями (auth, инфраструктура) |
|
|
861
|
+
| OIDC | OpenID Connect — протокол авторизации через внешний сервис (Logto, Keycloak, Google) |
|
|
862
|
+
| JWT | JSON Web Token — подписанный токен, подтверждающий личность пользователя |
|
|
863
|
+
| FSD | Feature-Sliced Design — методология структуры фронтенда по слоям: app, pages, widgets, features, entities, shared |
|
|
864
|
+
| MercuriusDriver | NestJS-адаптер GraphQL для веб-фреймворка Fastify (альтернатива Apollo Server) |
|
|
865
|
+
| URQL | GraphQL-клиент для React с встроенным кэшем и инвалидацией |
|
|
866
|
+
| Zustand | Минималистичная библиотека состояния для React (альтернатива Redux) |
|
|
867
|
+
| ParaglideJS | i18n-библиотека с типизированными ключами переводов, работает как Vite-плагин |
|
|
868
|
+
| BullMQ | Библиотека очередей задач для Node.js на основе Redis |
|
|
869
|
+
| E2EClient | Встроенная тест-утилита (`test/utils/`) для выполнения GraphQL-запросов с мок-авторизацией |
|
|
870
|
+
| Profile | Модель пользователя в БД; связана с OIDC через поле `oidcSub @unique` |
|
|
871
|
+
| TanStack Router | Файловый роутер для React: маршруты определяются структурой папок `src/routes/` |
|
|
872
|
+
| AuthGuard | FSD-слайс-компонент, перенаправляющий неавторизованного пользователя с защищённой страницы |
|
|
873
|
+
| beforeLoad | Хук TanStack Router для проверки условий перед загрузкой страницы (используется для защиты роутов) |
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## Ключевые ограничения
|
|
878
|
+
|
|
879
|
+
- Язык документации — **русский**, код — латиница
|
|
880
|
+
- Стек: **NestJS, Prisma, PostgreSQL, GraphQL (MercuriusDriver), React, TanStack Router, FSD, URQL, Zustand, ParaglideJS**
|
|
881
|
+
- **Запрещены файловые пути FSD** в ARCHITECTURE.md — только логические названия
|
|
882
|
+
- **Все Query/Mutation с `[...]`** — обязательно с пагинацией (limit/offset или first/after)
|
|
883
|
+
- **Все Query/Mutation** — с комментарием-директивой доступа (`@public`, `@UseGuards(JwtAuthGuard)`, `@Roles(ADMIN)`)
|
|
884
|
+
- **Каждая Prisma-модель** (не join) — с `createdAt`/`updatedAt`
|
|
885
|
+
- **Enum вместо String** для всех перечислимых значений
|
|
886
|
+
- **Трассируемость** — ссылки на SPEC.md в комментариях к схемам
|
|
887
|
+
- **Режим обновления** — раздел «План миграции», не переписывание схемы
|
|
888
|
+
- **Не дублировать boilerplate**: Profile, me/updateProfile/profileUpdated, auth-слайс, Header, 404-страница
|
|
889
|
+
- **Тесты**: использовать `E2EClient.loginAs()` (BE) и `VITE_MOCK_AUTH=true` (FE), coverage ≥ 80%/70%
|
|
890
|
+
- После валидации — **git-коммит**
|