npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bx24-hmr

v0.6.0

Published

Hot Module Replacement for B24 dev. Launcher + dashboard + Chrome extension. Repo-clean: no in-place bundle writes.

Readme

bx24-hmr

Hot Module Replacement для модулей Bitrix24 с Vue/JS bundle-структурой (<module>/install/js/<module>/...). По умолчанию подключён im (мессенджер); через wizard или CLI можно добавить любое количество модулей — crm, imopenlines, tasks и т.п. Правка Vue 3 компонентов, CSS и Vuex-моделей — без F5 и без модификации артефактов сборки в репозитории modules.

Имя проекта im-hmr отражает исторический фокус на im v2; сейчас инструмент шире — отслеживаемые модули задаются списком в конфиге.

Состоит из трёх процессов:

bx24-hmr (npm-пакет)
├── launcher          HTTP :39125 — дашборд, REST + SSE, мастер первого запуска
├── HMR core          WS :39124 + bundle-server :39126 — fs.watch, persistent chef -w, патчи
└── chrome extension  загружается отдельно — content-script + DNR + плавающий UI

Лаунчер и HMR core живут в одном Node-процессе. Chrome-расширение — отдельно, грузится через chrome://extensions → "Загрузить распакованное".


Установка

Требуется Node.js ≥ 20.

npm install -g bx24-hmr
bx24-hmr

В PATH появится команда bx24-hmr — лаунчер с дашбордом.

macOS при первом запуске спросит разрешение на входящие сетевые соединения — это WS-сервер на :39124, который слушает Chrome-расширение. Жмём Allow.

Обновление:

npm update -g bx24-hmr

Бинарь без Node

Single-file бинарь (без зависимости от Node) — opt-in, собирается отдельным таском (см. docs/NEXT.md). На время foundation-перехода загрузка бинарей со страницы Releases приостановлена; для машин без Node используйте nvm/fnm или установите Node системно.

Первый запуск — мастер

После запуска открывается http://localhost:39125/ с мастером:

  1. Repo — путь к локальному клону modules (Mercurial). Автодетект сканирует ~/Projects, ~/PhpstormProjects, ~/Workspace, ~/dev, ~/code.
  2. chef — проверка CLI @bitrix/chef; если не установлен, один клик ставит его глобально через npm install -g @bitrix/chef.
  3. Build & VCS — выбор build-tool'а и VCS. По умолчанию автодетект (chef в PATH + .hg/ или .git/ в репе). Здесь же можно переключиться на произвольный shell-builder (например, npm run build) или сменить VCS вручную. См. docs/ADAPTERS.md.
  4. Extension — мастер распаковывает расширение в ~/.bx24-hmr/extension/ и копирует путь в буфер. Открыть chrome://extensions, включить Developer mode, Load unpacked, вставить путь.
  5. Messenger — открыть вкладку с порталом Bitrix24, дождаться когда расширение подключится к WS.

После окончания мастера на дашборде видно состояние всех частей: HMR core, chef, watcher, hg, подключённые клиенты, последние патчи.

IDE для open-in-editor (PhpStorm / WebStorm / VSCode / Cursor) выбирается позже — в Settings на дашборде.

Какие сайты обслуживаются

Расширение по умолчанию ни на чём не активно. Хосты добавляются в whitelist из popup-а расширения (toggle «Track this site» — добавляет текущий hostname). Управление списком и шаблонами доменов — на chrome-extension://…/options/options.html (Options).


Использование

