Ctrl K
ComunidadGuías…

REFACTORING_REVIEW.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Ревью и рефакторинг Battle Forge

Ревью текущего рабочего дерева, включая существовавшие незакоммиченные изменения.
Проверены player, импорт/нормализация персонажей, операции персонажа и инвентаря,
основные пути записи боя, библиотеки DM и HTTP/Socket.IO/MongoDB-слой.
Это обзор кода и автоматических проверок, без нагрузочной проверки развёрнутой системы.

## Исправлено

- **Состояние player:** 32 разрозненных writable signals заменены шестью типизированными
  группами: session, sheet, attackState, spellState, resourceState, inventoryState.
  Сам персонаж остаётся отдельным сигналом: изменения формы не должны инвалидировать
  вычисления характеристик и оружия. Фабрики в
  [player-state.model.ts](src/app/features/player/player-state.model.ts)
  задают начальное состояние и полный сброс каждого сценария.
- **Выход и смена сессии:** сбрасываются также вкладка, оружие, режим атаки,
  раскрытая способность и ошибка импорта, которые раньше переживали logout.
  Версия сессии отделяет завершения старых запросов от текущего экрана.
  Поздний вход/импорт не восстанавливает закрытую сессию, старое применение заклинания
  не снимает pending нового запроса, ресурс не активирует эффект для другой сессии.
  Защищены success и error/finally-ветки. Уже отправленную запись это не отменяет.
- **Общие сценарии:** login/import используют startCharacterSession; варианты
  применения заклинания используют performSpellUse.
  Восемь повторов load → missing check → patch → save в CharacterService используют
  updateLoadedCharacter. Это координация записи, не транзакция.
- **Utilities:** getAbilityModifier используется парсером, нормализатором, расчётом
  атаки и player; getSpellMaxUses/getSpellUsesRemaining — UI и правилами заклинаний;
  clampInteger — числовыми полями; isPositiveInteger — расходом предметов/ресурсов.
- **Расход:** отрицательные, нулевые, дробные и нечисловые количества отклоняются
  сервисами. Раньше отрицательная quantity увеличивала количество предметов,
  а NaN мог удалить стек. removeItem больше не мутирует загруженный массив
  и отклоняет дробные/нечисловые индексы.
- **Стоимость ресурса:** фиксированная стоимость не уменьшается до остатка.
  Недостаточный ресурс блокирует кнопку и обработчик.
- **Импорт:** ошибка чтения/сохранения видна на экране, а не только в logger.
  Ошибка разбора JSON отображается и до входа.

## Оставшиеся проблемы по приоритету

### P1. Потеря обновлений при параллельной работе

[CharacterService](src/app/core/services/character.service.ts) и
[InventoryService](src/app/core/services/inventory.service.ts) читают лист, меняют
копию и заменяют весь документ. Расход предмета и обновление HP могут прочитать одну
версию; поздний saveCharacter вернёт старое HP либо старый инвентарь.
Два расхода одного ресурса могут сохранить одинаковый остаток.

[BattleService.takeDamage](src/app/core/services/battle.service.ts) вычисляет HP
из локального realtime-снимка: два удара также могут потерять один результат.
turnTransition сериализует смену хода только внутри одного экземпляра сервиса.

**Предложение:** серверные команды consumeItem/useResource/castSpell/applyDamage
с проверкой остатка и условной атомарной записью; revision и compare-and-set для
редактирования листа. Конфликт возвращать явно и обновлять клиентский снимок.
Проверять двумя конкурентными клиентами. Клиентская очередь не решает эту проблему.

### P1. Изменения нескольких документов не атомарны

[DataStoreService.update](server/src/data/data-store.service.ts) выполняет группы
операций последовательно. CharacterService.updateCharacter проверяет отсутствие
нового имени, затем записывает новое и удаляет старое. Ошибка посередине оставляет
дубликаты; параллельное создание между проверкой и записью может быть перезаписано.

replaceCollection удаляет коллекцию **до** проверки всех дочерних путей и записи новых
документов. Некорректный ключ либо ошибка записи способны оставить частичную коллекцию.
Урон игроку отдельно записывается в комнату и лист, поэтому ошибка второй записи
оставляет разные HP.

**Предложение:** валидировать batch до первой записи; использовать серверные команды
и MongoDB-транзакции для изменений нескольких документов с учётом конфигурации MongoDB.
Ввести стабильный id персонажа вместо имени-ключа; имя сделать редактируемым полем.
Миграцию провести с поддержкой прежних ссылок.

### P1 при доступе недоверенных клиентов. Вход по имени не ограничивает API

[DataController](server/src/data/data.controller.ts) предоставляет CRUD по path,
[DataGateway](server/src/data/data.gateway.ts) принимает подписку по path.
В server/src нет guards для ролей DM/player или проверки владения персонажем.
Скрытие кнопок и экран входа Angular не ограничивают API.

