Support policy
| Runtime | Supported line |
|---|---|
| Node.js | >=22 (>=22.12 when paired with NestJS 12 — its own require(esm) floor) |
NestJS (@nest-native/cache peer) | ^10.0.0 || ^11.0.0 || ^12.0.0 |
drizzle-orm (@stalefree/core optional peer) | ^0.44.0 || ^0.45.0 |
@stalefree/core has no runtime dependencies and no framework peer; only the
Drizzle-backed L2 stores (@stalefree/core/{sqlite,postgres,mysql}) need
drizzle-orm, which is why it is an optional peer. The Postgres bus takes the
client you construct and never imports pg itself.
The Node line is the packages' own engines. NestJS 12 raises the effective
floor to >=22.12 when you pair the adapter with it: 12 ships ESM-only and
relies on require(esm), which upstream states as Node 20.19+ / 22.12+ (Node
20 is outside this line). The @nestjs/* packages' engines field says only
>= 20, so npm will not warn you on Node 22.0–22.11 — check the runtime
yourself. With NestJS 10 or 11 the adapter runs on any Node 22.
How a major is adopted
A new peer major is widened into the range, never swapped in:
- the published
peerDependenciesrange widens to include the new major; - the devDependencies — and therefore the lockfile every default CI job installs — stay on the older major, so the default suite keeps testing that end;
- a dedicated CI leg installs the newer major with
--no-saveon top of that lockfile and runs the typecheck and the suites.
Both ends of the range are then tested claims. NestJS 12 (released
2026-08-27; ESM-only; Node >=22.12 on this support line, see above) is the
live example: the nestjs-latest-major job, on Node 22 (the newest 22.x,
above that floor), installs @nestjs/*@^12 in every workspace
(--no-save; a root-only install cannot swap a peer-linked set in place),
proves from inside the adapter workspace that @nestjs/core resolved to 12,
and re-runs the adapter typecheck and both test suites. A 10/11 matrix
typechecks the adapter at the older end. The bare-Express sample has no
@nestjs/* anywhere — it is the framework-neutrality proof — so it is not
part of that leg.
What NestJS 12 changed, and what it means here
- ESM-only, with an exports map. A deep import that names a directory under
@nestjs/*(for example@nestjs/common/interfaces) no longer resolves. The adapter imports only from the@nestjs/commonand@nestjs/testingroots, and the 12 leg fails on any directory import that ever creeps in. - Lifecycle hooks are ordered by module-hierarchy level. Two providers may
see the same hook in a different order than on 11. The adapter's only hook
is
onApplicationShutdown, which detaches the cache from the bus — a synchronous local unsubscribe (every shipped bus implements it as aSet.delete, a no-op once the bus is closed), so it does not care whether your bus or database closed before or after it. Nothing here depends on cross-provider hook order.
Dependabot
The @nestjs/* packages peer on each other, so a major that arrives as one
Dependabot PR per package cannot even install — npm ci fails with ERESOLVE
before a test runs. The peer group in .github/dependabot.yml therefore
groups majors as well as minors and patches, so the next major arrives as a
single PR whose CI result means something. That PR is input to the recipe
above, not a substitute for it.