Semantic versioning, and the one rule a script enforces
ngwr follows semver: a breaking change is a major, a new feature is a minor, a fix is a patch. The half of that worth writing down is the half that is checked rather than intended — scripts/release-prepare.ts refuses --bump=minor and --bump=patch outright when any commit since the last release tag carries a ! type or a BREAKING CHANGE: footer. It prints every offending subject and exits non-zero, and since a release is cut only by running the Release PR workflow — whose one input is that bump — the refusal IS the release: no version is written, no changelog is generated, no pull request is opened.
# What a release actually runs. --bump is the workflow's one input,
# and this is the step that refuses it.
pnpm release:prepare --bump=minor
# Illustrative output, with one breaking commit on the branch:
#
# Refusing --bump=minor: 1 breaking commit(s) since v14.0.0.
# feat(select)!: clearable is opt-in
# A `!` type or a `BREAKING CHANGE:` footer requires --bump=major.
#
# Exit status 1. No version written, no changelog, no release PR.It exists because the opposite shipped. v12.2.0 was a minor that carried a BREAKING CHANGES heading in its own changelog — select: clearable is opt-in — and an app on ^12.1.0 received it on the next install with nothing said. The bump was a value someone picked from a dropdown, and nothing connected it to the commits it was about to publish. That connection is now a step in the release, which is the only place it can live: a review can miss it, a convention can be forgotten, and a changelog that contradicts its own version number is written after the decision has already been made.
What the guard reads, and therefore what it cannot see.
Two smaller rules complete it. rc is exempt, and has to be: a release candidate is where a major is staged, and it publishes to the next dist-tag rather than to latest, so nobody receives it by upgrading. And “a patch is only fixes” is a convention, not a gate — 10.2.1 shipped a Features section. Nothing in that patch broke anything, which is why the guard is aimed where it is; but if you want to know exactly what a version contained, the changelog for it is generated from the commits rather than written, so it tells you.
What a major is allowed to break
Three surfaces, not one, and the two that are not TypeScript are the ones people are surprised by. Components ship ViewEncapsulation.None, so their .wr-* BEM classes are reachable from your stylesheet and are treated as API; the --wr-* custom properties are API for the same reason. All three change only in a major, and the middle column below is the part to read.
Surface | Covered | How you check one |
|---|---|---|
| Anything with a docs page | Every component, directive, pipe, service, util and validator under /reference, and every provideWr*() function. Their API tables are compared against the library on every pull request — names, types and defaults alike — so most drift between a page and the source is a red build rather than something you find at runtime. | |
| .wr-* class names | Components ship ViewEncapsulation.None, so the BEM classes are styleable and are treated as API. They are also what the test harnesses read. Inspect the element — what you see is what a major has to keep. | |
| --wr-* custom properties | The token layer, plus the per-component hooks listed in the CSS variables section of each component page. Both halves are gated: check:tokens fails a token in the theme layer that nothing paints with, and check:css-vars regenerates every per-component list from the stylesheets, so the hooks a page names are the hooks the CSS reads. | |
| Marked @internal | The marker survives into the shipped types, so it is greppable: grep -rn "@internal" node_modules/ngwr/types. Injection tokens a component uses to talk to its own parent live here — WR_SELECT, WR_TABS, WR_CHECKBOX_GROUP, WR_RADIO_GROUP, WR_COLLAPSE_GROUP, WR_BUTTON_GROUP, WR_CAROUSEL, WR_ICONS, WR_I18N_CONFIG, WR_OVERLAY, WR_OVERLAY_CONTAINER. Provide the documented provideWr*() instead. | |
| Exported, undocumented, unmarked | A real gap rather than a category: wrAppendOverlayClose, wrMirrorOffsets, wrPresentAsSheet and WrOutsideClick from ngwr/overlay, useConfigValue, useFormFieldAria, squirclePath, wrContrastFor, wrIntentTokens, isSafeCssValue, and a handful of WR_* context tokens such as WR_STEPPER. They import, they work, and nothing on this site describes what they promise. Treat them as unsupported until a page exists — and open an issue naming the one you want, because that is what turns a hole into a decision. |
The last row is the honest one and it is worth stating plainly rather than leaving to a reader with a .d.ts open: being exported is not the same as being supported. An entry point's public-api.ts exports a few helpers that exist because a component needed them, and a symbol like that carries no promise until something describes what it promises. The machine-readable half of this is the @internal marker, which survives into the published types — so the question “may I use this?” has a grep-able answer for one category and, for now, a judgement call for the other.
# Everything on this page is a fact about the repository. Read it there.
cat node_modules/ngwr/package.json | jq .peerDependencies # the ranges
grep -rn "@internal" node_modules/ngwr/types # what is not API
npm view ngwr time # every release, dated
npm view ngwr dist-tags # latest, and any rc on `next`How long a line keeps getting fixes
Two lines at a time: the current major in full, the one before it for mechanical security fixes only. The table below is the same one SECURITY.md carries, and neither copy is maintained by hand — release:prepare rewrites it from the version being cut, after the hand-maintained one spent two majors naming 10.x as current against a shipped 12.2.0.
Line | Support | What that means |
|---|---|---|
14.x | Every security fix, and every other fix. This is the line releases are cut from. | |
13.x | Security fixes only, and only where the fix is mechanical — no new inputs, no narrowed types, nothing that changes what already-accepted input renders. Where the only correct fix is not mechanical the advisory says so and the answer is to upgrade. This row ends when v15 ships. | |
< 13.0 | Unsupported. Report against any version you can reproduce on — working out which majors are affected is triage’s job, not yours — but the fix will land on a supported line. |
Read the middle row against the cadence, not against a calendar.
Reporting is private: GitHub's private vulnerability reporting or email, never a public issue. The targets — 72 hours to acknowledge, 7 days to triage, 30 days to a fix or workaround for high and critical — are in SECURITY.md, with the scope: the published package and its bundled assets, not this site and not the dependencies, which go upstream.
The Angular range — a real floor and an open ceiling
Every Angular peer is declared >=22.0.0, and the two halves of that range mean very different things. The floor is real and it is enforced by the compiled output, not by the range — though by a minority of it: of 654 partially-compiled declarations, 25 record minVersion: 22.0.0 (the service declarations, spread across 22 files), and one unreadable declaration is enough to fail the build. The ceiling is absent, and absent is not a promise. It means your package manager will not stop you from installing ngwr next to an Angular that did not exist when the release was cut — nothing more.
Below the floor, the install succeeds and the build is what fails. npm, pnpm and yarn all treat an unsatisfied peer as a warning, so adding ngwr to an Angular 21 project prints “Issues with peer dependencies found” and exits 0. The failure surfaces later, in the linker, as Unsupported enum value for [object Object] pointing into a fesm2022 bundle — a message that never mentions a version. Read the peer warning at install time; it is the only place the real reason is printed.
Above the ceiling, nobody knows yet, and the honest answer is a procedure rather than a range. When a new Angular major lands, ngwr has not been built or tested against it — the gates run on the Angular the repository is pinned to. So: upgrade Angular on a branch, run your own suite, and expect a supporting ngwr release rather than a widened range, because the fix for a framework break is code. Nothing about the open range makes an untested combination work, and nothing about it will warn you that you are in one. The gates page lists what runs and what each gate cannot see; there is one maintainer and no dated commitment, which is worth weighing before you plan an upgrade around a release that has not been announced.
TypeScript and Node are decided by your Angular, not by ngwr. The package declares no engines field and no TypeScript peer at all: it ships pre-compiled bundles and .d.ts files, so the compiler version that matters is the one Angular's own peer range names — typescript >=6.0 <6.1 for Angular 22 — and the Node version is whatever your Angular CLI accepts. That is a deliberate absence rather than a missing field: a second range here could only ever contradict the first one.
The cadence, stated rather than inferred
It is fast, and reading it out of a changelog gives a worse impression than reading it here. Eight majors shipped between 2026-06-12 (v7.0.0) and 2026-09-04 (v14.0.0) — under a fortnight apart on average. That was a library settling its vocabulary, its entry-point layout and its palette before it had an install base to protect: two of those majors broke nothing but painted colour, and the largest single category across the rest is renames — closeable to closable, totalItems to total, compact / normal to sm / md. The table on Quality has what each one actually cost. Useful to know, and not a promise about next quarter.
Three things follow, and they are what a team can actually act on. One: a caret range on a major is a safe range now, which it demonstrably was not on v12 — that is what the guard at the top of this page buys, and it is the whole reason it is worth stating publicly instead of leaving you to infer a policy from a changelog. Two: budget for reading a migration on each major, not for a rewrite — the migration guide covers every break back to v6, and ng update ngwr@14 rewrites the half a codemod can honestly repair and reports the half it cannot. Three: check rather than trust this paragraph, because a page written today describes the releases that existed today.
# Pin the major and let the minors in. That is a safe range now — the guard
# above is what makes it one — and it was not on v12, where 12.2.0 was a minor
# carrying a breaking change.
npm pkg set dependencies.ngwr="^14.0.0"
# Then read what a range would actually have picked up, before you widen it.
npm view ngwr versions --jsonWhat none of this buys: there is no commercial support, no SLA and no contract — Quality says so in the same words. The library is MIT and every gate runs in the public repository, so a fork is a real option; that is the fallback the licence gives you, not a plan anyone is offering to run for you.
See also
- GuideQualityWhat gates a release, what the gates cannot see, and what the project does not buy you.
- GuideMigration guideEvery breaking change back to v6, and which half of each major the codemod rewrites.
- GuideInstallationThe peer list in full, what ships in the tarball, and what a per-component style entry costs.