В последние годы в разработке на JavaScript закрепился стереотип: чтобы системная утилита работала быстро, ее обязательно нужно переписать на компилируемом языке — Rust, Go или Zig. Так появились Bun, uv и тяжелые бинарные дистрибутивы пакетных менеджеров. Но у этого подхода есть скрытая цена: бинарники весят десятки мегабайтов. На серверах непрерывной интеграции (CI/CD) и в облачных контейнерах скачивание 50-мегабайтного исполняемого файла для каждого тестового прогона нередко отнимает больше времени, чем экономит сам нативный менеджер. Команда UnJS разрушила этот миф, выпустив upm — полноценный клиент для реестра npm на чистом TypeScript, который занимает всего 320 КБ на диске.
Миф о нативных бинарниках: почему Node.js может быть быстрым
Анализ сетевых процессов показывает, что главным узким местом при установке зависимостей является вовсе не скорость выполнения процессорных инструкций, а ввод-вывод (I/O): сетевые запросы к реестру пакетов, распаковка архивов и создание ссылок в файловой системе. Современный рантайм Node.js 22 уже содержит быстрый встроенный HTTP-клиент на базе Fetch API, оптимизированный модуль сжатия zlib и системный пул потоков worker_threads.
Используя только эти стандартные возможности платформы, upm параллельно запрашивает метаданные версий и разрешает граф зависимостей со скоростью нативных утилит. При этом дистрибутив пакета в архиве npm весит всего 107 КБ, а в распакованном виде — 320 КБ. Для сравнения: pnpm 12 занимает 59,8 МБ, а нативный менеджер aube на Rust — 44,5 МБ.
Программный интерфейс: установка пакетов без терминала
Обычные пакетные менеджеры разрабатываются как консольные утилиты (CLI). Если разработчику нужно автоматизировать сборку окружения или написать собственный генератор проектов, ему приходится запускать дочерние процессы операционной системы через child_process.spawn(). Это порождает лишнюю нагрузку на процессор и создает риски уязвимостей командной инъекции.
В upm каждая команда изначально спроектирована как обычная асинхронная JavaScript-функция:
// scripts/setup-sandbox.ts — управление зависимостями через программный API
import { install, add } from 'upm';
async function bootstrapEnvironment() {
// 1. Установка базовых зависимостей с жесткой фиксацией версий
const installResult = await install({
dir: './sandbox',
frozen: true, // режим строгого соответствия lock-файлу для CI
log: (msg, level) => {
if (level === 'warn' || level === 'error') console.error(`[upm] ${msg}`);
},
});
console.log(`Установлено пакетов: ${installResult.packages}`);
// 2. Программное добавление тестового фреймворка в секцию devDependencies
await add(['vitest@^3'], {
dir: './sandbox',
group: 'devDependencies',
});
}
bootstrapEnvironment().catch(console.error);
Утилита также поддерживает хранилище на жестких ссылках (~/.upm/store), исключая дублирование файлов на диске между разными проектами, и бесшовно читает чужие файлы фиксации: package-lock.json от npm, pnpm-lock.yaml и bun.lock.
Безопасность из коробки: защита от атак первого дня
Особое внимание авторы уделили безопасности цепочки поставок (supply-chain security). В экосистеме npm участились атаки, когда злоумышленники захватывают аккаунты мейнтейнеров и публикуют вредоносные версии популярных пакетов, надеясь заразить ранние автоматические сборки.
В upm встроен защитный интервал публикации:
# .npmrc — параметры безопасности и изоляции проекта
# Блокировать установку пакетов, опубликованных менее 2 суток назад
min-release-age=2
# Исключения для доверенных внутренних пакетов компании
min-release-age-exclude[]=@mycompany/*
# Запрет неявного подъема транзитивных зависимостей
hoist=false
По умолчанию действует правило min-release-age = 1 (24 часа): уязвимые пакеты блокируются автоматически, давая сообществу и сканерам время обнаружить инцидент. Кроме того, upm по умолчанию пропускает скрипты жизненного цикла (postinstall), предотвращая запуск несанкционированного кода в процессе установки.
Когда легковесный клиент заменяет привычный тулчейн
Инструмент находится в статусе предварительного релиза и пока имеет функциональные ограничения. В нем нет поддержки Git-зависимостей, требующих локальной сборки через клонирование репозитория (поддерживаются только прямые архивы tarball), а при работе с чужими lock-файлами поддерживается только операция установки без изменения состава зависимостей.
Тем не менее для облачных песочниц, легковесных микросервисов, CI-конвейеров и встраивания в CLI-утилиты upm предлагает идеальный баланс: отсутствие мегабайтных бинарников, безопасность по умолчанию и прямое программное управление из кода.
