Versioning
What 0.x means in practice, how the packages of the family are tied together by versions, and what the library does not promise yet.
Machine-translated, not yet reviewed. Read the original
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.
While it is 0.x
The library follows Semantic Versioning, and before 1.0
that means exactly one thing: a minor release may break compatibility. Not
“in theory” — it has already happened. 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 are made before the freeze rather than after it.
On a zero major, ^ and ~ mean the same thing. ^0.41.0 expands to
>=0.41.0 <0.42.0 — the same range as ~0.41.0. That is, the package manager
already pins the minor for you: an update to 0.42 takes a deliberate action
rather than arriving with an install.
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: @feugene/granularity-code lives on 0.1.x
while the core walks towards 0.42 — and an extra major in a companion 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-v0.11.0. - Publishing happens from the tag. Pushing a tag triggers publication to
npm with
--provenanceand to GitHub Packages: the released artefact has traceable provenance, and your scanner verifies it rather than us.
The compatibility matrix
A companion declares which version of the core and of the preset it works with —
not in words but as a range in peerDependencies. Your package manager reads it
by itself and refuses to assemble an incompatible pair. Here is the same range in
readable form:
| Package | Core @feugene/granularity | Preset @feugene/unocss-preset-granular | Vue |
|---|---|---|---|
@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 | — | — |
A dash means the package does not declare that dependency at all:
granularity-datasource knows about neither the core nor the preset — it is
about data rather than markup — and test-kit works with any core, because it
checks CSS and the DOM rather than importing components.
The upper bound is one and the same everywhere — <1.0.0. It says not “we are
compatible with everything below one” but “beyond that there are no promises”:
1.0 will redefine the contract, and the ranges will be rewritten with it.
The table is not typed by hand but verified against the manifests on every
build: a range that has diverged from a companion’s package.json fails the
portal’s build. It is verified, though, 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 before 1.0 it may arrive in a minor.
What will happen at 1.0
Before 1.0 there is no versioning of the documentation: there is only
/docs, without prefixes. Premature versioning doubles the work and confuses the
search — an archive with no readers costs exactly as much as a living section.
With the 1.0 release the following turns on:
- the current tree is copied to
/v0/, marked with an “archive” banner and given arel="canonical"to its current counterpart; - archive pages that do have a current counterpart leave the index;
- a version switcher appears in the documentation header next to the package version.
The version in the header is read from the core manifest at build time and is never typed by hand — that rule is already in force, well before any 1.0.
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 minor. How long 0.41 will receive fixes after 0.42 comes out 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. On 0.x that happens by itself — see the callout above — but
check that
package.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.