**Предложение:** серверная сессия и проверка роли/комнаты/персонажа для HTTP и Socket.IO.
Текущая модель подходит доверенной группе, но не разграничению доступа игрока и DM.

### P2. Realtime-снимки не имеют версии

[DataGateway](server/src/data/data.gateway.ts) присоединяет socket к комнате, затем
читает и отправляет снимок. publishRelated тоже читает асинхронно. Позднее чтение
может доставить старое состояние после нового; клиент не проверяет revision.

**Предложение:** включить монотонную revision в чтения/события и отбрасывать старые версии.
Разделить результат команды и доставку уведомления: ошибка publishRelated после
успешной записи сейчас превращается в ошибку HTTP-запроса, хотя данные уже сохранены.
Для безопасного повтора команды нужен requestId.

### P2. Player и BattleService всё ещё имеют много обязанностей

Группировка сигналов упрощает переходы UI, но не решает размер
[player.component.ts](src/app/features/player/player.component.ts) и шаблона.
BattleService совмещает I/O, историю, ходы, эффекты, спасброски и синхронизацию листа.

**Следующий этап:**
1. Выделить локальные player-компоненты sheet, arena, spells/resources и inventory-dialog.
   Передавать readonly данные и события; состояние диалога держать рядом с диалогом.
2. Управление сессией/подпиской вынести в facade в providers экрана с жизненным циклом
   компонента. Не делать пользовательское UI-состояние root singleton.
3. Выделить чистые переходы боя, возвращающие nextState и history entry.
   BattleService оставить координацию команд, серверу — атомарность.
4. Переносить виджеты в shared/ui при реальном повторном использовании.
   Не вводить универсальный CRUD/store для всех сущностей: правила нормализации различаются.

### P2. TypeScript не валидирует входящие документы

[SetDataDto / UpdateDataDto](server/src/data/data.dto.ts) проверяют оболочку запроса,
но data остаётся unknown/Record. Клиентская нормализация исправляет часть старых записей,
однако API позволяет сохранить невалидное содержимое. Парсер LSS также полагается
на объявленные типы многих полей JSON.

**Предложение:** отдельные схемы внешнего LSS, сохранённого персонажа и серверных команд.
Проверять команды на сервере, импорт — на входной границе; разделять проверку нового
ввода и исправление legacy-данных.

### P2. Зависимости требуют отдельного обновления

Проверка корневого lockfile через npm audit --json 27.09.2026 сообщает о 18 затронутых
пакетах: 17 moderate и 1 high. Это не 18 независимых уязвимостей: часть записей —
зависимые пакеты одной цепочки. Серверный lockfile этим запуском не проверялся.

High относится к fast-uri@3.1.5: npm explain показывает dev-цепочку через ajv и
Angular CLI/devkit. Поэтому результат аудита сам по себе не доказывает уязвимость
production-приложения. Пример исходного advisory:
[fast-uri / repeated hostname percent-decoding](https://github.com/advisories/GHSA-fph4-wmhf-6fwf).

Есть также замечания к Angular и Vitest. Предложенный npm способ исправления части
Vitest-цепочки требует major-обновления, а package.json закрепляет vitest в overrides.
Следует отдельно согласовать совместимые версии Angular/Analog/Vitest, проверить
применимость advisories и повторить test:all/build. Автоматический audit fix --force
в рефакторинге не применялся.

### P3. Инструкции проекта отстали от реализации

В .agents/skills/battle-forge-project-map и доменных навыках ещё указаны FirebaseService,
RTDB и emulator. В коде уже RealtimeDataService, NestJS, Socket.IO и MongoDB;
test:all запускает coverage и серверные тесты. Инструкции могут направлять работу
в несуществующие файлы.

**Предложение:** отдельно обновить карту и инструкции тестирования под текущий стек.
Этот прикладной рефакторинг не требует миграции хранилища или новых runtime-зависимостей.

## Проверка

- npm run test:all: 52 frontend-файла, 320 тестов; 6 серверных файлов, 22 теста — успешно.
- Coverage: statements/lines 87,34%, branches 75,69%, functions 80,24%; порог 70% пройден.
- npm run build: production-сборка успешна, ошибок шаблонов и бюджетов нет.
- npm run typecheck: строгая проверка приложения и тестов успешна.
  Исправлены типы DOM-запросов и неполные fixtures; @types/node добавлен только в devDependencies
  для существующего теста с node:fs. Команда typecheck включена в test:all.
- Добавлены 32 регрессионных случая: поздние ответы/выход/повторный вход, сброс UI,
  фиксированная стоимость ресурса, ошибки импорта, недопустимые количества и индексы.
- Проверка зависимостей: npm audit завершается с замечаниями, описанными выше.
- Ручная проверка браузера и конкурентные интеграционные тесты с MongoDB не выполнялись.