# Раздача приложения: что делают за нас библиотека и сборщик Отвечает на вопросы, возникшие по ходу задачи `spa-skeleton`, — какие свойства раздачи приходят не из нашего кода, а из стандартной библиотеки, из PocketBase и из инструментов приложения. Наблюдения понадобились потому, что ревью нашло три места, где записанное намерение расходилось с тем, что на деле делает чужой код. ## Как снималось Прогонами на живом бинарнике (свой конфиг с выдуманными ключами, свой каталог данных вне репозитория) и чтением исходников зависимостей, зафиксированных в `go.mod`: `github.com/pocketbase/pocketbase` версии **v0.39.10** и стандартной библиотеки Go. Отдельно — прогоны установщика и сборщика приложения в контейнере. Числа ниже сняты 2026-08-15 на этом прогоне, а не взяты из чужих записок. ## Что выяснилось - **Маршрутизатор стандартной библиотеки сравнивает сегменты пути после раскодирования.** Поэтому `/%5f/` попадает туда же, куда `/_/`, а `/%68ealth` — туда же, куда `/health`: ответы совпадают байт в байт. Исходная форма остаётся в `URL.RawPath`, и решение, принимаемое **вне** сервиса по сырому пути — правилом обратного прокси, — такой формы не видит. Цена записана в [security.md](../security.md), «Периметр»: барьер перед панелью владельца обходится подменой одного знака. - **PocketBase пишет каждый запрос в свою таблицу журнала**, а не только в вывод контейнера: слой `activityLogger` подключён ко всем маршрутам и кладёт путь целиком (до 3000 знаков), адрес отправителя, источник перехода и клиент. Умолчания — хранить пять суток, адрес записывать. Готовая раздача статики (`apis.Static`) первой же строкой ставит признак «успех не записывать»; своя раздача этого признака не наследует, и его надо ставить руками. Отсюда правило в [review.md](../review.md): журналов **два**, и говорить надо про оба. - **Вшитая файловая система не несёт времени правки.** `embed.FS` отдаёт нулевое время у любого файла, поэтому отдача файла стандартной библиотекой никогда не отвечает подтверждением «не менялось» — всякая проверка приходит полным телом. Заголовок, обещающий дешёвую проверку, без метки ответа обещает то, чего код не делает. - **Сборщик приложения чистит выходной каталог перед каждой сборкой.** Метка, положенная рядом с собранным ради того, чтобы каталог существовал в git, уезжает первым же прогоном. Живёт она только этажом выше выходного каталога. - **Проверка типов однофайловых компонентов не работает с седьмой линией TypeScript.** `vue-tsc` версии 3.3.10 зовёт у компилятора точку входа, которой новый компилятор не отдаёт, и сборка падает на этапе проверки типов. Рабочая пара — пятая линия TypeScript. - **Установщик пакетов без сети не отказывает, а виснет.** Он уходит в повторы с нарастающей паузой **на каждом пакете**, и набор проверок вместо кода отказа просто стоит. Пределы у отдельных обращений положения не спасают: их сумма и даёт зависание. Помогает короткое обращение-проба перед установкой. - **Вес приложения в бинарнике равен весу собранного.** Замер: две сборки, с собранным приложением и с пустым каталогом, разница — 86 072 байта, то есть ровно `index.html` плюс единственный ресурс. Собранное приложение на четыре экрана в разведке `spa-framework` весило того же порядка. ## Чего эта записка не узнала - **Во что ступень сборки обходится образу по времени.** Прогон до конца не доходит: из контейнеров этой машины нет исходящей сети при рабочем разрешении имён. По весу вопрос закрыт иначе — ступень в рабочий слой не копируется, и финальный образ от неё не растёт вовсе. - **Как поведёт себя раздача под настоящим потоком.** Ограничителя частоты на корневом маршруте нет, а профиля нагрузки у проекта нет тоже. - **Что делает настоящий браузер** с этими заголовками: проверено кодами ответов и заголовками, а не браузером.