Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FRAMEWORK-API.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @lenne.tech/nest-server — Framework API Reference

> Auto-generated from source code on 2026-07-03 (v11.27.3)
> Auto-generated from source code on 2026-07-04 (v11.27.4)
> File: `FRAMEWORK-API.md` — compact, machine-readable API surface for Claude Code

## CoreModule.forRoot()
Expand Down
236 changes: 236 additions & 0 deletions migration-guides/11.27.3-to-11.27.4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Migration Guide: 11.27.3 → 11.27.4

## Overview

| Category | Details |
|----------|---------|
| **Breaking Changes** | None |
| **New Features** | Opt-in `CHECK_LOW_RESOURCE` e2e mode (keeps parallel test runs stable under load); `check.mjs` now labels signal-killed steps (`SIGTERM`/`SIGKILL`) instead of showing a bare exit code. |
| **Bugfixes** | Idle watchdog no longer false-kills output-buffering steps (build / typecheck / audit); a stray `SIGKILL` after PID reuse is prevented; an invalid `--idle-timeout` value now falls back to the default instead of silently disabling the watchdog. |
| **Migration Effort** | 0 minutes for npm consumers (`pnpm update` is enough). Optional: projects that copied `scripts/check.mjs` / `vitest-e2e.config.ts` from the starter can adopt the updated files. |

This is a **repo-internal tooling & test-infrastructure release**. It touches
**no framework source** (`src/` is unchanged) — only `scripts/check.mjs` and
`vitest-e2e.config.ts`. The npm package (`dist/`) is functionally identical to
11.27.3; consuming projects need no code or config changes.

---

## Quick Migration

No code changes required.

```bash
# Update package
pnpm add @lenne.tech/nest-server@11.27.4

# Verify build
pnpm run build

# Run tests
pnpm test
```

> These files ship in the git repository / starter, **not** in the npm package.
> An npm-mode consumer is unaffected. A project that copied `scripts/check.mjs`
> and/or `vitest-e2e.config.ts` from the starter (11.27.1+) can copy the updated
> versions to get the improvements below — see **Compatibility Notes**.

---

## What's New in 11.27.4

### 1. Opt-in low-resource e2e mode (`CHECK_LOW_RESOURCE`)

Running several full e2e suites at the same time on one machine (e.g. multiple
parallel `lt dev` / `lt ticket` environments, or a second terminal) can saturate
CPU and the shared MongoDB. Under that load individual requests exceed the 30s
`testTimeout` and auth queries fail — surfacing as intermittent `401`s or
per-file timeouts. Data isolation was never the cause (every run already gets a
unique database); the cause is **physical resource contention**.

`vitest-e2e.config.ts` now supports an **opt-in** throttle. It changes nothing
by default — solo and moderately parallel runs stay at full speed:

```bash
# Full speed (default) — nothing changes
pnpm test

# Opt in when you deliberately run MANY suites in parallel
CHECK_LOW_RESOURCE=1 pnpm test

# Pin the fork cap explicitly (otherwise ~1/3 of CPU cores)
CHECK_LOW_RESOURCE=1 CHECK_LOW_RESOURCE_FORKS=2 pnpm test
```

| `CHECK_LOW_RESOURCE` | Effect |
|----------------------|--------|
| unset / `0` / `false` (default) | No cap, no timeout change — full speed |
| `1` / `true` | `poolOptions.forks.maxForks` capped (`~cores/3`, min 2), `testTimeout` 30s → 60s, `hookTimeout` 120s → 240s |
| any value + `CHECK_LOW_RESOURCE_FORKS=<n>` | Fork cap pinned to `<n>` |

When active it prints a one-line notice at startup:
`[e2e] low-resource mode active: maxForks=…, timeouts raised`.

### 2. Signal-exit labelling in `scripts/check.mjs`

When a step's process is killed by a signal, the package manager surfaces it as
a numeric `Command failed with exit code 143` (SIGTERM) / `137` (SIGKILL) line,
while the outer shell reports only a generic exit `1`. The `check` report now
detects that and appends a clear reason so it isn't mistaken for a test-assertion
failure:

```
[check] step ended via SIGTERM (exit 143) — the process was killed, not an
assertion failure. Usual cause: resource pressure (parallel checks/builds
swapping) or an external kill. Re-run this project's check alone to confirm.
```

The hint is suppressed when the step was killed by the check's own idle
watchdog (that path already carries its own `[watchdog]` note), and it only
matches the package manager's own failure line — never an `exit code 143` a test
happens to log — so real assertion failures are never mislabeled.

---

## What's Fixed in 11.27.4

All fixes are in `scripts/check.mjs`, refining the idle watchdog introduced in
11.27.x.

### Watchdog now applies to test steps only

The idle watchdog kills a step whose child produces no output for the timeout
window (default 300s). It previously watched **every** step. But `build`,
`typecheck` and `audit` legitimately buffer all their output to the end (and go
silent under a non-TTY pipe) — watching them risked false-killing a slow but
progressing run. The watchdog is now armed **only for test steps**, whose
runners stream progress continuously, so prolonged silence genuinely means
deadlocked workers. The run header reflects this: `watchdog: 5m 0s (tests)`.

### No stray `SIGKILL` after PID reuse

When the watchdog fires it sends `SIGTERM`, then escalates to `SIGKILL` after a
5s grace window. That escalation timer is now tracked and cancelled once the
child exits within the grace window — previously the stray timer could fire
later and `SIGKILL` an **unrelated** process that had reused the freed PID.

