Версионирование

Что означает 0.x на практике, как пакеты семейства связаны версиями и чего библиотека пока не обещает.

Вопрос внедренца звучит не «какая сейчас версия», а «сколько эта версия проживёт». Честный ответ на сегодня: окно поддержки не объявлено. Ниже — что вместо него есть, что из этого проверяемо и что делать, пока обещания нет.

Пока 0.x

Библиотека следует Semantic Versioning, и до 1.0 это значит ровно одно: минорный выпуск может сломать совместимость. Не «теоретически» — это уже происходило. Версия 0.41.0 вынесла GrCodeBlock из ядра в @feugene/granularity-code, и подпуть @feugene/granularity/components/GrCodeBlock исчез:

diff
- import { GrCodeBlock } from '@feugene/granularity'
+ import { GrCodeBlock } from '@feugene/granularity-code/components/GrCodeBlock'

Причина названа в журнале изменений и она про 1.0: вынести что-то из замороженного ядра стоит мажора, поэтому переезды такого рода делаются до заморозки, а не после.

На нулевом мажоре ^ и ~ означают одно и то же. ^0.41.0 разворачивается в >=0.41.0 <0.42.0 — тот же диапазон, что у ~0.41.0. То есть менеджер пакетов и так закрепляет минор за вас: обновление до 0.42 требует осознанного действия, а не приезжает с install.

Каждый пакет версионируется сам

Общей версии у семейства нет, и это не недосмотр. У ядра и спутников разный цикл: @feugene/granularity-code живёт на 0.1.x, пока ядро идёт к 0.42, — и лишний мажор в спутнике ради выпуска ядра означал бы обновление, за которым не стоит ни одного изменения.

Из этого следует три вещи, и все три видны в репозитории:

  1. У каждого пакета свой журнал изменений. Общего журнала в корне нет намеренно — журналы на портале собраны из них.
  2. У каждого пакета свой тег. Ядро выпускается тегом vX.Y.Z, спутник — <имя-каталога>-vX.Y.Z, например granularity-charts-v0.11.0.
  3. Публикация идёт из тега. Пуш тега запускает публикацию в npm с --provenance и в GitHub Packages: у выпущенного артефакта есть прослеживаемое происхождение, и это проверяет ваш сканер, а не мы.

Матрица совместимости

Спутник объявляет, с какой версией ядра и пресета он работает, — не словами, а диапазоном в peerDependencies. Ваш менеджер пакетов прочитает его сам и откажется собрать несовместимую пару. Здесь тот же диапазон в читаемом виде:

ПакетЯдро @feugene/granularityПресет @feugene/unocss-preset-granularVue
@feugene/granularity-charts>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-chrono>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-code>=0.40.0 <1.0.0>=0.16.0 <1.0.0^3.5.0
@feugene/granularity-dashboard>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-datasource^3.5.0
@feugene/granularity-devtools>=0.38.0 <1.0.0^3.5.0
@feugene/granularity-editor>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-forms-schema>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-media>=0.38.0 <1.0.0>=0.13.0 <1.0.0^3.5.0
@feugene/granularity-test-kit>=0.13.0 <1.0.0^3.5.0
@feugene/unplugin-granularity>=0.38.0 <1.0.0

Прочерк означает, что пакет этой зависимости не объявляет вовсе: granularity-datasource не знает ни о ядре, ни о пресете — он про данные, а не про разметку, — а test-kit работает с любым ядром, потому что проверяет CSS и DOM, а не импортирует компоненты.

Верхняя граница у всех одна и та же — <1.0.0. Она говорит не «мы совместимы со всем до единицы», а «дальше границы обещаний нет»: 1.0 переопределит контракт, и диапазоны будут переписаны вместе с ним.

Таблица не набрана руками, а сверяется с манифестами на каждой сборке: диапазон, разошедшийся с package.json спутника, роняет сборку портала. Но сверяется она с тем тегом, на который закреплён подмодуль библиотеки, — а не с тем, что лежит в npm сию секунду. Источник истины — peerDependencies установленного пакета.

Требования к окружению

ЧтоВерсияОткуда
Node>=22engines во всех пакетах семейства
Vue^3.5.0peer-зависимость
Формат модулейтолько ESM"type": "module", CommonJS-сборки нет

Эти три строки — тоже часть совместимости, и меняются они по тем же правилам: поднять нижнюю границу Node — ломающее изменение, и до 1.0 оно может приехать минором.

Что произойдёт на 1.0

До 1.0 версионирования документации нет: есть только /docs, без префиксов. Преждевременное версионирование удваивает работу и путает поиск — архив, у которого нет читателей, стоит ровно столько же, сколько живой раздел.

С выпуском 1.0 включается следующее:

  • текущее дерево копируется в /v0/, помечается баннером «архив» и получает rel="canonical" на актуальный аналог;
  • страницы архива, у которых актуальный аналог есть, уходят из индекса;
  • в шапке документации появляется переключатель версий рядом с версией пакета.

Версия в шапке читается из манифеста ядра при сборке и руками не пишется — это правило действует уже сейчас, до всякого 1.0.

Чего библиотека пока не обещает

Раздел существует потому, что молчание здесь читается как обещание. Ни одного из перечисленного сегодня нет:

  • срок жизни минора. Сколько 0.41 будет получать исправления после выхода 0.42 — не объявлено;
  • период депрекации. Подпуть может исчезнуть в том же выпуске, в котором появилась замена: так и вышло с GrCodeBlock;
  • бэкпорт исправлений безопасности в предыдущие миноры;
  • метаданные since и deprecated у компонентов. Страница компонента не говорит, в какой версии появился проп: этих данных в библиотеке пока нет, и портал не станет их выдумывать.

Что делать вместо этого:

  1. Закрепляйте минор. На 0.x это происходит само — см. выноску выше, — но проверьте, что в package.json стоит диапазон, а не latest.
  2. Читайте журнал пакета, который поднимаете. Не общий: у каждого свой, и ломающее изменение описано там с диффом.
  3. Поднимайте по одному. Спутник и ядро связаны диапазоном, а не общей версией: обновление ядра не требует обновления спутников, пока диапазон сходится.

Где смотреть изменения

Журналы изменений на портале собраны из журналов пакетов в репозитории библиотеки — по одному на пакет, с диффами и объяснениями. Это тот же текст, что в репозитории: портал его не пересказывает.

Последняя ревизия: 2026-09-02