What this page is for
A version number is a promise about breakage. This page says exactly which promise BPMN Kit
makes, so you can decide what to write in your package.json and know what an upgrade can
do to you.
Every package follows Semantic Versioning 2.0.0. The rest of this page is the part semver leaves open: what counts as the API in a toolkit that also emits XML files, writes state to disk and speaks HTTP.
What is covered today
The promises below take effect for a given package when it reaches 1.0.0. Twelve packages do — they are listed at the end of this page. A package still on 0.x is not covered by them, even though other packages in the workspace are: under semver, 0.x makes no compatibility promise at all. Releases of those have been additive in practice, but in practice is not a contract, so pin an exact version if one of them matters to you.
Versions are per package, not per repo
Packages version independently. @bpmnkit/core reaching 2.0.0 does not make
@bpmnkit/canvas 2.0.0, and the two can sit many majors apart.
Sibling dependencies are declared as carets, so @bpmnkit/plugins depending on
^1.2.0 of @bpmnkit/core resolves to one shared copy alongside your own ^1.4.0.
Two copies of @bpmnkit/core in one tree is not a duplicate of one copy — class identity,
instanceof and module-level registries all stop matching across the seam — so keep BPMN Kit
packages within one major of each other.
Releases are cut by Changesets. Every change that
reaches npm has a changeset naming its packages and its bump, and lands in that package’s
CHANGELOG.md.
What counts as public API
The public API of a package is what its exports entry points export, minus anything
marked @internal. Nothing else.
import { Bpmn } from "@bpmnkit/core" // ✅ API
import { minimap } from "@bpmnkit/plugins/minimap" // ✅ API — a declared subpath
These are not API, and may change in any release:
| Not API | Why |
|---|---|
Deep paths into dist/ | An implementation layout, not an entry point |
Members marked /** @internal */ | Reachable from the .d.ts because TypeScript has no other way to say “not yours” — ProcessBuilder carries several |
| Anything reachable only by structural inference | If you cannot import it by name from an entry point, it is not named in the contract |
src/ in the repo | The published package is the artifact; the repository is not |
Each package’s entry points are listed in its exports map. @bpmnkit/plugins is the one to
watch: it has no root export, only 34 subpaths, one per plugin.
For a package whose product is a command, exports says nothing — @bpmnkit/cli has none
at all. Its public API is instead its documented commands: the command and flag names, the
meaning of its exit codes, and the shape of any --format json output. Prose written to a
terminal for a human to read is not API, and neither is the exact wording of an error.
For a package that renders UI, the rendered DOM and its class names are not API either. Style through the documented CSS custom properties; a panel’s internal markup can change in a minor.
What is a breaking change
Runtime behaviour
Breaking: removing or renaming an export or an entry point; removing a function parameter or making an optional one required; throwing where a value used to be returned; changing a documented default.
Types
Type-level breakage is real breakage: a build that no longer compiles is a broken build. The direction matters, and it is the opposite for things we hand you and things you hand us.
| Change | Verdict |
|---|---|
| Adding an export, or a new entry point | minor |
| Adding an optional property to an options object | minor |
| Adding a member to a union we accept | minor |
| Adding a member to a union we return | major — your exhaustive switch stops compiling |
| Adding a required property to anything you construct | major |
| Making a returned property optional | major — you now have to narrow it |
| Narrowing a return type | major |
| Widening a parameter type | minor |
| Renaming an exported type | major, even when the shape is identical |
Generated BPMN, DMN and Form documents
This is the promise that matters most here, and the one a general semver policy has nothing to say about. The rule is:
A change is breaking if it moves
semanticHashfor the same input. A change to the bytes alone is not.
semanticHash is BPMN Kit’s canonical, presentation-free projection of a model, and it is
exported for exactly this purpose. Verified against the current build:
| Property | Holds |
|---|---|
| The same input rebuilt produces the same hash | ✅ |
applyAutoLayout does not move it | ✅ |
| Renaming an element moves it | ✅ |
| Changing an element id moves it | ✅ |
So, concretely:
- Major — different element ids, a changed document structure, a different default attribute on an emitted element, a changed FEEL expression. Anyone diffing generated files in review, or deploying them by id, sees these.
- Minor or patch — different layout coordinates, different attribute order, different whitespace, a nicer waypoint route. The picture moved; the model did not.
For the avoidance of doubt about precedent: @bpmnkit/core 0.4.0 derived element ids from the
model instead of generating them randomly. That moved semanticHash for every document, and
shipped as a minor. Under this policy it is a major, and 0.x is the only reason it was not.
Formats outside the package
These are contracts even though no TypeScript signature describes them, and the same rule applies — a change that makes an existing file, store or caller stop working is major:
- The
.bpmn.tests.jsonsidecar read bycasen testand the runner’s Tests tab. - Profile storage on disk —
~/.config/casenon Linux,~/Library/Application Support/casenon macOS,%APPDATA%\casenon Windows. A format change must migrate existing profiles, not invalidate them. - The
@bpmnkit/proxyHTTP surface, for the routes the documentation names. - Element template validation — a template that validates today does not start failing in a minor.
What is not a breaking change
- Adding a feature, an export, an entry point, or an optional parameter.
- Fixing a bug so behaviour matches its documentation. If you relied on the bug, this can still move under you; it is a patch, and the changelog will say what changed.
- Performance, internal structure, dependency versions inside a compatible range.
- Anything about layout, formatting or diagram interchange that leaves
semanticHashalone. - Documentation, READMEs, or the contents of
@bpmnkit/docspack.
Runtime support
The supported runtimes are listed under Runtime Requirements: Node.js 20 LTS and newer, Deno 1.40+, Bun 1.0+, and browsers supporting ES2022. Every package is ESM-only — there is no CommonJS build, and there will not be one.
Raising the floor follows the runtime’s own lifecycle:
- Dropping a Node.js major that is still in LTS is a major.
- Dropping one that has reached end of life is a minor, called out in the release notes.
- Raising the browser baseline past ES2022 is a major.
Deprecation
Nothing that is public API disappears without warning.
- It is marked
@deprecatedin the type declarations, naming what to use instead. Your editor and your build show it; nothing breaks. - It keeps working for at least one minor release, and is listed in the changelog entry that deprecated it.
- It is removed only in a major, and the major’s release notes list every removal.
An alias kept purely for compatibility is documented as such — ProcessBuilder’s strict
option is the existing example, a deprecated alias for explicitJoins.
Support window
Fixes land on the latest minor of the current major. When a new major ships, the previous major gets security fixes for six months; other fixes require an upgrade.
Security issues should be reported through GitHub rather than in a public pull request.
Which packages this covers
A package is covered by this page once it is at 1.0.0 or above, and not before. The distinction is deliberate: several packages are published, useful, and not yet ready to freeze an API — shipping them as 1.0 to make the list tidy would be a promise the project could not keep. Joining later costs nothing, because going from 0.x to 1.0 breaks no one, so the bar is applied strictly rather than generously.
Three conditions, all of which must hold:
- A test suite that would catch its own breakage.
- A documentation page on this site.
- An API worth defending for a year.
Twelve packages meet them today and carry the promise:
@bpmnkit/core | @bpmnkit/feel |
@bpmnkit/canvas | @bpmnkit/editor |
@bpmnkit/engine | @bpmnkit/plugins |
@bpmnkit/api | @bpmnkit/ascii |
@bpmnkit/connectors | @bpmnkit/connector-gen |
@bpmnkit/docspack | @bpmnkit/cli |
The other fourteen published packages stay on 0.x on purpose, and make no promise. Most are short of the first two conditions; the rest are worked examples, scaffolders, or generated builds with no API of their own to freeze.
The membership is not only prose. It lives in STABLE in
scripts/published-packages.mjs,
and the repo’s own checks enforce both directions of it: nothing on the list may lack tests or
a documentation page, and nothing at 1.0.0 or above may be missing from the list. A major
version cannot arrive by accident.
Whatever this page says, a package’s current version on npm is the authoritative answer.