Skip to main content

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.

SignalWhere it appearsWhat it means
Package test step durationPR performance comment and package quality summaryEnd-to-end package coverage step
Test execution durationPR performance commentTime reported by node:test for package tests
Slowest suites/testsPR performance commentReview hints for tests that changed or became slow
Sample validation durationSample validation job summaryCoarse 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).

LegInstallsWhy this version
11 floorframework 11.0.1, @nestjs/swagger@11.4.4, pinned exactlyThe 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 bothThe 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.