Frontend: - Replace Next.js with Vite + React + TypeScript - Add new component architecture (app-shell, sidebar, dashboard modules) - Implement product modules: FRAME, safety protocols, walkthrough checkin, campus/staff attendance, personality quiz, sign language, classroom timer - Add shadcn/ui component library with Tailwind CSS - Remove legacy generated components, stores, and pages Backend: - Add product migrations: frame_entries, user_progress, safety_quiz_results, walkthrough_checkins, communication_events, personality_quiz_results, campus_attendance_config/summaries, staff_attendance_records, content_catalog - Add corresponding models, services, and routes - Implement cookie-based auth with refresh token rotation - Add content catalog seeder with product content - Migrate to ESLint flat config - Switch from yarn to npm Infrastructure: - Update .gitignore for new tooling - Add project documentation (CLAUDE.md, docs/) - Remove deprecated config files and yarn.lock Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
44 KiB
План миграции бэкэнда на TypeScript + ESM
Статус: черновик плана (реализация ещё не начата). Дата: 2026-06-09.
Этот документ описывает полную, поэтапную миграцию backend/ с CommonJS/JavaScript на
TypeScript со строгим режимом и нативные ESM-модули (import/export). План составлен
так, чтобы приложение оставалось рабочим на каждом шаге и соответствовало правилам
CLAUDE.md (без any, без отключения линтера/TS, без приведения типов, импорты через
алиас @, документация и тесты на каждый модуль, минимальные изменения, без
оверинжиниринга).
0. Принятые решения (2026-06-09)
Зафиксированы по итогам обсуждения и проверочных spike:
- Дата/время:
moment→dayjs(минимальный diff, близкий API). - sequelize-cli в ESM (spike проведён): подтверждено — CLI загружает
.cjs-конфиг и.sequelizercв пакете с"type": "module". Но т.к. выбраны TS-миграции (см. п.4), sequelize-cli из миграционного флоу убирается. - Инструменты: dev — раннер
tsx(исполняет.ts+ESM напрямую, это инструмент, не React-разметка), прод — сборкаtsc+tsc-alias. Бандлер не вводим. - Миграции — на TypeScript. Используем Umzug 3 +
tsx(spike подтвердил выполнение.ts-миграций end-to-end). Хранилище —SequelizeStorageповерх существующей таблицыSequelizeMeta, поэтому история уже применённых миграций сохраняется. Лучшие практики — см. раздел 11. - Версия Node: 24 (Active LTS). Удовлетворяет самым строгим ограничениям зависимостей (
vitest,eslintтребуют>=24). Node 26 — ещё «Current», не LTS, в прод не берём. - Тесты: минимальный смоук-набор на этапе миграции (+ unit на чистые модули).
- OAuth-стратегии passport: обновление — отдельной задачей после миграции; задача внесена в
docs/full-integration-refactor-plan.md.
ORM остаётся Sequelize 6.37 (решение зафиксировано отдельно: Prisma — слишком большой рефакторинг, v7 — alpha).
1. Текущее состояние (факты, на которых строится план)
Замеры по backend/src (без node_modules):
- Всего файлов
.js: 206, суммарно ~55 651 строк. - Распределение по слоям:
db/— 90 файлов (модели 39, db/api 28, миграции 15, сидеры 5), ~33 400 строк — самый объёмный слой.routes/— 45 файлов, ~12 700 строк.services/— 48 файлов, ~7 600 строк.constants/— 13 файлов,middlewares/— 3,auth/— 2,config/— 1 +config.js,ai/— 1.
- Модульная система: 100% CommonJS.
require(используется в 173 файлах,module.exports— в 201; ESM-импортов (import …) — 0. - Экспорт-паттерны:
module.exports = class— 74 файла (services, db/api, middlewares, helpers);module.exports = function(sequelize, DataTypes)— 38 (фабрики моделей Sequelize). - ORM: Sequelize 6.37, миграции/сидеры через sequelize-cli 6.6.5 (CommonJS-инструмент, читает
.sequelizerc→src/db/db.config.js). - Express 5.2, аутентификация на passport (JWT + Google + Microsoft), документация через swagger-jsdoc (парсит
./src/routes/*.js). - Dev-запуск:
watcher.js(chokidar + nodemon), он же триггеритdb:migrate/db:seedпри появлении новых файлов. - ESLint flat-config с
sourceType: 'commonjs'и правиломimport-x/no-unresolved. - Тестов в бэкэнде сейчас нет (0 файлов
*.test.js/*.spec.js); проверка — толькоnode -c(синтаксис) согласноdocs/full-integration-refactor-plan.md. - Алиас
@в бэкэнде пока не используется (хотяCLAUDE.mdего требует); все импорты относительные. - Окружения:
engines.node >= 18, Dockerfile наnode:20.15.1-alpine, локально доступен Node 22.
Важный ориентир: фронтенд уже на TypeScript + ESM + Vite (strict, paths: { "@/*": ["./src/*"] },
vitest, playwright). Бэкэнд логично привести к тем же конвенциям (strict TS, алиас @, vitest).
ESM-блокеры, выявленные в коде
- Динамическая загрузка моделей в
db/models/index.js:fs.readdirSync(__dirname)+require(path.join(...))в цикле. В нативном ESMrequireнедоступен; нужен либо явный списокimport, либо динамическийimport(). __dirname/__filenameв 7 файлах:config/load-env.js,index.js,ai/LocalAIApi.js,db/models/index.js,services/email/list/{passwordReset,addressVerification,invitation}.js. В ESM их нет — замена наfileURLToPath(import.meta.url).- sequelize-cli загружает
.sequelizerc,db.config.js, миграции и сидеры как CommonJS черезrequire. При"type": "module"файлы.jsстанут ESM и CLI сломается. - swagger-jsdoc указывает на
./src/routes/*.js— после сборки путь к исходникам/выходу изменится. watcher.js+ nodemon заточены под запуск.js-файла напрямую.- В ESM (NodeNext) относительные импорты требуют явного расширения (
./foo.js), а алиасы tsconfig не резолвятся Node в рантайме без дополнительного инструмента.
2. Целевое состояние
- Весь рантайм-код бэкэнда —
.tsсо строгим TypeScript (strict: true,noImplicitAny,strictNullChecks), безanyи без приведения типов. - Нативный ESM:
"type": "module"вpackage.json,import/exportво всём рантайм-коде. - Импорты через алиас
@(как на фронтенде), работающие и в dev, и в прод-сборке. - Сборка
tsc→dist/, запуск прод изdist/; dev — черезtsx watch(TS + ESM + алиасы без отдельной сборки). - Миграции и сидеры остаются на sequelize-cli и сохраняют CommonJS (расширение
.cjs) — как исторические артефакты, переписывать их на TS нецелесообразно. - Введён минимальный каркас типизированных тестов (
vitest) иtypecheckкак гейт качества. - Обновлены Dockerfile, скрипты
package.json, ESLint и документация.
3. Ключевые проектные решения (с обоснованием)
3.1. Две фазы вместо «большого взрыва»
Задача совмещает две независимые трансформации: смену языка (JS → TS) и смену модульной системы (CJS → ESM). Делать обе сразу на 206 файлах рискованно. Рекомендуется разделить:
- Фаза A — типизация (остаёмся на CommonJS). TypeScript компилируется в
module: "CommonJS". ВключаемallowJs: true, чтобы.tsи.jsсосуществовали, и мигрируем файлы снизу вверх по дереву зависимостей. Приложение всё это время запускается как раньше. Риск минимальный. - Фаза B — перевод на ESM. Когда весь код уже на TS, механически переводим модульную систему:
"type": "module",module: "NodeNext",require→import,module.exports→export,__dirname→import.meta.url, переписываем динамический загрузчик моделей, чиним sequelize-cli и swagger-пути.
Такое разделение изолирует «языковые» ошибки от «модульных» и даёт чёткие точки отката.
Альтернатива (не рекомендуется): одновременный переход на
tsx+"type": "module"и правка всех импортов сразу. Быстрее по числу шагов, но даёт большой нестабильный diff и трудный откат. Противоречит принципу «минимальные изменения».
3.2. Инструмент сборки и запуска
- Сборка:
tsc(официальный компилятор) вdist/. Просто, предсказуемо, без бандлера — соответствует «без оверинжиниринга». - Алиасы
@в выходной сборке:tscне переписывает@/*в относительные пути. Добавляемtsc-aliasкак пост-шаг сборки. (Альтернатива — нативныеimportsвpackage.jsonс префиксом#, ноCLAUDE.mdтребует именно@.) - Dev-запуск:
tsx watch src/index.ts— исполняет TS+ESM напрямую и резолвитpathsиз tsconfig, заменяяnodemon. Авто-миграции/сидинг изwatcher.jsвыносим в отдельныйpredev-шаг или оставляем лёгкий watcher только дляdb/migrations+db/seeders(см. 4.7).
3.3. Миграции — переход на Umzug + TypeScript (решение принято)
sequelize-cli — CommonJS-инструмент и не поддерживает .ts-миграции. Поскольку выбраны
TS-миграции (решение 0.4), отказываемся от sequelize-cli в пользу Umzug 3 — это та же
библиотека, что лежит под капотом sequelize-cli, поэтому переход совместим по хранилищу
истории.
Подход (детали и пример каркаса — в разделе 11):
- Собственный лёгкий раннер
db/migrate.tsна Umzug, запускаемый черезtsx. - Хранилище истории —
SequelizeStorageповерх существующей таблицыSequelizeMeta. Уже применённые миграции остаются записанными, повторно не выполняются. - Новые миграции пишутся как
.ts(ESM, типизированныйQueryInterface). - Существующие 15 миграций не переписываем: оставляем как есть в формате
.cjs(исторические артефакты), Umzug-glob включает и.cjs, и.ts,tsxгрузит оба. Это исключает риск нарушить уже применённую историю БД. - Сидеры — вторым экземпляром Umzug (отдельная meta-таблица) либо оставляем минимальный текущий механизм; решается в разделе 11.
Spike подтвердил: Umzug 3.8.3 +
tsxвыполняют.ts-миграции end-to-end. Отдельно подтверждено, что.cjs-артефакты корректно грузятся в ESM-пакете, что нужно для сосуществования старых.cjsи новых.tsмиграций.
Модели переводим на TS/ESM (их импортирует рантайм). Команды миграций моделей не требуют.
3.4. Загрузчик моделей и типобезопасный объект db
Заменяем fs.readdirSync + require на явные import всех 39 моделей в db/models/index.ts
и сборку строго типизированного объекта db. Это убирает динамику (несовместимую с ESM-статикой),
даёт автодополнение и исключает any. Минус — вербозный index с 39 импортами, но это
разовый предсказуемый код. Динамический import() здесь не используем — только статические.
3.4.1. Политика импортов: статика по умолчанию
Решение: избегаем динамических импортов (import()), кроме случаев, где это действительно
оправдано. На текущем коде таких случаев нет:
- Единственный реальный динамический
require— загрузчик моделей (db/models/index.js:25) — переводится на статические импорты (3.4). - Найденные
import("…")— это JSDoc-аннотации типов, а не рантайм; в TS становятся обычнымиimport type. __dirname/__filenameв 7 файлах используются дляfs-чтения файлов (.env, HTML-шаблоны писем), не для импортов. Замена наpath.dirname(fileURLToPath(import.meta.url))сохраняет их статическими — это не динамический импорт.
Если в будущем потребуется ленивая загрузка (тяжёлый опциональный модуль, разрыв цикла
зависимостей), динамический import() допускается точечно и с обоснованием в коде, а не как
паттерн. Циклы зависимостей предпочтительно разрывать рефакторингом, а не import().
3.5. Стратегия типизации моделей Sequelize
Модели сейчас — фабрики sequelize.define('name', {...}). Чтобы не переписывать 39 моделей в
class-based стиль (большой рискованный diff), рекомендуется остаться на define(), но
добавить типобезопасность:
- На каждую модель — интерфейс атрибутов (
XAttributes) и creation-атрибутов (XCreationAttributes). - Фабрика возвращает
ModelStatic<Model<XAttributes, XCreationAttributes>>. associateтипизируется через общий тип реестраDb.
Это самый объёмный по трудозатратам пункт миграции (39 моделей + связи), поэтому он вынесен в отдельную подфазу и может идти параллельно остальному после готовности каркаса.
3.6. Типизация Express и слоёв
@types/express,Request/Response/NextFunction, расширениеRequestполемcurrentUserчерез declaration merging (src/types/express.d.ts).helpers.wrapAsync,commonErrorHandler, middlewares — строго типизированные сигнатуры.- db/api и services — публичные методы получают типы входных DTO и возвращаемых моделей.
4. Поэтапный план работ
Фаза 0 — Подготовка инструментов (без изменения рантайм-поведения)
- Установить dev-зависимости:
typescript,tsx,tsc-alias,@types/node, и типы для библиотек без собственных деклараций:@types/express,@types/cors,@types/passport,@types/passport-jwt,@types/jsonwebtoken,@types/nodemailer,@types/multer,@types/swagger-jsdoc,@types/swagger-ui-express,@types/bcrypt,@types/validator. (axios, sequelize, pg,dayjs,@json2csv/plainjs,umzugпоставляют типы сами;@types/lodashне нужен —lodashудаляется.) Добавить рантайм-зависимостьumzug(для TS-миграций, см. раздел 11). - Создать
backend/tsconfig.json(Фаза A — CommonJS):strict: true,noImplicitAny,strictNullChecks,noUnusedLocals,noUnusedParameters,noFallthroughCasesInSwitch(зеркало фронтенда).target: ES2022,module: CommonJS,moduleResolution: Node,esModuleInterop: true.allowJs: true,checkJs: false— для сосуществования.jsи.ts.outDir: dist,rootDir: src,baseUrl: src,paths: { "@/*": ["*"] }.
- Обновить ESLint: добавить
typescript-eslint, парсер для.ts, сохранитьimport-x/no-unresolvedс TS-резолвером. На Фазе A не ломать существующие.js-правила. - Добавить скрипт
typecheck: tsc --noEmitи завести его как обязательный гейт.
Критерий готовности фазы: npm run typecheck проходит на смешанной кодовой базе, приложение запускается как раньше.
Фаза 0.5 — Аудит и обновление зависимостей (совмещается с Фазой A)
Полный разбор — в разделе 10. Кратко по порядку:
- Удалить неиспользуемые драйверы и пакеты:
mysql2,tedious,sqlite(диалект вездеpostgres, 0 использований),lodash(один импортlodash/get, заменяется на optional chaining). - Заменить deprecated
json2csv@5.0.7→@json2csv/plainjs@7(26 файлов). - Заменить устаревшую
moment→dayjs(26 файлов). - Консолидировать загрузку файлов: убрать
formidable(1 файл), оставитьmulter; убратьbody-parser→express.json()(Express 5). - Убрать
sequelize-cli(миграции переходят на Umzug + tsx, см. раздел 11). OAuth-стратегии — отдельной задачей (см.full-integration-refactor-plan.md);sequelize-json-schemaпересмотреть при типизацииroutes/openai.ts. - Минорно поднять актуальные пакеты (
@google-cloud/storage7.19→7.21 и т.п.).
Эти изменения лучше делать вместе с типизацией соответствующих модулей, чтобы новые типы сразу ложились на новые API. Каждое — отдельным PR со смоук-проверкой.
Фаза A — Перевод на TypeScript (язык), модульная система = CommonJS
Мигрируем снизу вверх по зависимостям, по одному связному блоку за раз; после каждого блока — typecheck + ручной/смоук-прогон.
constants/(13 файлов) — листовые, без зависимостей. Перевести в.ts, экспортировать типизированные константы (as const).config/load-env.ts,config.ts— типизировать чтение env (хелперыrequiredEnv/readBooleanEnv/readNumberEnv/readListEnvуже есть, добавить типы и типConfig).helpers.ts,db/utils.ts— общие утилиты.db/models/*— типизация моделей (см. 3.5) + типобезопасныйdb/models/index.ts(см. 3.4). Самый крупный блок; можно дробить по доменам.db/api/*(28 файлов) — типизировать публичные статические методы, опираясь на типы моделей.services/*(48 файлов), включаяservices/notifications/errors/*,services/email/list/*.auth/*иmiddlewares/*— типизация passport-стратегий, cookies, проверки прав; declaration merging дляRequest.currentUser.ai/LocalAIApi.ts.routes/*(45 файлов) — типизированные роутеры; swagger-JSDoc-комментарии переносятся как есть (swagger-jsdoc парсит и.ts).index.ts— точка входа.- Существующие миграции/сидеры/
db.config/.sequelizerc— на Фазе A не трогаем (остаются.js, CommonJS-пакет их корректно исполняет). Перевод на Umzug — в Фазе B.
Критерий готовности: 0 .js в рантайм-коде (кроме исторических миграций/сидеров), typecheck зелёный, приложение работает.
Фаза B — Перевод на ESM (модульная система)
- Переключить tsconfig на ESM:
module: NodeNext,moduleResolution: NodeNext. ОтключитьallowJs(язык уже весь TS). - Поставить
"type": "module"вpackage.json. - Конвертировать синтаксис во всех
.ts:require→import,module.exports→export/export default. (Можно ускорить кодмодомcjstoesm— но результат вычитывать вручную; правило: не доверять слепо.) __dirname/__filename→path.dirname(fileURLToPath(import.meta.url))в 7 файлах.- Переписать
db/models/index.tsна статическиеimportвсех моделей. - Поправить относительные импорты под NodeNext (явные расширения в выходе обеспечивает
tsc-alias; алиас@— предпочтительный путь). - Миграции на Umzug + tsx (раздел 11): убрать
sequelize-cliи.sequelizerc; перевестиdb.config.js→db/config.ts; добавить раннерdb/migrate.ts(Umzug +SequelizeStorageнаSequelizeMeta); существующие 15 миграций оставить как.cjs, новые писать на.ts. Проверитьmigrate upна чистой БД и на БД с уже применённой историей. - swagger-jsdoc: сделать путь
apisenv-зависимым — dev:./src/routes/*.ts, прод:./dist/routes/*.js. - dev-запуск: заменить
nodemon/watcher.jsнаtsx watch src/index.ts(hot-reload + стриминг логов в реальном времени). Авто-применение миграций —predev-шаг (tsx src/db/migrate.ts up); если нужно сохранить применение «на лету» при добавлении файла, оставить лёгкий chokidar-вотчер наdb/migrations/db/seeders. Паритет с текущим поведением проверяется в Фазе D, п.4. db/reset.js(используетexecSync('sequelize ...')) — переписать на вызов Umzug-раннера (migrate down/upилиsequelize.syncдля dev), перевести в.ts/ESM.
Критерий готовности: npm run build (tsc + tsc-alias) собирает dist/, node dist/index.js стартует, миграции/сидеры проходят, все маршруты отвечают.
Фаза C — Скрипты, сборка, контейнеризация
package.jsonскрипты:dev: tsx watch src/index.tsbuild: tsc && tsc-aliasstart: node dist/index.js(прод; миграции — отдельной командой перед стартом)db:migrate: tsx src/db/migrate.ts up,db:rollback: tsx src/db/migrate.ts down,db:seed: tsx src/db/seed.ts(раздел 11)typecheck,lint,test.- Указать
engines.node: ">=24"(Active LTS, решение 0.5).
- Обновить корневой
package.json(build:production,start:production) под новый build-флоу бэкэнда. - Копирование ассетов в
dist/.tscсобирает только.ts. Не-TS ассеты, которые читаются черезfsпо пути отimport.meta.url, нужно копировать вdist/отдельным build-шагом: HTML-шаблоны писемsrc/services/email/htmlTemplates/**(используются вservices/email/list/*). Добавить вbuild(например,cpy/copyfiles/rsync) и проверить, что пути отimport.meta.urlрезолвятся вdist/. - Dockerfile: базовый образ
node:24-alpine(текущая Active LTS); добавить шагnpm run build, в прод-образ копироватьdist/(включая скопированные шаблоны) +node_modules(multi-stage build). Заменитьyarn installнаnpm ci(консистентно с корневымbuild:production). .dockerignore/.gitignore: добавитьdist/.
Фаза D — Тесты и верификация
- Добавить
vitest(консистентно с фронтендом) и базовый каркас тестов. - Покрыть unit-тестами критичные чистые модули:
config(парсинг env),helpers,db/utils, auth-хелперы, проверки прав. - Смоук-проверка рантайма: старт сервера,
/api-docs, healthcheck, 1–2 защищённых маршрута. - Проверка паритета dev-workflow (nodemon/watcher). Убедиться, что после перехода на
tsx watchопыт разработки не хуже текущегоwatcher.js+ nodemon:- Hot-reload: правка любого
.tsвsrc/**вызывает автоматический перезапуск сервера; в консоли видны сообщения о рестарте (аналогnodemon restarted due changes). - Логи в реальном времени:
console.*/логи приложения стримятся в stdout сразу, без буферизации; проверить, что вывод не теряется при рестарте (tsx watchне глушит stdout дочернего процесса). - Миграции в dev: новые миграции применяются ожидаемым образом. Текущий
watcher.jsавто-применял новые файлы вdb/migrations/db/seeders«на лету» через chokidar; план заменяет это наpredev-шагdb:migrate. Подтвердить, что выбранный сценарий (ручнойnpm run db:migrateпри добавлении миграции или сохранённый отдельный chokidar-вотчер) задокументирован и работает. Если авто-применение «на лету» нужно — оставить лёгкий watcher, запускающийtsx src/db/migrate.ts upна событиеadd. - Завершение процесса:
Ctrl+Cкорректно останавливаетtsx watchи дочерний сервер (нет «зависших» процессов на порту).
- Hot-reload: правка любого
- Обновить
docs/full-integration-refactor-plan.md(раздел Backend baseline): добавитьtypecheck,build, тесты. - Финальный гейт:
typecheck+lint+vitest+build+ ручной прогон миграций на чистой БД + подтверждённый паритет dev-workflow (п.4).
5. Типизация: где основной объём
| Слой | Файлов | Сложность типизации |
|---|---|---|
db/models |
39 | Высокая — атрибуты, creation-атрибуты, связи |
db/api |
28 | Средняя — DTO входа/выхода, опции транзакций |
routes |
45 | Средняя — Request/Response, currentUser, обёртки |
services |
48 | Средняя — бизнес-DTO, ошибки |
constants |
13 | Низкая |
| прочее (auth, middlewares, config, ai, helpers) | ~10 | Низкая–средняя |
Основной риск и трудозатраты сосредоточены в db/ (модели + api). Рекомендуется выделить
типизацию моделей в отдельный поток работ и при необходимости распараллелить по доменам
(люди/учёба/посещаемость/финансы/контент).
6. Риски и меры
- Переход миграций на Umzug — главный риск. Меры: использовать
SequelizeStorageс той же таблицейSequelizeMeta, чтобы история уже применённых миграций сохранилась; обязательно прогнать раннер и на чистой БД, и на БД с существующей историей; новые миграции —.ts, старые — оставить.cjsбез переписывания. (Базовый механизм подтверждён spike: Umzug 3.8.3 + tsx выполняют.ts-миграции.) - Разрастание
anyпод давлением сроков — запрещеноCLAUDE.md. Мера:typecheckкак блокирующий гейт; временно сложные места изолировать узкими типами, а неany. - Расхождение dev (
tsx) и прод (dist) — алиасы/расширения резолвятся по-разному. Мера: всегда проверять иnpm run dev, иnode dist/index.jsв CI. - Связи Sequelize теряют типы при
define()-подходе. Мера: общий тип реестраDb+ строго типизированныйassociate. - swagger пути ломаются после сборки. Мера: env-зависимый
apis-glob, проверка/api-docsв смоуке. - Большой diff усложняет ревью. Мера: PR на каждый связный блок (по разделам Фазы A), а не одним коммитом.
7. Стратегия отката
- Фаза A полностью обратима: TS компилируется в тот же CommonJS; при проблеме можно остановиться на любом блоке — смешанная кодовая база рабочая.
- Фаза B — отдельная ветка/серия PR; точка возврата — последний зелёный коммит Фазы A.
- Содержимое уже применённых миграций не меняется; Umzug читает ту же
SequelizeMeta, поэтому история БД не затрагивается. Откат миграционного слоя = вернуть прежнийdb/reset.js/CLI-вызовы.
8. Порядок исполнения (сводка)
Фаза 0 (инструменты) → Фаза A (TS, снизу вверх: constants → config → helpers/utils → models →
db/api → services → auth/middlewares → ai → routes → index) → Фаза B (ESM: tsconfig NodeNext,
type:module, синтаксис import/export, import.meta.url, загрузчик моделей, Umzug-миграции,
swagger, tsx) → Фаза C (скрипты/Docker/Node 24) → Фаза D (тесты/верификация/доки).
Оценка объёма намеренно не дана в часах: основной труд — типизация
db/(~61 файл) иroutes(45 файлов). После готовности каркаса (Фаза 0 + загрузчик моделей) остальное — предсказуемая механическая работа поблочно.
9. Открытые вопросы
Все вопросы по состоянию на 2026-06-09 закрыты — см. раздел 0 «Принятые решения».
Остаётся один пункт к проверке в коде по ходу работ: подтвердить замену sequelize-json-schema
при типизации routes/openai.ts (раздел 10.3).
10. Аудит зависимостей: обновление и замена
Версии проверены по npm-реестру на 2026-06-09 (не из памяти модели). Источник истины —
реестр; несколько пакетов в package.json (например, lodash, nodemailer, cors, eslint)
уже соответствуют последним стабильным релизам в текущем реестре, менять их не нужно.
10.1. Удалить (не используются в коде)
| Пакет | Версия | Причина |
|---|---|---|
mysql2 |
3.22.5 | Диалект БД везде postgres, 0 использований |
tedious |
19.2.1 | MSSQL-драйвер, 0 использований |
sqlite |
5.1.1 | 0 использований |
lodash |
4.18.1 | Единственный импорт lodash/get в services/notifications/helpers.js — заменяется на optional chaining (?.) |
Эффект: меньше поверхности атаки, легче и быстрее установка/сборка. Оставляем только драйвер pg + pg-hstore.
10.2. Заменить — deprecated / устаревшие
| Текущий | Статус | Рекомендуемая замена | Масштаб |
|---|---|---|---|
json2csv 5.0.7 |
deprecated («Package no longer supported») | @json2csv/plainjs 7.0.6 (официальный преемник, scoped-пакеты) |
26 файлов |
moment 2.30.1 |
Maintenance mode, проект сам рекомендует альтернативы | dayjs 1.11.21 (решено: минимальный diff, близкий API) |
26 файлов |
body-parser (в index.js) |
Избыточен в Express 5 | Встроенный express.json() |
1 файл |
formidable 3.5.4 |
Дублирует multer |
Консолидировать на multer 2.1.1 (популярнее, активнее), переписать services/file.js |
1 файл |
sequelize-cli 6.6.5 (devDep) |
Не поддерживает .ts-миграции |
Umzug 3.8.3 + tsx (раздел 11) | весь миграционный флоу |
Выбор dayjs зафиксирован (решение 0.1).
10.3. Заменить — опционально (низкая поддержка)
| Текущий | Последняя публикация | Вариант | Комментарий |
|---|---|---|---|
passport-google-oauth2 0.2.0 |
2022 | passport-google-oauth20 2.0.0 или openid-client 6.8.4 |
Низкая активность; passport-google-oauth20 — почти drop-in, openid-client — стратегически современнее, но больше работы |
passport-microsoft 2.1.0 |
2024 | оставить либо openid-client 6.8.4 |
Поддержка приемлема; единый openid-client под оба провайдера — отдельная задача |
sequelize-json-schema 2.1.1 |
2022 | zod-to-json-schema 3.25.2 или ручная схема |
Используется в 1 файле (routes/openai.js); пересмотреть при типизации этого роута |
Решено (0.7): OAuth-замена выносится в отдельную задачу после основной миграции (внесена
в docs/full-integration-refactor-plan.md), чтобы не смешивать риски (изменение потока
аутентификации ≠ смена языка/модулей).
10.4. Обновить версии (минор/патч, без замены)
@google-cloud/storage 7.19 → 7.21. Остальные ключевые рантайм-пакеты уже на актуальных стабильных:
express 5.2.1, helmet 8.2.0, axios 1.17.0, bcrypt 6.0.0, jsonwebtoken 9.0.3,
pg 8.21.0, pg-hstore 2.3.4, cors 2.8.6, passport 0.7.0, passport-jwt 4.0.1,
csv-parser 3.2.1, swagger-jsdoc 6.3.0, swagger-ui-express 5.0.1, chokidar 5.0.0,
nodemailer 8.0.10, multer 2.1.1.
Перед общим обновлением выполнить npm outdated и npm audit, фиксировать точные версии в lock-файле.
10.5. Sequelize — остаёмся на 6 (важно)
sequelize стабильный — 6.37.8; версия 7 существует только как alpha (7.0.0-alpha.9).
Пользователь просил стабильные версии, поэтому переход на Sequelize 7 (TS-native) сейчас
не делаем. ORM как таковой не меняем: переписывание моделей под другой ORM (Drizzle/Prisma)
противоречит принципу «минимальные изменения» и выходит за рамки миграции на TS/ESM. Это
возможная отдельная инициатива в будущем, но не часть данного плана.
10.6. Новые зависимости для TS/ESM (актуальные версии)
- Рантайм:
umzug3.8.3 (миграции, раздел 11). - Dev:
typescript6.0.3,tsx4.22.4,tsc-alias1.8.17,vitest4.1.8,typescript-eslint8.61.0,@types/node25.9.2,@types/express5.0.6,@types/multer2.1.0, и типы для остальных библиотек без собственных деклараций (см. Фаза 0, п. 1). - Не добавлять:
@types/lodash(lodashудаляется);@types/passport-google-oauth20(OAuth-замена отложена).
Целевая среда исполнения — Node 24 (Active LTS): удовлетворяет самым строгим engines.node
зависимостей (vitest и eslint требуют >=24).
10.7. Порядок применения
- Сначала удаления (10.1) — самый безопасный шаг, не меняет поведение.
- Затем замены (10.2) — модуль за модулем, вместе с типизацией соответствующих файлов в Фазе A.
- Опциональные замены (10.3) — после стабилизации основной миграции, отдельными задачами.
- Минорные апдейты (10.4) — единым PR с
npm auditперед финальной верификацией (Фаза D).
11. Лучшие практики: TS-миграции на Umzug
Umzug — библиотека-движок, на которой построен и сам sequelize-cli, поэтому переход совместим
по хранилищу истории. Подход (подтверждён spike: Umzug 3.8.3 + tsx исполняют .ts-миграции):
-
Единое хранилище истории. Использовать
SequelizeStorageс той же таблицейSequelizeMeta, что вёл sequelize-cli. Уже применённые миграции остаются зарегистрированными и повторно не запускаются — история БД не теряется. -
Сосуществование старого и нового. Glob включает и
.cjs(15 существующих миграций — не переписываем), и.ts(все новые).tsxгрузит оба формата. -
Типизированные миграции. Каждая новая миграция —
.tsс типизированным контекстомQueryInterface:// src/db/migrations/2026XXXX-create-foo.ts import type { QueryInterface } from 'sequelize'; import { DataTypes } from 'sequelize'; export async function up({ context: qi }: { context: QueryInterface }) { await qi.createTable('foo', { id: { type: DataTypes.UUID, primaryKey: true, defaultValue: DataTypes.UUIDV4 }, }); } export async function down({ context: qi }: { context: QueryInterface }) { await qi.dropTable('foo'); } -
Раннер с CLI. Один файл
src/db/migrate.tsсоздаётUmzugи пробрасывает CLI черезumzug.runAsCLI()(даёт командыup/down/pending/executed):// src/db/migrate.ts import { Umzug, SequelizeStorage } from 'umzug'; import { sequelize } from '@/db/models'; // существующий инстанс Sequelize export const migrator = new Umzug({ migrations: { glob: 'src/db/migrations/*.{cjs,ts}' }, context: sequelize.getQueryInterface(), storage: new SequelizeStorage({ sequelize }), // таблица SequelizeMeta logger: console, }); if (import.meta.url === `file://${process.argv[1]}`) { migrator.runAsCLI(); } -
Скрипты:
db:migrate=tsx src/db/migrate.ts up,db:rollback=... down,db:status=... pending. В Dockerfile/прод миграции запускать перед стартом (tsxставить в рантайм-зависимости либо запускать из собранногоdist). -
Сидеры. Второй экземпляр Umzug со своей meta-таблицей (
SequelizeMeta_seeders) и аналогичным раннеромsrc/db/seed.ts; существующие 5 сидеров перенести как.cjs(не переписывать) или по необходимости — на.ts. -
Замена
watcher.js. Авто-миграции в dev — черезpredev-шаг (tsx src/db/migrate.ts up), а не chokidar-вотчер; перезапуск сервера —tsx watch. -
Проверка перед мерджем. Прогнать раннер дважды: на чистой БД (все миграции применяются по порядку) и на БД с уже заполненной
SequelizeMeta(применяются только новые).