Versioning

  • Upgrades and support
  • 7 min
  • 1 snippet

What 3.x promises, how the packages of the family are tied together by versions, and what the library does not promise yet.

Reviewed 2026-10-27

On this page

An adopter’s question is not “what is the current version” but “how long will this version live”. The honest answer today: no support window is declared. Below is what exists instead, which of it is checkable, and what to do while there is no promise.

Since 1.0

The library numbers its releases by Semantic Versioning, and since 1.0 the public surface — the components, their subpaths, the tokens and the registries — breaks only in a major. On 0.x it was different: a minor release could break compatibility, and did. Version 0.41.0 moved GrCodeBlock out of the core into @feugene/granularity-code, and the @feugene/granularity/components/GrCodeBlock subpath disappeared:

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

The reason is named in the changelog and it is about 1.0: taking something out of a frozen core costs a major, so moves of that kind were made before the freeze rather than after it. The first major under that rule is 2.0: themes and modes changed the build config and the switching API. The second is 3.0: component token values moved into themes, and themes in the granum config became required. Both moves are covered in migration.

From major 1 on, ^ and ~ differ. ^3.0.0 expands to >=3.0.0 <4.0.0 and accepts new minors; ~3.0.0 expands to >=3.0.0 <3.1.0. On a zero major — @feugene/granularity-datasource 0.1.x and the @feugene/fint-i18n peer — ^ still means the same as ~.

Every package is versioned on its own

The family has no shared version, and that is not an oversight. The core and the companions run on different cycles: the core ships patch after patch, a companion may sit on its version for weeks, @feugene/granularity-datasource is still on 0.x, and the token package @feugene/granularity-tokens started its own numbering at 1.0 together with core 2.0 and moved to 2.0 together with core 3.0. An extra companion release for the sake of a core release would mean an update with not a single change behind it.

Three things follow, and all three are visible in the repository:

  1. Every package has its own changelog. There is deliberately no shared changelog at the root — the changelogs on this portal are assembled from them.
  2. Every package has its own tag. The core is released with a vX.Y.Z tag, a companion with <directory-name>-vX.Y.Z, for instance granularity-charts-v2.0.0.
  3. Publishing happens from the tag. Pushing a tag triggers publication to npm with --provenance: the package on npm has traceable provenance, and your scanner verifies it rather than us. The GitHub Packages mirror is published under the @efureev scope and without provenance.

The compatibility matrix

A companion declares which version of the core and of the build pipeline it works with — not in words but as a range in peerDependencies. Your package manager reads it by itself and speaks up about an incompatible pair: npm with a warning, pnpm and Yarn Berry with strict settings by refusing to install. Here is the same range in readable form:

PackageCore @feugene/granularityPipeline @feugene/granumVue
@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——

The table covers the core, the pipeline and Vue. The other peer dependencies — for instance the optional @feugene/fint-i18n >=0.9.0 <1.0.0 and @feugene/unplugin-granularity, @feugene/granularity-chrono for forms-schema, the editor and schema libraries of their companions — are in the package’s package.json. For test-kit both the pipeline and Vue are optional.

A dash means the package does not declare that dependency at all: granularity-datasource knows about neither the core nor the pipeline — it is about data rather than markup — test-kit works with any core, because it checks CSS and the DOM rather than importing components, and granularity-tokens is the token dictionary and the themes — the core depends on it, not the other way round.

The companions with components keep their values in the theme — the colour, the rules and the face are decided by the theme rather than by the package code. That is why core 2.x does not suit them: the core brings the theme catalogue from 3.1 on, through the token package — charts, dashboard, forms-schema and media have the core range >=3.1.0 <4.0.0. chrono, code and editor draw the field with the shared frame sheet of the core and stand on >=3.5.0 <4.0.0. Core 2.x is left only to devtools (>=2.4.0), which reads the state of components rather than drawing them, and to unplugin-granularity: it resolves components without generating a single rule — >=1.0.0 <4.0.0.

The pipeline column moves on its own major: every package is built with granum 3, and the lower bound is the mechanism the package stands on: component values in the theme need granum 3.10, a theme section for a group of components — 3.11. They cannot be mixed with a companion built for core 1: the manifests of granum 2 and 3 are incompatible, and the config with the themes list the documentation relies on is understood by granum 3.8 and later only.

The table is typed by hand but verified against the manifests in the portal’s CI: a core or Vue range that has diverged from a companion’s package.json fails the run. The pipeline column is not verified by the gate yet. And the check runs against the tag the library submodule is pinned to — not against what sits in npm this very second. The source of truth is the peerDependencies of the installed package.

Environment requirements

WhatVersionWhere from
Node>=22engines in every package of the family
Vue^3.5.0a peer dependency
Module formatESM only"type": "module", there is no CommonJS build

Those three lines are part of compatibility too, and they change by the same rules: raising the lower bound of Node is a breaking change, and since 1.0 it arrives only in a major.

Documentation for one version

The documentation describes one version — the one the portal is pinned to, currently the 3.x line. There is no archive of earlier versions and no switcher: the 0.x, 1.x and 2.x pages are not kept on the portal, and moving off them is covered in migration.

The version in the header is read from the installed core’s manifest at build time and is never typed by hand.

What the library does not promise yet

This section exists because silence here reads as a promise. Not one of the following exists today:

  • the lifetime of a version. Whether the 2.x line receives fixes after 3.0, and how long a minor lives after the next one, is not declared;
  • a deprecation period. A subpath may disappear in the same release in which its replacement appears: that is what happened with GrCodeBlock;
  • backports of security fixes into previous minors;
  • since and deprecated metadata on components. A component page does not say which version a prop appeared in: the library does not carry that data yet, and the portal will not invent it.

What to do instead:

  1. Pin the minor if you need predictability: ~3.2.0 rather than ^3.2.0 — see the callout above — and check that package.json holds a range rather than latest.
  2. Read the changelog of the package you are raising. Not a shared one: every package has its own, and a breaking change is described there with a diff.
  3. Raise them one at a time. A companion and the core are tied by a range rather than by a shared version: updating the core does not require updating the companions as long as the range still holds.

Where to see the changes

The changelogs on this portal are assembled from the changelogs of the packages in the library repository — one per package, with diffs and explanations. It is the same text as in the repository: the portal does not retell it.