Quality And CI
The project reports package quality in three places:
- GitHub step summaries for quick CI scanning.
- Sticky pull request comments for coverage, test performance, and cognitive complexity.
- Uploaded artifacts for raw coverage, test, and complexity data.
Coverage
Coverage uses c8 over the package source:
npm run test:cov
The gate requires 100% statements, branches, functions, and lines. The PR coverage comment compares the pull request against cached base-branch coverage when base data is available.
CI runs package coverage on Node.js 22 and Node.js 24. The Node.js 22 quality job owns the PR coverage, performance, and cognitive-complexity comments so those reports stay single-source.
Performance
Tests use node:test. npm run test:cov runs a small reporting wrapper that
writes test-results.json with suite and individual test durations parsed from
the runner output.
The PR performance comment shows passed/failed/skipped counts, suite count, total test step duration, test execution duration, and the slowest suites and tests. When base data exists, each duration includes a diff against the base branch.
These numbers are lightweight regression signals, not a synthetic benchmark. Use them to notice suspicious changes, then inspect the related test or sample.
| Signal | Where it appears | What it means |
|---|---|---|
| Package test step duration | PR performance comment and package quality summary | End-to-end package coverage step |
| Test execution duration | PR performance comment | Time reported by node:test for package tests |
| Slowest suites/tests | PR performance comment | Review hints for tests that changed or became slow |
| Sample validation duration | Sample validation job summary | Coarse duration for showcase plus focused samples |
Cognitive Complexity
Cognitive complexity uses SonarJS through ESLint:
npm run complexity:check
npm run complexity:report
complexity:check enforces the threshold of 15 per source function.
complexity:report writes complexity/cognitive-complexity-summary.json with
totals, per-file aggregates, and the most complex functions. The PR comment
treats complexity as a review signal; the hard gate is the ESLint threshold.
Document Validation
Every sample generates an AsyncAPI 3.0 document and validates it with the
official @asyncapi/parser. Parser errors are treated as build failures. This is
the project's definition of a valid spec — the package never claims output is
valid without passing the parser.
Docs Site
The Docusaurus site is built in CI to catch broken links and build regressions:
npm run ci:docs
onBrokenLinks is set to throw, so a dangling internal link fails the build.
Release And Security
Release validation checks README/docs links, README version literals, sample version sync, the package tarball, and that every workspace resolves the hoisted NestJS the lockfile declares:
npm run release:check
Every sample pins exactly the @nestjs/* versions the root resolves, so a copy
nested under sample/*/node_modules means the lockfile drifted from the sample
manifests and the samples silently exercise a different NestJS than the package
suite. release:check:nestjs-resolution fails on any nested copy, and on any
major other than the one the root @nestjs/core devDependency declares.
For the publish checklist and version-sync rules, see Release Guide.
Supply-chain auditing checks high-severity risk in both the package workspace and the docs site:
npm run security:audit
Run the complete local gate with:
npm run ci
Samples
Samples are release blockers. GitHub Actions runs them in the dedicated
Sample validation job, and the local gate includes the same matrix:
npm run ci:sample
release:check also verifies every sample/*/package.json depends on the
current packages/asyncapi version and that npm workspace resolution agrees with
the lockfile.
NestJS Compatibility Matrix
The published peer range is @nestjs/common / @nestjs/core
^11.0.0 || ^12.0.0, plus the optional @nestjs/swagger peer at
^11.4.4 || ^12.0.0. The devDependencies and the lockfile stay on an 11.x in
the middle of that range — that is what npm ci and every job above test — and
the nestjs-compat job is a matrix with one leg per end, so both ends are
tested claims rather than assumptions. Each leg installs its end on top of the
lockfile, then re-runs the build, the workspace-wide typecheck, the suite, and
the sample matrix (the build runs first because the samples import
@nest-native/asyncapi through the workspace link, whose entry points live in
packages/asyncapi/dist).
| Leg | Installs | Why this version |
|---|---|---|
11 floor | framework 11.0.1, @nestjs/swagger@11.4.4, pinned exactly | The oldest graph the range can produce. This package imports @nestjs/* roots only and uses nothing added by a later 11.x, but every @nestjs/swagger@11.x peers on common/core ^11.0.1, so 11.0.1 is the oldest framework that installs next to any swagger 11 at all; swagger is pinned at the low end of the package's own optional peer range. Exact pins mean a downgrade that silently no-ops fails instead of passing as "still 11". |
12 | ^12.0.0 for both | The newest end; floats so a new 12.x patch is tested on the next run. |
The floor leg, by hand:
npm ci
npm install --no-save --workspaces --include-workspace-root \
@nestjs/common@11.0.1 @nestjs/core@11.0.1 \
@nestjs/platform-express@11.0.1 @nestjs/testing@11.0.1 \
@nestjs/microservices@11.0.1 @nestjs/swagger@11.4.4
node scripts/check-nestjs-resolution.mjs 11.0.1 @nestjs/swagger@11.4.4
npm run build --workspace @nest-native/asyncapi
npm run typecheck
npm test
npm run ci:sample
Before a leg runs anything, two gates prove the tree is the one it claims to
test, because npm makes it easy to end up with another one. --workspaces --include-workspace-root (not --workspace-root) is what puts the leg's
version in front of the samples: they pin @nestjs/* exactly, so with
--workspace-root alone npm satisfies each sample's 11 pin by nesting an 11
copy under it, and the sample matrix runs on 11 while the root reports
something else. And scripts/check-nestjs-resolution.mjs resolves the
framework packages from inside every workspace and requires exactly the leg's
version from the hoisted root copy — a nested copy fails even when its version
is right — then checks every peer range in the NestJS ecosystem (every
installed package at any depth that is @nestjs/* or peers on one, this
package's own published ranges included) against the tree the suite will run
on. That final-tree check is the gate because npm's own signal is not one: a
peer conflict npm can override produces npm warn ERESOLVE overriding peer dependency and exit 0, neither npm ls nor --strict-peer-deps reports it
afterwards, and the same warning appears for transitional states that end
coherent, so grepping the install log for it is a false-positive class rather
than a gate. The same script runs with no argument in release:check, against
the lockfile. Every @nestjs/* package any workspace declares goes in one install
command, because --no-save never persists the edges and a second
npm install reconciles the tree back to the lockfile.