Versioning
What 1.x promises, how the packages of the family are tied together by versions, and what the library does not promise yet.
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:
- 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.
On major 1, ^ and ~ differ. ^1.0.0 expands to >=1.0.0 <2.0.0 and accepts
new minors; ~1.0.0 expands to >=1.0.0 <1.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, and @feugene/granularity-datasource is still
on 0.x. 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:
- Every package has its own changelog. There is deliberately no shared changelog at the root — the changelogs on this portal are assembled from them.
- Every package has its own tag. The core is released with a
vX.Y.Ztag, a companion with<directory-name>-vX.Y.Z, for instancegranularity-charts-v1.0.4. - 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@efureevscope 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:
| Package | Core @feugene/granularity | Pipeline @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 | — | — |
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 unplugin-granularity
resolves components without generating a single rule.
The family reached 1.0 together, and the ranges say so: the lower bound is
>=1.0.0, the upper <2.0.0. Before that the bound was <1.0.0 and meant
“beyond that there are no promises” — 1.0 has redefined the contract, and the
ranges were rewritten with it, as promised.
The pipeline column moves on its own major: since the 1.1.0 release the companions
are built with granum 2 and declare >=2.0.0 <3.0.0 — granum 1 does not carry out the
theming recipes and the ban on root imports that the documentation relies on.
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
| What | Version | Where from |
|---|---|---|
| Node | >=22 | engines in every package of the family |
| Vue | ^3.5.0 | a peer dependency |
| Module format | ESM 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 1.x line. There is no /v0/ archive and no version switcher: the
0.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. How long the 1.x line will receive fixes after 2.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;
sinceanddeprecatedmetadata 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:
- Pin the minor if you need predictability:
~1.0.0rather than^1.0.0— see the callout above — and check thatpackage.jsonholds a range rather thanlatest. - 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.
- 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.