### Invalid `--idle-timeout` falls back to the default

An unparseable, negative, or unit-suffixed value (e.g. `--idle-timeout=30s`)
now keeps the watchdog at its default (300s) and prints a notice, instead of
silently evaluating to `0` and disabling the protection. Only an explicit `0`
disables the watchdog.

```bash
--idle-timeout=120 # 120s
--idle-timeout=0 # explicitly disabled
--idle-timeout=30s # invalid → default 300s + "[check] ignoring invalid idle-timeout" notice
CHECK_IDLE_TIMEOUT=90 # env equivalent
```

### Clearer watchdog reason

The `[watchdog]` note now suggests re-running the **actual** failing command
(`Re-run the step directly to debug: <cmd>`) instead of a hard-coded
`pnpm run vitest`.

---

## Breaking Changes

None.

---

## Compatibility Notes

- **npm-mode consumers:** unaffected. `scripts/check.mjs` and
`vitest-e2e.config.ts` are not part of the npm package (`dist/`); no runtime
or API behavior changed. `pnpm update` is sufficient.
- **`IServerOptions` / `CoreModule.forRoot()` / all Core classes:** unchanged.
No source, export, or method signature changed in this release.
- **Starter-derived projects** (copied `scripts/check.mjs` / `vitest-e2e.config.ts`
from nest-server-starter 11.27.1+): copy the updated files to adopt the
test-only watchdog, the PID-reuse fix, the signal-exit hint, and the opt-in
`CHECK_LOW_RESOURCE` mode. All changes are backward compatible — the default
behavior (no env vars set) is unchanged, so an updated `check.mjs` /
`vitest-e2e.config.ts` is a drop-in replacement.
- **Vendor-mode consumers:** not applicable — `scripts/` and
`vitest-e2e.config.ts` are repo/starter tooling, not part of the vendored
`src/core/` file set. Nothing to sync.
- **`lt dev` / `lt ticket` parallel workflows:** the throttle is **manual**
(`CHECK_LOW_RESOURCE=1`). To have it apply automatically inside isolated
environments, the lt CLI can export it into the test env (the same way it
exports `LT_DEV_TEST_SHARDS` for the Playwright config). That is a CLI change,
not a framework change.
- **Nuxt / Playwright side:** no equivalent needed. The Playwright config
already uses `workers: 1` plus a `LT_DEV_TEST_SHARDS`-driven relaxed mode for
load, so the same class of contention is already handled there.

---

## Troubleshooting

### My `build` / `typecheck` / `audit` step is no longer killed after 300s

Intended. Only **test** steps are watched now (they stream progress). Buffering
steps that legitimately go silent are no longer at risk of a false watchdog
kill. If such a step genuinely hangs, it still fails via its own tool timeout or
the overall run.

### The check aborted with "step ended via SIGTERM (exit 143)"

That step's process was killed by a signal, not by a failed assertion. The usual
cause is resource pressure (several heavy checks/builds/test suites running at
once and swapping the machine) or an external kill. Re-run that project's check
alone to confirm — it will typically pass.

### e2e tests flake (401 / timeouts) only when I run many suites in parallel

This is physical CPU/MongoDB contention, not a data-isolation bug (each run has
its own database). Opt into the throttle for those sessions:
`CHECK_LOW_RESOURCE=1 pnpm test`. It caps parallel forks and raises timeouts so
the suites share the machine without starving each other.

### `CHECK_LOW_RESOURCE` seems to have no effect

Confirm the value is truthy (`1` / `true`) and not `0` / `false` / empty. When
active, the run prints `[e2e] low-resource mode active: maxForks=…` at startup.

---

## Repo-Internal Tooling Changes (no consumer action)

Summary of everything in this release, for projects that track the tooling
(all listed above in detail):

- `scripts/check.mjs`: idle watchdog restricted to test steps; stray-`SIGKILL`
(PID-reuse) fix; invalid `--idle-timeout` falls back to default instead of
disabling; watchdog reason shows the actual command; new `signalExitHint`
labelling `SIGTERM`/`SIGKILL` step exits.
- `vitest-e2e.config.ts`: opt-in `CHECK_LOW_RESOURCE` mode (fork cap + raised
timeouts) for stable parallel e2e runs; default behavior unchanged.

These carry forward the tooling introduced in
[11.27.1 → 11.27.2](./11.27.1-to-11.27.2.md) (chain-faithful audit) and
[11.27.2 → 11.27.3](./11.27.2-to-11.27.3.md) (idle watchdog + per-run test
databases).

---

## Module Documentation

No core module changed in this release. Relevant testing documentation:

- **Testing rules:** [.claude/rules/testing.md](../.claude/rules/testing.md) —
test framework, per-run database lifecycle, parallel execution
- **Reference implementation:** [nest-server-starter](https://github.com/lenneTech/nest-server-starter)
— carries the same `scripts/check.mjs` + `vitest-e2e.config.ts`

---

## References

- [Migration Guide 11.27.2 → 11.27.3](./11.27.2-to-11.27.3.md) — Previous release (verification-email crash fix + idle watchdog + per-run test DBs)
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) — reference implementation
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lenne.tech/nest-server",
"version": "11.27.3",
"version": "11.27.4",
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
"keywords": [
"node",
Expand Down
Loading
Loading