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

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

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

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

## С 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: вынести что-то из
замороженного ядра стоит мажора, поэтому переезды такого рода делались **до**
заморозки, а не после.

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

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

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

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

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

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

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

| Пакет | Ядро `@feugene/granularity` | Конвейер `@feugene/granum` | Vue |
|---|---|---|---|
| `@feugene/granularity-charts` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-chrono` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-code` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-dashboard` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-datasource` | — | — | `^3.5.0` |
| `@feugene/granularity-devtools` | `>=1.0.0 <2.0.0` | — | `^3.5.0` |
| `@feugene/granularity-editor` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-forms-schema` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-media` | `>=1.0.0 <2.0.0` | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/granularity-test-kit` | — | `>=2.0.0 <3.0.0` | `^3.5.0` |
| `@feugene/unplugin-granularity` | `>=1.0.0 <2.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,
а не импортирует компоненты; `unplugin-granularity` разрешает компоненты, не
порождая ни одного правила.

Семейство пришло к 1.0 вместе, и диапазоны об этом и говорят: нижняя граница
`>=1.0.0`, верхняя — `<2.0.0`. Раньше граница была `<1.0.0` и означала «дальше
границы обещаний нет». 1.0 контракт переопределила, и диапазоны переписаны
вместе с ним — ровно как и было обещано.

Колонка конвейера живёт своим мажором: с выпуска 1.1.0 спутники собраны granum 2
и объявляют `>=2.0.0 <3.0.0` — granum 1 рецепты темизации и запрет корневого
импорта, на которые опирается документация, не выполняет.

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

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

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

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

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

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

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

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

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

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

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

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

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

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