8.1 KiB
type, work_item_id, adr_id, title, status, date, authors, supersedes, superseded_by
| type | work_item_id | adr_id | title | status | date | authors | supersedes | superseded_by | |
|---|---|---|---|---|---|---|---|---|---|
| adr | ET-006 | ADR-002 | GPX-фича как отдельный модуль gpx.js | accepted | 2026-05-22 |
|
null | null |
ADR-002: GPX-фича как отдельный модуль gpx.js
Контекст
ET-006 добавляет самодостаточную фичу: парсинг GPX, внутренняя модель,
управление source/layer/маркерами карты, bottom sheet sheet-gpx,
canvas-профиль высот, расчёт статистики. Оценка объёма — ~600–900 строк JS.
ТЗ (02-trz.md §6) оставляет структуру файлов открытой и предлагает два
варианта: дописать в app.js либо вынести в отдельный gpx.js. ТЗ ссылается
на units.js как на прецедент.
Фактическое состояние кодовой базы (проверено):
src/web/units.jsне существует.app.js— единственный JS-файл фронтенда: 113 КБ, ~2900 строк.app.jsподключён как классический скрипт (<script src="app.js">), не как ES-модуль. Все функции — глобальные; обработчики событий навешаны через инлайновыеonclick="..."вindex.html.- Сборщика (bundler) нет. Статика раздаётся как есть через FastAPI
StaticFiles(mount/), Docker копирует каталог целиком (COPY src/web/ ./src/web/).
То есть «прецедент units.js» в ТЗ — иллюстративная неточность, но решение
о структуре файлов ТЗ явно делегирует этому этапу. Принимается архитектурно.
Решение
Реализовать GPX-фичу в новом файле src/web/gpx.js, подключённом
классическим <script> после app.js в index.html.
gpx.js владеет:
- парсингом GPX (см. ADR-003);
- внутренней моделью
window.gpxTracks(см.08-data-requirements.md); - управлением объектами карты: source / line layer / waypoint layers / маркеры;
- логикой
sheet-gpx(список треков, активный трек); - canvas-профилем высот и расчётом статистики (Haversine, набор/сброс высот).
Контракт интеграции (единственная поверхность связности)
gpx.js потребляет глобали, объявленные в app.js:
| Символ | Назначение |
|---|---|
window._map |
экземпляр MapLibre |
openSheet(id) / closeSheet(id) |
управление bottom sheet |
showToast(msg) |
уведомления об ошибках (см. ниже) |
app.js получает ровно одну новую строку — хук в rebuildMapOverlays():
// в конце rebuildMapOverlays()
if (typeof rebuildGpxOverlays === 'function') rebuildGpxOverlays();
Хук защищён typeof, поэтому app.js остаётся валидным и без gpx.js.
Это закрывает REQ-F-13 (восстановление GPX-слоёв после map.setStyle()).
Toast-хелпер
Сейчас существует только #ruler-toast + showRulerToast() — частный случай.
ET-006 требует несколько разных сообщений (REQ-F-02, REQ-F-03).
Рекомендуется обобщить до переиспользуемого showToast(message)
(элемент-контейнер + автоскрытие 4 с, стиль как у #ruler-toast — TRZ §3.4).
Конкретная реализация — на этапе разработки; дизайн toast — за дизайнером.
Изменения подключения
index.html: добавить<script src="gpx.js"></script>после строки 400 (<script src="app.js">), плюс разметку (кнопка#btn-gpx-upload,#tb-gpx,#sheet-gpx) — см. TRZ §3.- Новый статический ассет
gpx.jsподхватывается автоматически: Docker уже копирует весьsrc/web/, FastAPIStaticFilesотдаёт по/gpx.js. Инфраструктурных изменений нет (см.07-infra-requirements.md).
Рассмотренные альтернативы
Альтернатива A: дописать в app.js (отклонена)
app.jsуже 113 КБ — добавление ~800 строк ухудшает читаемость и время ревью.- Смешение несвязанных доменов в одном файле.
- Невозможно ревьюить/тестировать GPX-логику изолированно.
Альтернатива B: ES-модуль (<script type="module">) (отклонена)
- Потребовала бы перевода всех инлайновых
onclick-обработчиков наaddEventListenerи экспорт/импорт — большой blast radius на чужой код. app.jsне является модулем; смешивать module и classic-скрипты с общими глобалями хрупко.- Вне scope ET-006.
Альтернатива C: отдельный классический скрипт gpx.js (выбрана)
- Изоляция домена, минимальная поверхность связности.
- Совместимо с текущей моделью «всё глобальное, без сборщика».
- Ревью и тесты ET-006 локализованы:
gpx.js+ 3 правки разметки вindex.html+ 1 строка вapp.js.
Последствия
Положительные
- Первое разбиение фронтенда на несколько файлов — задаёт лёгкий паттерн «одна фича = один классический скрипт + глобали» для будущих задач.
- Поверхность ревью/тестирования ET-006 изолирована.
Отрицательные / митигации
| Последствие | Митигация |
|---|---|
Порядок подключения: gpx.js после app.js |
Межфайловые вызовы происходят в момент события — функции уже определены. Единственный вызов app.js → gpx.js защищён typeof. |
| Связность через глобали (нет инкапсуляции) | Контракт интеграции зафиксирован выше и узок (3 потребляемых символа + 1 хук). |
Нет showToast — нужно обобщать #ruler-toast |
Небольшой шаред-утиль; реализация на этапе разработки. |
Влияние на компоненты
- Frontend — новый файл
gpx.js, правкиindex.html, 1 строка вapp.js. - Backend / API / OSRM / БД — без изменений.
- C4-диаграммы — состав компонентов не меняется (фронтенд остаётся одним
логическим компонентом) → обновление C4 не требуется. Отдельных
.mmdв репозитории нет.
Связанные
- ТЗ:
docs/work-items/ET-006/02-trz.md(§6, REQ-F-13) - ADR-003: стратегия парсинга GPX
- Данные:
docs/work-items/ET-006/08-data-requirements.md - Инфра:
docs/work-items/ET-006/07-infra-requirements.md - Риски:
docs/work-items/ET-006/10-tech-risks.md