@lenne.tech/nest-server 11.42.8 → 11.42.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/better-auth.md +24 -0
- package/.claude/rules/configurable-features.md +5 -3
- package/.claude/rules/testing.md +4 -4
- package/FRAMEWORK-API.md +3 -2
- package/dist/core/common/decorators/api-methods.decorator.d.ts +2 -0
- package/dist/core/common/decorators/api-methods.decorator.js +8 -0
- package/dist/core/common/decorators/api-methods.decorator.js.map +1 -0
- package/dist/core/common/helpers/swagger.helper.js +312 -1
- package/dist/core/common/helpers/swagger.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core/modules/ai/core-ai-mcp.controller.js +15 -1
- package/dist/core/modules/ai/core-ai-mcp.controller.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js +8 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.helper.js.map +1 -1
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.registry.d.ts +7 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.registry.js +12 -0
- package/dist/core/modules/ai/helpers/ai-mcp-oauth.registry.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.config.js +12 -0
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +2 -2
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-openapi.helper.d.ts +62 -0
- package/dist/core/modules/better-auth/core-better-auth-openapi.helper.js +95 -0
- package/dist/core/modules/better-auth/core-better-auth-openapi.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.service.d.ts +10 -2
- package/dist/core/modules/better-auth/core-better-auth.service.js +89 -0
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core/modules/tus/core-tus.controller.js +3 -0
- package/dist/core/modules/tus/core-tus.controller.js.map +1 -1
- package/dist/core/modules/tus/core-tus.service.d.ts +3 -0
- package/dist/core/modules/tus/core-tus.service.js +58 -25
- package/dist/core/modules/tus/core-tus.service.js.map +1 -1
- package/dist/core/modules/tus/interfaces/tus-config.interface.js +1 -0
- package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -1
- package/dist/core/modules/tus/tus.module.d.ts +2 -0
- package/dist/core/modules/tus/tus.module.js +23 -1
- package/dist/core/modules/tus/tus.module.js.map +1 -1
- package/dist/core.module.js +1 -1
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +9 -1
- package/docs/security-overrides.md +6 -5
- package/migration-guides/11.42.8-to-11.42.9.md +186 -0
- package/migration-guides/11.42.9-to-11.42.10.md +111 -0
- package/package.json +7 -8
- package/src/core/common/decorators/api-methods.decorator.ts +25 -0
- package/src/core/common/helpers/swagger.helper.ts +454 -1
- package/src/core/common/interfaces/server-options.interface.ts +46 -8
- package/src/core/common/interfaces/swagger-setup-options.interface.ts +5 -2
- package/src/core/modules/ai/README.md +7 -0
- package/src/core/modules/ai/core-ai-mcp.controller.ts +19 -2
- package/src/core/modules/ai/helpers/ai-mcp-oauth.helper.ts +12 -2
- package/src/core/modules/ai/helpers/ai-mcp-oauth.registry.ts +29 -0
- package/src/core/modules/better-auth/README.md +34 -0
- package/src/core/modules/better-auth/better-auth.config.ts +27 -1
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +4 -17
- package/src/core/modules/better-auth/core-better-auth-openapi.helper.ts +219 -0
- package/src/core/modules/better-auth/core-better-auth.service.ts +166 -2
- package/src/core/modules/tus/README.md +50 -14
- package/src/core/modules/tus/core-tus.controller.ts +6 -0
- package/src/core/modules/tus/core-tus.service.ts +97 -26
- package/src/core/modules/tus/interfaces/tus-config.interface.ts +1 -0
- package/src/core/modules/tus/tus.module.ts +43 -1
- package/src/core.module.ts +2 -1
- package/src/index.ts +1 -0
|
@@ -157,6 +157,7 @@ JWT-based authentication for existing projects:
|
|
|
157
157
|
| `@CommonError(code)` | Error code registration |
|
|
158
158
|
| `@SkipTenantCheck()` | Opt out of CoreTenantGuard validation on a method (not for tenant-restricted API tokens — they stay bound) |
|
|
159
159
|
| `@ApiTokenScopes(...scopes)` | Open a route/class to API tokens holding one of the scopes; tokens are denied everywhere else |
|
|
160
|
+
| `@ApiMethods(...methods)` | Swagger only (11.42.9): the HTTP methods an `@All()` handler actually serves — `setupSwagger()` documents those instead of all eight. No runtime effect |
|
|
160
161
|
|
|
161
162
|
### File Handling
|
|
162
163
|
|
|
@@ -166,7 +167,7 @@ JWT-based authentication for existing projects:
|
|
|
166
167
|
| **REST Endpoints** | `GET /files/id/:id`, `GET /files/:filename` (core, gated by `file.downloadRoles`, default ADMIN); `POST /files/upload`, `DELETE /files/:id` (project-specific) |
|
|
167
168
|
| **GraphQL Endpoints** | `getFileInfo` (`file.downloadRoles`), `uploadFile` / `uploadFiles` (`file.uploadRoles`), `deleteFile` (`file.deleteRoles`) — all default ADMIN |
|
|
168
169
|
| **File access control** | Roles are the coarse filter; per-file rules go in `CoreFileService.checkRights()` using metadata written at upload time. Cover BOTH the `id` and the `filename` branch — the filename route authorizes on the by-name lookup alone when presigned S3 downloads are on, and `deleteFileByName()` always does. Working reference: `src/server/modules/file/file.service.ts` (with `file.downloadRoles: [S_USER]` in `src/config.env.ts`, so the rule is actually reached). Both file classes carry `@SkipTenantCheck()` — GridFS is not tenant-scoped |
|
|
169
|
-
| **TUS Module** | Resumable uploads via tus.io protocol (creation, termination, expiration), gated by `tus.roles` (default `S_USER`); `OPTIONS` stays public for the CORS preflight. Records owner and, with `multiTenancy` only, the validated tenant at upload creation; a custom service plugs in via `TusModule.forRoot({ service })` |
|
|
170
|
+
| **TUS Module** | Resumable uploads via tus.io protocol (creation, termination, expiration), gated by `tus.roles` (default `S_USER`); `OPTIONS` stays public for the CORS preflight. Records owner and, with `multiTenancy` only, the validated tenant at upload creation; a custom service plugs in via `TusModule.forRoot({ service })`. Configured by `TusModule.forRoot({ config })`, else the server config's `tus` key (11.42.9+). Switched-off `creation` / `termination` answer 501 (enforced by `CoreTusService`, not `@tus/server`); upload URLs follow the path the upload was created at |
|
|
170
171
|
| **GridFS Migration** | Completed TUS uploads auto-migrate to GridFS |
|
|
171
172
|
| **CORS Support** | Automatic CORS headers for browser uploads |
|
|
172
173
|
|
|
@@ -636,6 +637,13 @@ If authentication succeeds, `req.user` is set with the authenticated user (inclu
|
|
|
636
637
|
> raw-forwarded routes. A forged `POST /iam/update-user {"roles":["admin"]}` is rejected with
|
|
637
638
|
> `FIELD_NOT_ALLOWED` (HTTP 400) at the input-parse stage, before any persistence.
|
|
638
639
|
|
|
640
|
+
> **Never routed: `/iam/open-api/*` (11.42.9).** nest-server registers Better-Auth's `openAPI()` plugin
|
|
641
|
+
> only so `setupSwagger()` can document Better-Auth's own routes, and marks its endpoints `SERVER_ONLY`:
|
|
642
|
+
> better-call leaves them out of the router, so a forwarded request answers 404 however its path is
|
|
643
|
+
> spelled, while the generator still runs in-process. Deliberately not a path check in the middleware —
|
|
644
|
+
> it would see the path as sent, Better-Auth the one `new URL()` produced (`/iam/./open-api/…`). Only a
|
|
645
|
+
> project that registered `openAPI()` itself in `betterAuth.plugins` gets the routes.
|
|
646
|
+
|
|
639
647
|
> **"Raw" has one exception since 11.38.0: the password-setting reset routes.**
|
|
640
648
|
> `CoreBetterAuthApiMiddleware.normalizeResetPassword()` rewrites the password field of
|
|
641
649
|
> `/reset-password`, `/email-otp/reset-password` and `/phone-number/reset-password` to its
|
|
@@ -18,7 +18,7 @@ used to **exact-pin**, to a vulnerable version:
|
|
|
18
18
|
|---------|----------|------------------------------------------|
|
|
19
19
|
| `ws` | [GHSA-96hv-2xvq-fx4p](https://github.com/advisories/GHSA-96hv-2xvq-fx4p) — high: memory-exhaustion DoS + uninitialized memory disclosure. Patched `>=8.21.0` | Older `@nestjs/graphql` releases declare `"ws": "8.20.1"` — an **exact pin**, not a caret. 13.4.5, the version this framework declares, pins `8.21.3` |
|
|
20
20
|
| `js-yaml` | [GHSA-pm4m-ph32-ghv5](https://github.com/advisories/GHSA-pm4m-ph32-ghv5) — high: exponential parsing time in flow collections (DoS), patched `>=5.2.2`; [GHSA-r3ph-w7gj-g6xm](https://github.com/advisories/GHSA-r3ph-w7gj-g6xm) — moderate: `maxTotalMergeKeys` does not bound CPU use for empty merge sources, covers `<=5.4.0`, patched `>=5.4.1` (published 2026-09-29) | `@nestjs/swagger` declares js-yaml as an **exact pin**, the same shape as the `ws` case: older releases `5.2.1`, 11.4.7 — the version this framework declares, and the newest 11.x — `5.3.0`. No swagger update reaches the fix, so **this entry is load-bearing again** |
|
|
21
|
-
| `@graphql-tools/utils` | [GHSA-7mx3-vvmw-hjmv](https://github.com/advisories/GHSA-7mx3-vvmw-hjmv) — high: prototype pollution in `mergeDeep`, which runs while the GraphQL schema is assembled. Covers `<=12.0.0`, patched `>=12.0.1` (added 2026-10-06) | `@nestjs/graphql` 13.4.5 — the version this framework declares, and the newest 13.x — declares `"@graphql-tools/utils": "12.0.0"`, an **exact pin**. Only `@nestjs/graphql` 14.x moves it, so **this entry is load-bearing**. `@apollo/server` reaches it too, through `@graphql-tools/schema` / `merge` — and in a lockfile that still holds schema 10.0.x (resolved before 2026-08-12), as **utils 11.x**: vulnerable too, with no 11.x fix, and outside the override key. Lift it with `pnpm update --depth Infinity @graphql-tools/schema @graphql-tools/merge`; the current releases request utils `^12.0.3`, which needs Node >=22.15 |
|
|
21
|
+
| `@graphql-tools/utils` | [GHSA-7mx3-vvmw-hjmv](https://github.com/advisories/GHSA-7mx3-vvmw-hjmv) — high: prototype pollution in `mergeDeep`, which runs while the GraphQL schema is assembled. Covers `<=12.0.0`, patched `>=12.0.1` (added 2026-10-06) | `@nestjs/graphql` 13.4.5 — the version this framework declares, and the newest 13.x — declares `"@graphql-tools/utils": "12.0.0"`, an **exact pin**. Only `@nestjs/graphql` 14.x moves it, so **this entry is load-bearing**. `@apollo/server` reaches it too, through `@graphql-tools/schema` / `merge` — and in a lockfile that still holds schema 10.0.x (resolved before 2026-08-12), as **utils 11.x**: vulnerable too, with no 11.x fix, and outside the override key. Lift it with `pnpm update --depth Infinity @graphql-tools/schema @graphql-tools/merge`; the current releases request utils `^12.0.3`, which needs Node >=22.15 — the framework's own floor since 11.42.10, which is why the snippet below targets 12.0.3 |
|
|
22
22
|
| `multer` | [GHSA-wc9g-mqfw-jrwm](https://github.com/advisories/GHSA-wc9g-mqfw-jrwm), [GHSA-535w-7cp7-47q4](https://github.com/advisories/GHSA-535w-7cp7-47q4), [GHSA-qfvm-cv95-jqjf](https://github.com/advisories/GHSA-qfvm-cv95-jqjf) — high: DoS via crafted multipart input; [GHSA-qvfw-j98x-7q72](https://github.com/advisories/GHSA-qvfw-j98x-7q72) — low: file size limit bypass. Patched `>=2.3.0` | `@nestjs/platform-express` up to 11.2.5 declares `"multer": "2.2.0"` — an **exact pin**. A direct `multer` dependency does not move it: you get both copies, and FileInterceptor uses the vulnerable one. 11.2.6, which this framework declared from 11.41.4, pins `2.4.0`, and so does 11.2.7, declared since 11.41.7 |
|
|
23
23
|
|
|
24
24
|
**Status since 11.42.4:** `@graphql-tools/utils` joins `js-yaml` as load-bearing — no `@nestjs/graphql`
|
|
@@ -64,12 +64,13 @@ overrides:
|
|
|
64
64
|
'multer@>=2.0.0 <2.4.0': '2.4.0'
|
|
65
65
|
|
|
66
66
|
# @nestjs/graphql 13.4.5 exact-pins @graphql-tools/utils@12.0.0 — GHSA-7mx3-vvmw-hjmv (high, patched >=12.0.1).
|
|
67
|
-
# Target
|
|
68
|
-
#
|
|
67
|
+
# Target the newest 12.x, so the caret requesters (@graphql-tools/schema / merge) are not held back.
|
|
68
|
+
# 12.0.2+ requires Node >=22.15 via @whatwg-node/promise-helpers@2 — the floor this framework declares
|
|
69
|
+
# since 11.42.10. On an older Node, take 12.0.1 instead.
|
|
69
70
|
# Does NOT reach a utils 11.x pulled in by an older @graphql-tools/schema 10.0.x — refresh that one instead:
|
|
70
|
-
# pnpm update --depth Infinity @graphql-tools/schema @graphql-tools/merge
|
|
71
|
+
# pnpm update --depth Infinity @graphql-tools/schema @graphql-tools/merge
|
|
71
72
|
# Remove once @nestjs/graphql pins >=12.0.1 in the major you use.
|
|
72
|
-
'@graphql-tools/utils@>=12.0.0 <12.0.
|
|
73
|
+
'@graphql-tools/utils@>=12.0.0 <12.0.3': '12.0.3'
|
|
73
74
|
```
|
|
74
75
|
|
|
75
76
|
### Retired: `@hono/node-server` (removed 2026-08-22, nest-server 11.36.1)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Migration Guide: 11.42.8 → 11.42.9
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| **New Feature** | `setupSwagger()` documents every HTTP interface the server makes usable — Better-Auth's own routes, `/ai/mcp`, the MCP OAuth endpoints (§1) |
|
|
8
|
+
| **Bugfix** | …and only those: routes the running configuration makes fail for every caller leave the document (§2) |
|
|
9
|
+
| **New Feature** | `@ApiMethods()` — the HTTP methods an `@All()` handler actually serves (§3) |
|
|
10
|
+
| **Bugfix** | The `tus` key in the server configuration takes effect — it was never read (§5). **Check your `tus` block** |
|
|
11
|
+
| **Bugfix** | TUS: upload URLs work below a global prefix and a project route, a `path` naming a new route moves the endpoint, `termination: false` / `creation: false` are enforced, new `relativeLocation` (§5) |
|
|
12
|
+
| **Bugfix** | `healthCheck.enabled: false` leaves the health check endpoints out (§6) |
|
|
13
|
+
| **Breaking Changes** | None in the API. A `tus` block in `config.env.ts` that did nothing before now applies (§5) |
|
|
14
|
+
| **Migration Effort** | Review a `tus` block in `config.env.ts` if you have one (§5.1); a proxy that renames the tus route or relative upload URLs need a look (§5.2). Regenerate a client built from `/api-docs-json` if you keep one (§4) |
|
|
15
|
+
|
|
16
|
+
## Quick Migration
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm update @lenne.tech/nest-server
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Vendor-mode projects pick it up with the core update.
|
|
23
|
+
|
|
24
|
+
## 1. Everything usable is documented
|
|
25
|
+
|
|
26
|
+
The document is built from the running application, so it follows the server configuration and every
|
|
27
|
+
project module automatically: a module that is registered appears, one that is not stays out. Three
|
|
28
|
+
kinds of interface used to be invisible to `@nestjs/swagger` because they have no Nest handler:
|
|
29
|
+
|
|
30
|
+
| Interface | Documented since 11.42.9 |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| Routes Better-Auth serves itself under `/iam` — whatever the configured plugins add (two-factor, passkey, sessions, password change, …), including plugins a project adds via `betterAuth.plugins` | from Better-Auth's own OpenAPI generator, under your Better-Auth controller (§1.1) |
|
|
33
|
+
| `/ai/mcp` (MCP server, `ai.mcp`) | POST/GET/DELETE, with the bearer requirement the endpoint enforces itself |
|
|
34
|
+
| MCP OAuth endpoints (`mountAiMcpOAuth()` in `main.ts`) | exactly the endpoints the call mounted (`/.well-known/…`, `/authorize`, `/token`, `/register`) |
|
|
35
|
+
|
|
36
|
+
### 1.1 Better-Auth routes
|
|
37
|
+
|
|
38
|
+
- Documented under your Better-Auth controller: same tags, and `operationTags` receives them with
|
|
39
|
+
`controller` set to that controller's class name (`handler` is Better-Auth's operation id).
|
|
40
|
+
- Operation ids: `<Controller>_betterAuth_<betterAuthOperationId>`, e.g.
|
|
41
|
+
`BetterAuthController_betterAuth_changePassword`. Unique and stable across builds.
|
|
42
|
+
- Schemas Better-Auth refers to are added as `BetterAuthUser`, `BetterAuthSession`, … so they cannot
|
|
43
|
+
collide with your own `User` model; Better-Auth's OpenAPI 3.1 constructs are converted to 3.0.
|
|
44
|
+
- Security: what Better-Auth enforces. A route behind one of its session middlewares (or one of the
|
|
45
|
+
social-account routes that check the session in their handler) carries the global requirement —
|
|
46
|
+
bearer or the session cookie; every other route is public (`security: []`): sign-in, password reset,
|
|
47
|
+
get-session, the two-factor verification steps of a sign-in. Better-Auth's own generator marks every
|
|
48
|
+
route bearer-only, so this is read from the live instance, not from the generator. Routes of plugins
|
|
49
|
+
you add yourself (`admin()`, `organization()`, …) always carry the global requirement.
|
|
50
|
+
- With API tokens configured they carry "API tokens: not allowed on this route" and never appear in the
|
|
51
|
+
API-token view.
|
|
52
|
+
- Under a global prefix or URI versioning Better-Auth's own routes are not reachable (the API
|
|
53
|
+
middleware does not forward them); they are then not documented, and a warning says why.
|
|
54
|
+
|
|
55
|
+
**Behind it:** Better-Auth exposes its generator only through its `openAPI()` plugin, so nest-server
|
|
56
|
+
registers that plugin itself (`disableDefaultReference: true`). Its own route,
|
|
57
|
+
`/iam/open-api/generate-schema`, is not part of your API: its endpoints are marked server-only, so
|
|
58
|
+
Better-Auth never routes them and they answer 404, as before this release. If you registered `openAPI()`
|
|
59
|
+
in `betterAuth.plugins` yourself, nothing changes — your registration replaces the internal one.
|
|
60
|
+
|
|
61
|
+
## 2. Only what can succeed is documented
|
|
62
|
+
|
|
63
|
+
A route that answers an error to every caller under the running configuration is left out. Each rule
|
|
64
|
+
reads the switch the handler itself reads:
|
|
65
|
+
|
|
66
|
+
| Configuration | Left out |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `auth.legacyEndpoints` keeps REST closed (the default since 11.38.0) | legacy `/auth/signin`, `/auth/signup`, `/auth/logout`, `/auth/refresh-token` (they answer 410) |
|
|
69
|
+
| `betterAuth.emailAndPassword.enabled: false` | `/iam/sign-in/email`, `/iam/sign-up/email` |
|
|
70
|
+
| `betterAuth.emailAndPassword.disableSignUp: true` | `/iam/sign-up/email` |
|
|
71
|
+
| `betterAuth.emailAndPassword.passwordReset: false` | `/iam/request-password-reset`, `/iam/reset-password`, `/iam/reset-password/{token}` |
|
|
72
|
+
| Better-Auth's own options, read from the live instance — so `betterAuth.options` counts too | e.g. POST `/iam/get-session` without `session.deferSessionRefresh`, social routes without a provider, two-factor OTP routes without `otpOptions.sendOTP`, `/iam/delete-user` without `user.deleteUser.enabled`, `/iam/change-email` without `user.changeEmail.enabled`, `/iam/update-session` without session fields that accept input |
|
|
73
|
+
| A Hub panel switched off (`hub.collectors.*`, `hub.mailbox`, `hub.emailPreview`, `hub.db`, `hub.migrations`) | its `*.json` endpoint and actions, which would only answer `{ available: false }` or 400 |
|
|
74
|
+
|
|
75
|
+
A project controller extending a core controller is covered too — the rules go by the handler, not by
|
|
76
|
+
the class name.
|
|
77
|
+
|
|
78
|
+
## 3. `@ApiMethods()` for `@All()` handlers
|
|
79
|
+
|
|
80
|
+
`@nestjs/swagger` documents an `@All()` handler as eight operations (GET … SEARCH), whatever it serves.
|
|
81
|
+
The TUS handlers now declare theirs, so `/tus` shows POST and `/tus/{id}` HEAD/PATCH/DELETE. Use the
|
|
82
|
+
decorator on your own catch-all handlers:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { ApiMethods } from '@lenne.tech/nest-server';
|
|
86
|
+
|
|
87
|
+
@All(':id')
|
|
88
|
+
@ApiMethods('get', 'delete')
|
|
89
|
+
async handle(@Req() req: Request, @Res() res: Response) { … }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
It affects the document only, never routing.
|
|
93
|
+
|
|
94
|
+
## 4. Generated clients
|
|
95
|
+
|
|
96
|
+
A client generated from `/api-docs-json` (hey-api, openapi-generator, …) gains functions for the newly
|
|
97
|
+
documented routes and loses those for routes your configuration switches off (and the eight
|
|
98
|
+
`/iam/{path}` catch-all functions since 11.42.8). Regenerate and let the compiler point at anything that
|
|
99
|
+
used a removed function.
|
|
100
|
+
|
|
101
|
+
## 5. TUS: the configuration takes effect
|
|
102
|
+
|
|
103
|
+
### 5.1 The server configuration's `tus` key is read
|
|
104
|
+
|
|
105
|
+
`TusModule.forRoot()` without a `config` of its own now reads the `tus` key of the server configuration
|
|
106
|
+
(`config.env.ts`, `NSC__TUS__*`) — the zero-config rule `BetterAuthModule.forRoot()` already follows. An
|
|
107
|
+
explicit `TusModule.forRoot({ config })` still wins. **Before this release that key was never read**:
|
|
108
|
+
TUS ran with the defaults whatever it said.
|
|
109
|
+
|
|
110
|
+
What that means for you, if `config.env.ts` has a `tus` block:
|
|
111
|
+
|
|
112
|
+
| You set | Before | Now |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `tus: false` | TUS enabled anyway | `/tus` is not registered |
|
|
115
|
+
| `allowedTypes` | every type accepted | other types answer 415 at creation |
|
|
116
|
+
| `maxSize` | 50 GB | your limit (413 above it) |
|
|
117
|
+
| `roles` | `[S_USER]` | your roles |
|
|
118
|
+
| `termination`, `creation`, `path`, `expiration`, … | defaults | your values (see 5.2 / 5.3) |
|
|
119
|
+
|
|
120
|
+
These are the values the block was written to enforce, so this is the behaviour you asked for — but it
|
|
121
|
+
starts on this upgrade. Check that the allowed types cover what your clients upload before you deploy.
|
|
122
|
+
|
|
123
|
+
### 5.2 `path` and upload URLs
|
|
124
|
+
|
|
125
|
+
Before this release `path` only changed the URL handed to a client for a new upload (`Location`); the
|
|
126
|
+
endpoint itself always stayed where the controller declared it. Every setup that works today keeps
|
|
127
|
+
working:
|
|
128
|
+
|
|
129
|
+
| `path` | Since 11.42.9 |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| unset | The upload URL is built from the path the upload was created at — so resuming now also works below a global prefix, a URI version or a project controller's own route, where it answered 404 before |
|
|
132
|
+
| ends with the controller's route — `'/api/tus'` (global prefix) or `'/files/tus'` (a proxy that strips `/files`) | Unchanged: the controller stays, upload URLs use this path. This was the workaround for the old behaviour and needs no change |
|
|
133
|
+
| a new route — `'/uploads'` | The controller is now mounted there (a project controller too, as `roles` already did). Before, the endpoint stayed at `/tus` while clients were sent to `/uploads/…`, so no upload could ever be resumed |
|
|
134
|
+
|
|
135
|
+
Like any controller route, a new route sits below the global prefix and the URI version.
|
|
136
|
+
|
|
137
|
+
Two cases need a look:
|
|
138
|
+
|
|
139
|
+
- **A proxy that renames the route** (public `/files/upload` forwarded as `/tus`): `path: '/files/upload'`
|
|
140
|
+
now moves the endpoint. Remove `path` and override `uploadCollectionPath(req)` in your
|
|
141
|
+
`CoreTusService` subclass to return the public path.
|
|
142
|
+
- **Relative upload URLs.** The framework now builds the upload URL itself, which takes precedence over
|
|
143
|
+
`@tus/server`'s `relativeLocation`. Use `tus.relativeLocation: true` (new); a `relativeLocation` you
|
|
144
|
+
pass to the tus server yourself — in a vendored core, for example — is honoured as well.
|
|
145
|
+
|
|
146
|
+
### 5.3 Switched-off extensions are enforced
|
|
147
|
+
|
|
148
|
+
`@tus/server` itself ignores the extension flags. Since this release:
|
|
149
|
+
|
|
150
|
+
| Setting | Effect |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| `termination: false` | DELETE answers 501, OPTIONS no longer advertises `termination` |
|
|
153
|
+
| `creation: false` | POST answers 501, OPTIONS no longer advertises the creation extensions |
|
|
154
|
+
| `creationWithUpload: false` | a POST carrying data answers 501 |
|
|
155
|
+
|
|
156
|
+
`checksum` and `concatenation` have no effect: neither built-in store supports them. The boot log now
|
|
157
|
+
lists the extensions the server actually offers.
|
|
158
|
+
|
|
159
|
+
## 6. `healthCheck.enabled: false`
|
|
160
|
+
|
|
161
|
+
The flag was declared and ignored: any `healthCheck` object registered `GET /health-check` and the
|
|
162
|
+
`healthCheck` query. `enabled: false` now leaves both out (and out of the Swagger document).
|
|
163
|
+
|
|
164
|
+
## Compatibility Notes
|
|
165
|
+
|
|
166
|
+
- The Swagger work changes nothing at runtime. Better-Auth's `openAPI()` plugin is now registered, but
|
|
167
|
+
kept off the router, so `/iam/open-api/*` answers 404 exactly as before. The runtime changes of this
|
|
168
|
+
release are the ones in §5 and §6.
|
|
169
|
+
- A `CoreTusService` subclass that overrides `onUploadCreate()` keeps the extension checks of §5.3: they
|
|
170
|
+
run before the hook, whether or not the override calls `super`.
|
|
171
|
+
- The description of Better-Auth's routes is prepared once at application bootstrap
|
|
172
|
+
(`CoreBetterAuthService.onApplicationBootstrap()`). A project service extending
|
|
173
|
+
`CoreBetterAuthService` that overrides `onApplicationBootstrap()` must call `super` to keep it.
|
|
174
|
+
- An explicit `@ApiBearerAuth()` / `@ApiSecurity()` on a route the guards admit to everybody now keeps
|
|
175
|
+
its security requirement instead of being documented as public.
|
|
176
|
+
|
|
177
|
+
## References
|
|
178
|
+
|
|
179
|
+
- Swagger helper: `src/core/common/helpers/swagger.helper.ts`
|
|
180
|
+
- Better-Auth rules: `src/core/modules/better-auth/core-better-auth-openapi.helper.ts`
|
|
181
|
+
- Decorator: `src/core/common/decorators/api-methods.decorator.ts`
|
|
182
|
+
- TUS: `src/core/modules/tus/tus.module.ts`, `src/core/modules/tus/core-tus.service.ts`, `src/core/modules/tus/README.md`
|
|
183
|
+
- Tests: `tests/unit/swagger-helper.spec.ts`, `tests/unit/swagger-availability.spec.ts`,
|
|
184
|
+
`tests/unit/better-auth-openapi-availability.spec.ts`, `tests/better-auth-openapi.e2e-spec.ts`,
|
|
185
|
+
`tests/unit/tus-module-config.spec.ts`, `tests/tus-protocol-config.e2e-spec.ts`,
|
|
186
|
+
`tests/unit/core-module-signatures.spec.ts`
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Migration Guide: 11.42.9 → 11.42.10
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| **Requirement** | `engines.node` is now `>= 22.15` (was `>= 22.12`). In practice the floor already applied — see §1 |
|
|
8
|
+
| **Security** | Recommended `@graphql-tools/utils` override target moves from 12.0.1 to 12.0.3 (§2) |
|
|
9
|
+
| **Dependencies** | `nodemailer` 10.0.16 (bug fixes only). Dev tooling: transitive `handlebars` 4.7.10 (§3) |
|
|
10
|
+
| **Breaking Changes** | None in the API |
|
|
11
|
+
| **Migration Effort** | None for projects on Node ≥ 22.15. Projects that carry their own `@graphql-tools/utils` override should raise it (§2) |
|
|
12
|
+
|
|
13
|
+
## Quick Migration
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm update @lenne.tech/nest-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Vendor-mode projects pick it up with the core update. No code or configuration change is required.
|
|
20
|
+
|
|
21
|
+
## 1. Node ≥ 22.15
|
|
22
|
+
|
|
23
|
+
The declared minimum Node version rises from 22.12 to 22.15. This changes the declaration, not the reality:
|
|
24
|
+
`@apollo/server` resolves `@graphql-tools/schema` 10.1.3, which requests `@graphql-tools/utils ^12.0.3`, and
|
|
25
|
+
that in turn requires `@whatwg-node/promise-helpers` 2 — whose own `engines` field demands Node `>=22.15.0`.
|
|
26
|
+
Every fresh install of a GraphQL-enabled project has carried that requirement since those releases came out.
|
|
27
|
+
The framework's `engines` field now says so instead of promising a floor its own dependency tree does not
|
|
28
|
+
honour.
|
|
29
|
+
|
|
30
|
+
Check the Node version wherever the server runs:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node --version # must print v22.15.0 or newer (any Node 24 qualifies)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The places to look: the Docker base image (`FROM node:…`), the CI runner's Node version, and the version a
|
|
37
|
+
host or PaaS pins. The starter's Dockerfile and the framework's own CI already run Node 24.
|
|
38
|
+
|
|
39
|
+
## 2. `@graphql-tools/utils` override: target 12.0.3
|
|
40
|
+
|
|
41
|
+
Overrides in this package's `pnpm-workspace.yaml` do not reach your project (see
|
|
42
|
+
[security-overrides.md](../docs/security-overrides.md)), so a project that copied the
|
|
43
|
+
`@graphql-tools/utils` entry for GHSA-7mx3-vvmw-hjmv holds its own. Until now the recommended target was
|
|
44
|
+
12.0.1, because 12.0.2+ needs Node ≥ 22.15. With the framework's floor at 22.15, take the newest 12.x:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
# Before
|
|
48
|
+
overrides:
|
|
49
|
+
'@graphql-tools/utils@>=12.0.0 <12.0.1': '12.0.1'
|
|
50
|
+
|
|
51
|
+
# After
|
|
52
|
+
overrides:
|
|
53
|
+
'@graphql-tools/utils@>=12.0.0 <12.0.3': '12.0.3'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pnpm install
|
|
58
|
+
pnpm update --depth Infinity @graphql-tools/schema @graphql-tools/merge
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Why it matters beyond tidiness: the key intersects the `^12.0.0` range that `@graphql-tools/schema` and
|
|
62
|
+
`@graphql-tools/merge` request, so a target behind the newest 12.x holds them back too. Commit
|
|
63
|
+
`pnpm-workspace.yaml` and `pnpm-lock.yaml` together — a workspace file without the re-installed lockfile fails
|
|
64
|
+
every `pnpm install --frozen-lockfile`, typically in the Docker build.
|
|
65
|
+
|
|
66
|
+
Afterwards `@nestjs/graphql` 13.4.5 still brings its own exact-pinned `@graphql-tools/schema` 10.1.0 /
|
|
67
|
+
`merge` 9.2.3 next to the 10.1.3 / 9.2.6 that `@apollo/server` uses. That is expected: both copies share the
|
|
68
|
+
single overridden `@graphql-tools/utils` 12.0.3 and the single `graphql`.
|
|
69
|
+
|
|
70
|
+
Projects still on Node < 22.15 keep the 12.0.1 target until they upgrade Node.
|
|
71
|
+
|
|
72
|
+
## 3. Dependency updates
|
|
73
|
+
|
|
74
|
+
- **`nodemailer` 10.0.15 → 10.0.16** — bug fixes in the address parser and cookie handling, no API change.
|
|
75
|
+
A project that carries a lock-step `nodemailer` override (nest-server-starter does) should raise it to
|
|
76
|
+
`'nodemailer@<10.0.16': '10.0.16'`.
|
|
77
|
+
- **`handlebars` 4.7.10 (dev tooling only)** — three advisories (GHSA-8r5x-fm3f-whwj, GHSA-p8wg-vrv2-v86f,
|
|
78
|
+
GHSA-xw65-4hp5-5hc7) reach projects through `@compodoc/compodoc`, which already allows the fixed version.
|
|
79
|
+
If `pnpm audit` reports them in your project, refresh the stale lockfile entry instead of adding an override:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm update --depth Infinity handlebars
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Compatibility Notes
|
|
86
|
+
|
|
87
|
+
| Pattern | Status |
|
|
88
|
+
| ------- | ------ |
|
|
89
|
+
| Projects on Node 24 (starter Dockerfile, CI) | Unaffected |
|
|
90
|
+
| Projects on Node 22.12 – 22.14 | Already outside what `@graphql-tools/utils` 12.0.3 declares; upgrade Node |
|
|
91
|
+
| Vendor-mode projects | The core carries no `engines` field — check the Node version of your own runtime (§1) |
|
|
92
|
+
| `engine-strict=true` in `.npmrc` | Installs now fail on Node < 22.15 instead of warning |
|
|
93
|
+
|
|
94
|
+
## Troubleshooting
|
|
95
|
+
|
|
96
|
+
| Symptom | Cause | Fix |
|
|
97
|
+
| ------- | ----- | --- |
|
|
98
|
+
| `pnpm install` warns `Unsupported engine` for `@whatwg-node/promise-helpers` | Node older than 22.15 | Upgrade Node (§1) |
|
|
99
|
+
| `ERR_PNPM_UNSUPPORTED_ENGINE` for `@lenne.tech/nest-server` | `engine-strict` with Node < 22.15 | Upgrade Node (§1) |
|
|
100
|
+
| `pnpm audit` reports `@graphql-tools/utils` after updating | Your own override still targets an older version, or an old `@graphql-tools/schema` 10.0.x pulls utils 11.x | Raise the override and refresh schema / merge (§2) |
|
|
101
|
+
| `pnpm audit` reports `handlebars` via `@compodoc/compodoc` | Stale lockfile entry | `pnpm update --depth Infinity handlebars` (§3) |
|
|
102
|
+
|
|
103
|
+
## Module Documentation
|
|
104
|
+
|
|
105
|
+
- [Security overrides](../docs/security-overrides.md) — which overrides are load-bearing and how to write your own
|
|
106
|
+
- `.claude/rules/package-management.md` — override and suppression rules
|
|
107
|
+
|
|
108
|
+
## References
|
|
109
|
+
|
|
110
|
+
- [GHSA-7mx3-vvmw-hjmv](https://github.com/advisories/GHSA-7mx3-vvmw-hjmv) — `@graphql-tools/utils` prototype pollution
|
|
111
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) — reference project
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.42.
|
|
3
|
+
"version": "11.42.10",
|
|
4
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).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
"url": "https://github.com/lenneTech/nest-server/issues"
|
|
85
85
|
},
|
|
86
86
|
"engines": {
|
|
87
|
-
"node": ">= 22.
|
|
87
|
+
"node": ">= 22.15",
|
|
88
88
|
"pnpm": "^11.0.0"
|
|
89
89
|
},
|
|
90
|
-
"packageManager": "pnpm@11.
|
|
90
|
+
"packageManager": "pnpm@11.28.5+sha512.484563d30b4c2ad27cb48aa94ff2b302dbcebc8ff6bf28816c2589886e146ce7f76f354f3bc1a438e6f5e9768c302aa0a6f7add8232903cd4cf81426cbc14d9e",
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"@apollo/server": "5.5.1",
|
|
93
93
|
"@as-integrations/express5": "1.1.2",
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"mongoose": "9.10.4",
|
|
130
130
|
"multer": "2.4.0",
|
|
131
131
|
"node-mailjet": "6.0.11",
|
|
132
|
-
"nodemailer": "10.0.
|
|
132
|
+
"nodemailer": "10.0.16",
|
|
133
133
|
"passport": "0.7.0",
|
|
134
134
|
"passport-jwt": "4.0.1",
|
|
135
135
|
"reflect-metadata": "0.2.2",
|
|
@@ -182,9 +182,9 @@
|
|
|
182
182
|
}
|
|
183
183
|
},
|
|
184
184
|
"devDependencies": {
|
|
185
|
-
"@aws-sdk/client-s3": "3.
|
|
186
|
-
"@aws-sdk/lib-storage": "3.
|
|
187
|
-
"@aws-sdk/s3-request-presigner": "3.
|
|
185
|
+
"@aws-sdk/client-s3": "3.1147.0",
|
|
186
|
+
"@aws-sdk/lib-storage": "3.1147.0",
|
|
187
|
+
"@aws-sdk/s3-request-presigner": "3.1147.0",
|
|
188
188
|
"@better-auth/core": "1.7.7",
|
|
189
189
|
"@better-auth/passkey": "1.7.7",
|
|
190
190
|
"@compodoc/compodoc": "2.0.0",
|
|
@@ -203,7 +203,6 @@
|
|
|
203
203
|
"@types/node": "26.6.4",
|
|
204
204
|
"@types/passport": "1.0.17",
|
|
205
205
|
"@vitest/coverage-v8": "4.1.11",
|
|
206
|
-
"ansi-colors": "4.1.3",
|
|
207
206
|
"better-auth": "1.7.7",
|
|
208
207
|
"bullmq": "6.3.11",
|
|
209
208
|
"cross-env": "10.1.0",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SetMetadata } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
/** Metadata key of {@link ApiMethods} */
|
|
4
|
+
export const API_METHODS_KEY = 'lt:apiMethods';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lists the HTTP methods an `@All()` handler actually serves.
|
|
8
|
+
*
|
|
9
|
+
* `@nestjs/swagger` documents an `@All()` handler as eight operations (GET, POST, PUT, DELETE, PATCH,
|
|
10
|
+
* OPTIONS, HEAD, SEARCH), whatever the handler does with them. `setupSwagger()` keeps only the methods
|
|
11
|
+
* named here, so a catch-all that serves three of them is documented with three.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* @All(':id')
|
|
16
|
+
* @ApiMethods('head', 'patch', 'delete')
|
|
17
|
+
* async handleUpload(@Req() req: Request, @Res() res: Response) { … }
|
|
18
|
+
* ```
|
|
19
|
+
* @since 11.42.9
|
|
20
|
+
*/
|
|
21
|
+
export const ApiMethods = (...methods: string[]) =>
|
|
22
|
+
SetMetadata(
|
|
23
|
+
API_METHODS_KEY,
|
|
24
|
+
methods.map((method) => method.toLowerCase()),
|
|
25
|
+
);
|