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

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

- Page: https://granularity.tech/ru/docs/versioning
- Last reviewed: 2026-10-27

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

## С 1.0

Библиотека нумерует выпуски по [Semantic Versioning](https://semver.org/), и с
1.0 публичная поверхность — компоненты, их подпути, токены и реестры —
ломается только мажором. На 0.x было иначе: минорный выпуск мог сломать
совместимость, и ломал. Версия 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: вынести что-то из
замороженного ядра стоит мажора, поэтому переезды такого рода делались **до**
заморозки, а не после. Первым мажором по этому правилу стала 2.0: темы и режимы
поменяли конфиг сборки и API переключения. Вторым — 3.0: значения токенов
компонентов переехали в темы, а `themes` в конфиге granum стал обязательным. Оба
переезда описаны в [миграции](/ru/docs/migration).

:::callout{type="info"}
С мажора 1 `^` и `~` различаются. `^3.0.0` разворачивается в `>=3.0.0 <4.0.0`
и принимает новые миноры, `~3.0.0` — в `>=3.0.0 <3.1.0`. На нулевом мажоре — у
`@feugene/granularity-datasource` 0.1.x и у peer `@feugene/fint-i18n` — `^`
по-прежнему значит то же, что `~`.
:::

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

Общей версии у семейства нет, и это не недосмотр. У ядра и спутников разный
цикл: ядро выпускает патч за патчем, спутник может неделями стоять на своей
версии, `@feugene/granularity-datasource` и вовсе ещё на 0.x, а пакет токенов
`@feugene/granularity-tokens` начал свою нумерацию с 1.0 вместе с ядром 2.0 и
перешёл на 2.0 вместе с ядром 3.0. Лишний выпуск
спутника ради выпуска ядра означал бы обновление, за которым не стоит ни одного
изменения.

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

:::steps
1. **У каждого пакета свой журнал изменений.** Общего журнала в корне нет
   намеренно — [журналы на портале](/ru/changelog) собраны из них.
2. **У каждого пакета свой тег.** Ядро выпускается тегом `vX.Y.Z`, спутник —
   `<имя-каталога>-vX.Y.Z`, например `granularity-charts-v2.0.0`.
3. **Публикация идёт из тега.** Пуш тега запускает публикацию в npm с
   `--provenance`: у пакета в npm есть прослеживаемое происхождение, и это
   проверяет ваш сканер, а не мы. Зеркало в GitHub Packages выходит под scope
   `@efureev` и без provenance.
:::

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

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

| Пакет | Ядро `@feugene/granularity` | Конвейер `@feugene/granum` | Vue |
|---|---|---|---|
| `@feugene/granularity-charts` | `>=3.1.0 <4.0.0` | `>=3.11.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-chrono` | `>=3.5.0 <4.0.0` | `>=3.10.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-code` | `>=3.5.0 <4.0.0` | `>=3.10.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-dashboard` | `>=3.1.0 <4.0.0` | `>=3.11.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-datasource` | — | — | `^3.5.0` |
| `@feugene/granularity-devtools` | `>=2.4.0 <4.0.0` | `>=3.8.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-editor` | `>=3.5.0 <4.0.0` | `>=3.10.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-forms-schema` | `>=3.1.0 <4.0.0` | `>=3.10.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-media` | `>=3.1.0 <4.0.0` | `>=3.10.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-test-kit` | — | `>=3.0.0 <4.0.0` | `^3.5.0` |
| `@feugene/granularity-tokens` | — | `>=3.10.0 <4.0.0` | — |
| `@feugene/unplugin-granularity` | `>=1.0.0 <4.0.0` | — | — |

В таблице — связь с ядром, конвейером и Vue. Остальные peer-зависимости —
например, опциональные `@feugene/fint-i18n` `>=0.9.0 <1.0.0` и
`@feugene/unplugin-granularity`, `@feugene/granularity-chrono` у `forms-schema`,
библиотеки редакторов и схем у своих спутников — смотрите в `package.json`
пакета. У `test-kit` конвейер и Vue опциональны.

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

Спутники с компонентами держат свои значения в теме — цвет, линии, шрифт решает тема,
а не код пакета. Поэтому ядро 2.x им не подходит: каталог тем ядро приносит с 3.1, через
пакет токенов, — у `charts`, `dashboard`, `forms-schema` и `media` диапазон ядра
`>=3.1.0 <4.0.0`. `chrono`, `code` и `editor` рисуют поле общим листом рамки ядра и
стоят на `>=3.5.0 <4.0.0`. Ядро 2.x осталось только у `devtools` (`>=2.4.0`), который
читает состояние компонентов, а не рисует их, и у `unplugin-granularity`: он разрешает
компоненты, не порождая ни одного правила, — `>=1.0.0 <4.0.0`.

Колонка конвейера живёт своим мажором: все пакеты собраны granum 3, а нижняя
граница — механизм, на котором пакет стоит: значения компонентов у темы — granum
3.10, раздел темы для группы компонентов — 3.11. Смешивать их со спутником,
собранным под ядро 1, нельзя: манифесты granum 2 и 3 несовместимы, а конфиг со
списком `themes`, на который опирается документация, понимает только granum 3.8 и
новее.

:::callout{type="warning"}
Таблица набрана руками, но сверяется с манифестами в CI портала: диапазон ядра
или Vue, разошедшийся с `package.json` спутника, роняет прогон. Колонку
конвейера гейт пока не сверяет. И сверка идёт с тем тегом, на который закреплён
подмодуль библиотеки, а не с тем, что лежит в npm сию секунду. Источник истины —
`peerDependencies` установленного пакета.
:::

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

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

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

## Документация одной версии

Документация описывает одну версию — ту, на которую закреплён портал; сейчас
это линия 3.x. Архива прежних версий и переключателя нет: страницы 0.x, 1.x и 2.x на
портале не сохранены, а переезд с них описан в [миграции](/ru/docs/migration).

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

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

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

- **срок жизни версии.** Получает ли линия 2.x исправления после выхода 3.0 и
  сколько живёт минор после следующего — не объявлено;
- **период депрекации.** Подпуть может исчезнуть в том же выпуске, в котором
  появилась замена: так и вышло с `GrCodeBlock`;
- **бэкпорт исправлений безопасности** в предыдущие миноры;
- **метаданные `since` и `deprecated` у компонентов.** Страница компонента не
  говорит, в какой версии появился проп: этих данных в библиотеке пока нет, и
  портал не станет их выдумывать.

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

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

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

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