На сайте из whitelist в углу появляется плавающий бейдж со статусом HMR. Сохранение Vue/CSS/Vuex-файла внутри отслеживаемого модуля (<repo>/<module>/install/js/<module>/**/src/**) — патч долетает до браузера за ~200 мс, F5 не нужен.

Список модулей хранится в config.json под ключом modules:

{
  "modules": [
    { "name": "im",  "root": "im",  "extensions": ["im.v2.application.messenger", "im.v2.component.**", "im.v2.model"], "enabled": true },
    { "name": "crm", "root": "crm", "extensions": ["crm.entity.**"], "enabled": false }
  ]
}

root — имя верхней папки модуля относительно repoPath. Watcher слушает всю папку модуля; фильтрация по src/ встроена. chef поднимается один раз в watch-режиме (chef build ext1 ext2 … -w, cwd = repoPath) и сам пересобирает изменённые extensions — без per-save spawn'а (даёт ~9× ускорение по медиане, см. docs/BENCH_CHEF_WATCH.md). Откатной режим --legacy-chef-spawn возвращает per-save spawn на случай регрессий.

| Что меняется | Стратегия | Что переживает hot-swap | |---|---|---| | Vue-компонент | __VUE_HMR_RUNTIME__.reload(id, def) | скролл, попапы, текст в textarea, state соседних компонентов | | CSS | cache-bust у <link href> | всё | | Vuex-модель | store.hotUpdate({modules:{...}}) | Vuex state | | im.v2.application.messenger | unmount + reload + initComponent | Vuex (Core живёт) — локальный UI теряется | | im.v2.application.core, ui.*, main.* | full page reload + sessionStorage snapshot | то что заснапшотили (черновики, скролл) |

Ошибки сборки

Если chef build падает — лаунчер показывает stderr в баннере на странице и в дашборде. Watcher продолжает работать; следующий валидный сейв триггерит ребилд.

Изменение инфраструктуры

Если меняется главный класс компонента или mount-нода — стратегия эскалируется до remount или полного reload (с попыткой восстановить state из sessionStorage).

Если в дереве im.v2.*/src/ появляется новый extension (новая папка с config.php) — нужен рестарт HMR core: dep-graph и список расширений собираются на старте.

Управление

  • Popup расширения (клик по иконке в Chrome): три toggle-а.
    • Track this site — добавить/убрать текущий hostname в whitelist.
    • Show in-page panel — спрятать/показать раскрывающуюся панель на странице (бейдж и тосты остаются).
    • Pause file watching — поставить scheduler на паузу (бэкенд лаунчера). Source of truth — GET /api/watcher/state на лаунчере; popup ресинкается при открытии.
  • Дашборд лаунчера на http://localhost:39125 — состояние HMR core и watcher, лента событий, конфиг (Settings — IDE, шаблон URL для open-in-editor, порты, extensions glob).
  • Hg-pause. Если в modules сделать hg up, hg revert, hg shelve — watcher автоматически встаёт на паузу до стабилизации tree (sliding window 5×2с, max-min ≤ 1). Это видно на дашборде и в баннере.

Self-update

После перехода на npm-дистрибутив встроенный self-updater (скачивание бинаря из GitLab Releases) приостановлен. Обновление — стандартной командой:

npm update -g bx24-hmr

Лаунчер всё ещё дёргает GitLab Releases API для отображения баннера «доступна новая версия», но кнопка установки в дашборде на время foundation-перехода отключена. Полноценная интеграция с npm-registry для проверки версии — отдельный таск.


Архитектура

Подробности — в docs/ARCHITECTURE.md. Кратко:

  • Launcher (src/launcher/) — Node-процесс, лайфцикл, конфиг в ~/.bx24-hmr/config.json, HTTP-дашборд (:39125), REST /api/* + SSE /api/events, мастер первого запуска, supervisor HMR core.
  • HMR core (src/server/) — persistent chef -w (один долгоживущий процесс на enabled-набор extensions) + @parcel/watcher на <repo>/.../src/** для классификации, hg-monitor, pause-controller, dep-graph для cascade-ребилдов lib-ов, bundle-server на :39126 (отдаёт инструментированные bundle'ы по HTTP — без записи на диск в repo), WS-сервер на :39124 для отдачи патчей.
  • Chrome extension — исходники в src/extension/, сборка в dist/extension/ (npm run build:ext транспилирует .ts → .js и копирует ассеты). MV3, три контекста (service worker, content_script в isolated world, page_entry в main world). DNR-правила редиректят запросы за *//bitrix/js/im/v2/**/*.bundle.{js,css}(.map)? на bundle-server лаунчера. Hot-swap живёт в client/handlers/* (component / css / model / pull / infra / full-reload).

Главное архитектурное свойство: репозиторий modules не модифицируется. Инструментирование bundle'ов происходит в памяти bundle-server'а на каждом HTTP-запросе. hg status остаётся чистым.


Сборка из исходников

Код пишется на TypeScript. Все новые файлы — .ts; оставшиеся .mjs/.js — legacy, мигрируются параллельно. В dev-режиме .ts читаются через tsx (node --import tsx, подключено в npm-скриптах). Для продакшен-дистрибутива всё бандлится через esbuild в dist/ и публикуется в npm registry.

Зависимости:

  • Node.js ≥ 20 — для запуска из исходников и для собранного npm-пакета.
  • Mercurial (hg) — для целевого репозитория modules.
  • Git — для самого этого репозитория.
git clone <repo> ~/Projects/im-hmr
cd ~/Projects/im-hmr
npm install
npm run start             # запуск из исходников через node --import tsx, открывает дашборд в браузере

Сборка дистрибутива (что попадёт в npm-пакет):

npm run build              # esbuild → dist/launcher, dist/server, dist/tools, dist/extension, dist/launcher/ui
npm run build:clean        # то же, но с предварительной очисткой dist/
npm run build:ext          # только Chrome-расширение → dist/extension/

Результат — в dist/. После npm run build локально можно запустить node bin/bx24-hmr, проверить чисто dist-пайплайн.

Подробности про разработку (smoke-test, инспект состояния, гочи) — в docs/DEV.md.


Релиз

Версия — single source of truth в package.json#version. tools/sync-version.ts пропагирует её в src/extension/manifest.json и в строку-баннер лаунчера. Build-pipeline (tools/build-dist.ts) запускает sync-version перед сборкой.

End-to-end релиз — npm run release (требует npm login под mainтейнером пакета):

npm version patch --no-git-tag-version    # бамп package.json без git-tag (тег создаёт release.ts)
git commit -am "v$(node -p 'require(\"./package.json\").version')"
npm run release:dry                        # печатает план без побочных эффектов
npm run release                            # build dist → npm publish → git tag → push

prepublishOnly гарантирует, что npm publish всегда происходит со свежесобранным dist/ — даже если запускать npm publish напрямую, мимо tools/release.ts.

Полный гайд для мейнтейнера — в docs/RELEASE.md.


Документация

Код пишется на TypeScript (новые файлы — .ts, миграция оставшихся .mjs/.js идёт параллельно). Dev-run — через node --import tsx, compile-сборка — через Bun (читает .ts нативно). Подробнее про конвенции — docs/STYLE.md, про состояние миграции — docs/TYPECHECK_BASELINE.md.

  • docs/ARCHITECTURE.md — детали трёх процессов, bundle-server, pause-controller, communication channels, state storage.
  • docs/DEV.md — запуск из исходников, smoke-test, typecheck, hot-paths в коде, гочи. Раздел Perf baseline — про npm run bench и big-repo fixture (--fixture=big) для нагрузочных замеров.
  • docs/STYLE.md — code style: narrative-структура файла, антипаттерны проекта, кандидаты на рефакторинг.
  • docs/TYPECHECK_BASELINE.md — состояние миграции на TS, snapshot ошибок tsc.
  • docs/RELEASE.md — процесс релиза, флаги release.mjs, чек-листы.
  • docs/NEXT.md — текущая итерация (distribution & updates), что уже сделано и что в работе.

Power-user CLI

Старый Node-CLI без дашборда — для тех кому не нужен GUI:

npm run hmr -- --repo /path/to/modules \
               --port 39124 \
               --module im:im:im.v2.application.messenger,im.v2.component.**,im.v2.model

Health-эндпоинт: http://127.0.0.1:39125/__hmr_status.

GUI-лаунчер поддерживает headless-флаги для скриптовых сценариев — они перезаписывают ~/.bx24-hmr/config.json на время запуска (без сохранения):

# Подключить два модуля одним вызовом
bx24-hmr --module im --module crm:crm:crm.entity.**

# Build/VCS переопределения
bx24-hmr --builder=shell --builder-cmd="npm" --builder-args="run,build" --vcs=git

Старый флаг --extensions поддерживается ради обратной совместимости (deprecated, выдаёт warning) — он применяется к extensions первого enabled-модуля. Полный список — bx24-hmr --help.

Профилирование пайплайна

Флаг --profile включает потактовый лог HMR-пайплайна — для случаев, когда патч долетает не за ~200 мс, а за секунды, и непонятно где именно теряется время.

bx24-hmr --profile

На каждой стадии в stdout пишется строка вида [profile] stage=<имя> evt=<id> key=value .... Каждое fs-событие получает короткий evt=<id> (base36-счётчик), который протягивается через весь pipeline — все стадии одного события можно собрать одной командой grep "evt=2 ".

Стадии

| Стадия | Когда логируется | |-------------------|-------------------------------------------------------------------------------------------------------------------| | fs-event | fs.watch поймал изменение src-файла — это t0 для total. | | enqueue | От fs-event до первого резолва extension'а (async classify()) и постановки в pendingExts. В total попадает только первый замер на пару (evt, ext). | | re-enqueue | Повторный classify() той же пары (evt, ext) в активной цепочке (быстрый бурст сейвов того же файла). В total не входит; считается в dup-enqueue=N строки total. | | pause-active | Глобально: scheduler вошёл в паузу (reason=hg-pull/branch-switch/hg-merge/src-burst/…). | | pause-resume | Глобально: scheduler вышел из паузы. ms — длительность паузы. | | debounce | Время от первого scheduleFlush до flushDebounce (обычно ~250 мс — burst схлопывается). | | queue-wait | От постановки батча в buildQueue до фактического старта runChefBuild. Растёт, когда builder уже занят. | | builder-spawn | От spawn() chef-процесса до первого байта stdout/stderr (cold-start стоимости). | | chef-build | Длительность builder.buildOnce (сам chef). | | dist-event | fs.watch увидел изменение dist-bundle (между chef-build и build-patch). | | build-patch | Построение Patch[] в onChange artifact-watcher'а (resolver + classifier + hmr-id postprocess). | | bundle-instrument | Постпроцессинг bundle при ответе bundle-server'а клиенту (отдельный hot-path). | | ws-push | Broadcast патчей по ws-клиентам. | | total | От fs-event до ws-push. Содержит breakdown по основным стадиям + other для всего необъяснённого. |

Пример выхода

[profile] stage=fs-event evt=2 file=/repo/im/install/js/im/v2/component/dialog/src/dialog.vue
[profile] stage=enqueue evt=2 ext=im.v2.component.dialog ms=3.1
[profile] stage=debounce evts=2 pending=1 ms=251.3
[profile] stage=queue-wait evts=2 count=1 ms=0.2
[profile] stage=builder-spawn evts=2 count=1 ms=42.4
[profile] stage=chef-build evts=2 exts=im.v2.component.dialog count=1 ms=1432.7
[profile] stage=dist-event file=/repo/im/install/js/im/v2/component/dialog/dist/dialog.bundle.js ms=0
[profile] stage=build-patch evts=2 files=1 patches=1 ms=6.4
[profile] stage=ws-push evts=2 patches=1 clients=1 ms=0.4
[profile] stage=total evt=2 clients=1 ext=im.v2.component.dialog ms=1736.1 enqueue=3.1 debounce=251.3 queue-wait=0.2 builder-spawn=42.4 chef-build=1432.7 build-patch=6.4 ws-push=0.4

Пример outlier'а с паузой (long pull/merge):

[profile] stage=fs-event evt=5 file=…/some.vue
[profile] stage=enqueue evt=5 ext=im.v2.component.dialog ms=2.4
[profile] stage=pause-active reason=hg-pull
… 65 секунд …
[profile] stage=pause-resume reason=hg-pull ms=65120.0
[profile] stage=chef-build evts=5 exts=im.v2.component.dialog count=1 ms=3533.5
[profile] stage=ws-push evts=5 patches=2 clients=2 ms=0.1
[profile] stage=total evt=5 clients=2 ext=im.v2.component.dialog ms=68660.2 pause=65120.0 enqueue=2.4 chef-build=3533.5 ws-push=0.1 other=4.2

Здесь pause=65120.0 сразу видно в строке total — задержка не в chef'е, а в hg-паузе.

Чтение breakdown

  • total ms=… — wall-clock от fs-event до ws-push.
  • Перечисленные стадии — длительности соответствующих фаз; сумма примерно равна total (отклонение может быть из-за overlap'ов).
  • othertotal минус все известные стадии. Если регулярно > 50 мс — стадия пропущена в инструментировании, стоит добавить.
  • dup-enqueue=N — сколько повторных постановок (evt, ext) отброшено в стадии enqueue (быстрые сейвы того же файла, дубли watcher'а). Большое значение — сигнал «шумного» fs-источника.
  • warn=stages-mismatch — сумма стадий превысила total больше чем на 100 мс. Чаще всего значит, что какая-то стадия атрибутировалась не на тот evt (баг профайлера, а не пайплайна).

Без --profile overhead на горячем пути нулевой (все методы профайлера — no-op'ы).