Зеркало #3216 для Site Finder. Отдельным PR, чтобы CI двух приложений не
блокировали друг друга.
Мажор ради GHSA-5xrq-8626-4rwp (CRITICAL, vitest ≤2.x). Сам CRITICAL к нам
неприменим — он про `--ui`, а `@vitest/ui` не установлен и скриптов с `--ui`
нет — но держать заведомо непатченный рантайм тестов ради этого рассуждения
не стоит.
vitest тянет vite сам, и у мажора другой диапазон (2.1.9 → vite ^5.0.0;
3.2.7 → ^5||^6||^7), поэтому одним мажором:
vitest 2.1.9 → 3.2.7 GHSA-5xrq-8626-4rwp
vite 5.4.21 → 7.3.6 GHSA-4w7w-66w2-5vf9, GHSA-fx2h-pf6j-xcff, GHSA-v6wh-96g9-6wx3
esbuild 0.21.5 → 0.28.2 GHSA-67mh-4wv8-2f99
OSV по локу: 4 → 1. Остаток — postcss 8.4.31, вендоренный ВНУТРИ
next/node_modules; нашим локом не управляется.
@vitejs/plugin-react 4.3.4 → 5.2.0 — вынужденно: у 4.x peer на vite ^4||^5||^6,
семёрка туда не попадает. В 6.x не идём — там peer уже ^8.0.0.
vitest.config.ts не тронут: в нём нет ничего из переименованного/убранного
мажором (ни environmentMatchGlobs, ни deps.inline, ни workspace).
Приёмка (локально, node 26):
npm run test 278 passed, 13 failed
npm run type-check ok
13 падений — те же самые, что на vitest 2 (те же 4 файла: api, useParcelAnalyzeQuery,
AnalysisPageContent.weights, WeightProfilePanel.identity; TypeError на
src/lib/sessionId.ts:8, localStorage undefined). Мажор их не добавил и не убрал.
Причина локальная — node 26 против node 20/24 в CI. Гейт — CI.
Лок не пересобирали с 3 июля (у «Меры» — 7 августа), и весь разрыв по уязвимостям
между двумя фронтендами объясняется этим. Прогон OSV: было 19 уязвимых пакет-версий,
стало 4. package.json не тронут — все фиксы влезли в существующие каретки.
Главное — Next: у 15.5.15 висело 21 advisory, 10 из них HIGH (обходы middleware/
proxy, SSRF в Server Actions и rewrites, набор DoS). Максимальный fixed_in среди них
— 15.5.21, то есть ВСЕ закрываются внутри ветки 15.5.x. Переход на 16 для
безопасности не требуется, это отдельная миграция.
next / eslint-config-next 15.5.15 -> 15.5.24
sharp 0.34.5 -> 0.35.4 унаследованные дыры libvips
postcss 8.5.10 -> 8.5.26 чтение произвольного .map по
sourceMappingURL
nanoid 3.3.11 -> 3.3.18
js-yaml 4.1.1 -> 4.3.2 квадратичный CPU на merge-key
form-data 4.0.5 -> 4.0.6 CRLF-инъекция
brace-expansion ... -> 1.1.18 три DoS
@babel/core 7.29.0 -> 7.29.7
@opentelemetry/core 2.7.1 -> 2.10.0
echarts 6.0.0 -> 6.1.0
@sentry/nextjs 10.53.1 -> 10.72.0
tailwindcss + postcss-плагин 4.2.4 -> 4.3.3
react / react-dom 19.2.5 -> 19.2.8
Дерево схлопнулось с 1010 до 749 пакетов — это следствие двухмесячной давности
лока, а не косметика, поэтому приёмка шла через полный npm ci, а не по диффу.
Остаются 4 и не чинятся здесь: postcss 8.4.31 Next вендорит ВНУТРИ своего
node_modules, а vitest 2.1.9 -> vite -> esbuild требуют мажора vitest — отдельная
задача.
Проверено локально: npm ci с нуля и npm run build проходят, сборка Next 15.5.24 +
tailwind 4.3.3 + sentry 10.72 отдаёт все маршруты. npm run test локально даёт 13
падений в 4 файлах (localStorage undefined при определённом window) — это jsdom 25
под здешним node 26; версии тестового стека в диффе НЕ менялись (jsdom 25.0.1,
vitest 2.1.9, vite 5.4.21 те же), CI гоняет node 20 и он тут гейт.
NB для тех, кто повторит локально: npm 12 по умолчанию режет remote-загрузки
(allow-remote=none) и спотыкается об опциональную @tailwindcss/oxide-wasm32-wasi.
Обходится флагом --allow-remote=all на время команды; в CI npm 10, там этого нет.
openapi-codegen-check был RED на каждом PR: CI шаг гонял bare `npx prettier`
(резолвится в плавающий latest), а committed api-types.ts был отформатирован
старым prettier (≤3.6.2, union-типы multi-line). 3.9.0 переносит union-wrapping
на single-line → CI-регенерация всегда давала diff → gate падал.
Чиним детерминизмом одной версии на обоих путях:
- frontend/package.json: добавлен prettier@3.9.0 (exact) в devDependencies
+ обновлён package-lock.json.
- CI openapi-codegen-check: bare `npx prettier` → ./node_modules/.bin/prettier
(project-local pinned, не плавает).
- .pre-commit-config.yaml: prettier hook additional_dependencies=["prettier@3.9.0"]
— тот же движок, что в CI (alpha.8-обёртка байт-в-байт == prettier 3.9.0).
- api-types.ts перегенерирован prettier 3.9.0 (union-типы single-line).
Backend OpenAPI-схема не менялась — только формат. CI-регенерация теперь
байт-в-байт совпадает с committed-файлом и с выводом pre-commit hook.
Port the prototype massing.js to a client-only React component (MassingScene)
behind a dynamic(ssr:false) wrapper (MassingViewer), wired into the future3d +
insolation drawers, replacing the СКОРО SoonCards.
Generative mass from parcel area (real geometry_suitability.area_ha) × КСИТ-цель
3.5 (labelled regulation-default · НСПД — honest, real max_far not in /analyze),
OrbitControls (orbit/zoom/auto-rotate), time-of-day sun with shadows (insolation
preview), live GFA/КСИТ-факт metrics (green ≤3.5 / amber over). Full renderer/
scene/RAF dispose on drawer close; graceful WebGL-unavailable fallback.
Adds three@0.184.0 + @types/three (package.json + package-lock.json in sync).
SSR-isolated (three imported only in MassingScene). TS strict, no any.
3 pages (market, PRINZIP drilldown, developers leaderboard) on top of
existing v_developer_full_metrics + domrf_realization views. ECharts on
the frontend, FastAPI router /api/v1/analytics on the backend.
- backend/Dockerfile: split builder/runner, runtime libs only, non-root app user, curl for healthcheck, --frozen via new uv.lock
- frontend/Dockerfile: npm ci instead of npm install (deterministic), USER node
- docker-compose.prod.yml: working backend healthcheck (curl now in image), redis healthcheck, drop dead NEXT_PUBLIC_API_BASE_URL env (Next.js bakes NEXT_PUBLIC_* at build time, runtime override is no-op)
- docker-compose.yml: same redis healthcheck, BACKEND_URL for next.config rewrites, drop uv from CMD (not in new image)
- Caddyfile: handle (not handle_path) so /api prefix is preserved into FastAPI router
- next.config.ts: rewrite /api/* and /health to BACKEND_URL in dev (no Caddy locally)
- frontend/src/lib/api.ts: empty default = same-origin relative URLs
- Makefile: drop uv run from migrate target
- Add backend/uv.lock and frontend/package-lock.json for reproducible builds
Verified: docker build succeeds for both, backend container starts, /health responds, curl healthcheck works inside image, container runs as non-root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>