Operations › Versions and deprecations
Docs/Operations/Versions and deprecations

Versions and deprecations

What you'll learn

What a version number promises, which parts of Easy CMS the promise covers, how old names are retired, and how long releases get security fixes.

Before this page: Upgrading.

Easy CMS follows semantic versioning. Every @easy-cms/* package, easy-cms and create-easy-cms are released together with the same version number, so upgrade them together.

What a version number promises

  • Before 1.0 (0.x): a minor release (0.60 → 0.61) could change APIs, config or REST answers; the changelog and the upgrade guide say what and how to update. Patch releases (0.60.0 → 0.60.1) only fix bugs.
  • From 1.0: breaking changes only in a major release (1.x → 2.0), with an upgrade guide. Minor releases add features; patch releases fix bugs.

What the promise covers

CoveredNot covered
@easy-cms/core and @easy-cms/core/plugin: their exports and types@easy-cms/core/internal and @easy-cms/drizzle, which the packages share
Config options you set, and the documented shape of cms.configDefaults filled into cms.config, which may grow
The Local APIcms.db: the adapter beneath access rules and hooks
REST routes and their answers, error codes<api>/admin/ui/*, which only the admin uses
CLI commands and their optionsThe admin's look and wording
Your data: upgrades migrate it forwardMembers marked @internal, left out of the published types
Adapters and their optionsExperimental packages

Each change is tested against databases made by older releases (from 0.10) and the latest one, on SQLite and Postgres: they are migrated with easy-cms migrate and their documents, versions and files read back.

A change that fixes a security problem may break something the promise covers when there is no other way; the changelog says so.

Experimental packages

@easy-cms/plugin-ecommerce, @easy-cms/plugin-graphql and @easy-cms/plugin-mcp share the version number but not the promise: their options, endpoints and data may change in a minor release, after 1.0 too. Their pages and READMEs say so at the top.

A package leaves this list, in a minor release, once all of these hold:

  1. Tested against the real thing: a real service or client (Stripe, a GraphQL client and codegen, an MCP client such as Claude Desktop), with the result written down in an issue.
  2. Settled: no breaking change in 3 minor releases in a row.
  3. Covered: CI tests every option the docs list.

The changelog and the upgrade guide say when a package leaves; from then on the promise above covers it. The roadmap lists the work still open for each.

Deprecations

When a name changes in a minor release, the old one keeps working until the next major and logs a warning once per process, with a code:

(node:4242) [EASY_CMS_DEP001] DeprecationWarning: POST <api>/users/login is now <api>/auth/login; the old path works through 1.x.

They are Node.js deprecation warnings, so Node's flags apply:

  • node --no-deprecation hides them.
  • node --throw-deprecation turns them into errors: useful in CI to find what still uses old names.
  • node --trace-deprecation shows where each came from.

Set these with NODE_OPTIONS, e.g. NODE_OPTIONS=--throw-deprecation npm test.

Config options are the exception before 1.0: a renamed option stops startup with its new name instead (admin.siteUrl: is now `siteURL` ), since a config is quick to fix and an ignored option would quietly do nothing.

Current deprecations

CodeOldNewRemoved
EASY_CMS_DEP001<api>/users/<action> (login, me, init, …)<api>/auth/<action>2.0
EASY_CMS_DEP002POST <api>/globals/<slug>PATCH2.0
EASY_CMS_DEP003?fallback-locale=?fallbackLocale=2.0
EASY_CMS_DEP004easy-cms create-admineasy-cms admin:create2.0
EASY_CMS_DEP005easy-cms run-scheduledeasy-cms jobs:run2.0
EASY_CMS_DEP006a field named constructor or prototypeanother name2.0

In your plugins

warnDeprecated(code, message) from @easy-cms/core (or @easy-cms/core/plugin) does the same for your own names. Use codes of your own:

import { warnDeprecated } from '@easy-cms/core/plugin'
 
export function acmePlugin(options: { apiKey?: string; key?: string }) {
  if (options.key !== undefined)
    warnDeprecated('ACME_DEP001', '`acmePlugin({ key })` is now `apiKey`; `key` goes in 2.0.')
  const apiKey = options.apiKey ?? options.key
  // …
}

Security fixes

Security fixes go to the latest minor release. From 1.0, the previous major gets them too for six months after the next major is out. Report a vulnerability as the security policy says.

Next steps