@lenne.tech/nest-server 11.27.6 → 11.28.0
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/architecture.md +66 -0
- package/.claude/rules/better-auth.md +88 -2
- package/.claude/rules/package-management.md +23 -14
- package/.claude/rules/role-system.md +90 -0
- package/.claude/rules/versioning.md +17 -2
- package/CLAUDE.md +8 -2
- package/FRAMEWORK-API.md +27 -1
- package/dist/core/common/decorators/restricted.decorator.d.ts +4 -4
- package/dist/core/common/decorators/restricted.decorator.js +29 -26
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/exceptions/access-denied.exception.d.ts +4 -0
- package/dist/core/common/exceptions/access-denied.exception.js +12 -0
- package/dist/core/common/exceptions/access-denied.exception.js.map +1 -0
- package/dist/core/common/helpers/clone.helper.d.ts +6 -0
- package/dist/core/common/helpers/clone.helper.js +67 -0
- package/dist/core/common/helpers/clone.helper.js.map +1 -0
- package/dist/core/common/helpers/db.helper.d.ts +2 -14
- package/dist/core/common/helpers/db.helper.js +13 -106
- package/dist/core/common/helpers/db.helper.js.map +1 -1
- package/dist/core/common/helpers/id.helper.d.ts +15 -0
- package/dist/core/common/helpers/id.helper.js +104 -0
- package/dist/core/common/helpers/id.helper.js.map +1 -0
- package/dist/core/common/helpers/input.helper.d.ts +1 -6
- package/dist/core/common/helpers/input.helper.js +15 -72
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/helpers/service.helper.js +4 -3
- package/dist/core/common/helpers/service.helper.js.map +1 -1
- package/dist/core/common/inputs/combined-filter.input.d.ts +1 -12
- package/dist/core/common/inputs/combined-filter.input.js +2 -51
- package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
- package/dist/core/common/inputs/filter.input.d.ts +10 -1
- package/dist/core/common/inputs/filter.input.js +40 -5
- package/dist/core/common/inputs/filter.input.js.map +1 -1
- package/dist/core/common/services/config.service.js +5 -5
- package/dist/core/common/services/config.service.js.map +1 -1
- package/dist/core/modules/ai/core-ai.constants.d.ts +22 -0
- package/dist/core/modules/ai/core-ai.constants.js +26 -0
- package/dist/core/modules/ai/core-ai.constants.js.map +1 -0
- package/dist/core/modules/ai/index.d.ts +1 -0
- package/dist/core/modules/ai/index.js +1 -0
- package/dist/core/modules/ai/index.js.map +1 -1
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.d.ts +17 -0
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js +3 -0
- package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js.map +1 -0
- package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-budget.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-connection.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-conversation.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +2 -3
- package/dist/core/modules/ai/services/core-ai-interaction.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-mode.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +1 -3
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-prompt.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-slot.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -2
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +7 -5
- package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
- package/dist/core/modules/ai/services/core-ai.service.d.ts +2 -17
- package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
- package/dist/core/modules/auth/guards/roles.guard.js +2 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-roles.guard.js +3 -3
- package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +6 -0
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.constants.d.ts +3 -0
- package/dist/core/modules/better-auth/core-better-auth.constants.js +7 -0
- package/dist/core/modules/better-auth/core-better-auth.constants.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js +1 -3
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.module.d.ts +1 -2
- package/dist/core/modules/better-auth/core-better-auth.module.js +23 -20
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.registry.d.ts +4 -0
- package/dist/core/modules/better-auth/core-better-auth.registry.js +16 -0
- package/dist/core/modules/better-auth/core-better-auth.registry.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.service.d.ts +1 -2
- package/dist/core/modules/better-auth/core-better-auth.service.js +7 -6
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant-member.model.js +3 -3
- package/dist/core/modules/tenant/core-tenant-member.model.js.map +1 -1
- package/dist/core/modules/tenant/core-tenant.guard.js +7 -6
- package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
- package/dist/core/modules/tus/tus.constants.d.ts +1 -0
- package/dist/core/modules/tus/tus.constants.js +5 -0
- package/dist/core/modules/tus/tus.constants.js.map +1 -0
- package/dist/core/modules/tus/tus.module.d.ts +1 -1
- package/dist/core/modules/tus/tus.module.js +8 -6
- package/dist/core/modules/tus/tus.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/server/modules/user/user.service.js +1 -1
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +39 -0
- package/migration-guides/11.27.6-to-11.27.7.md +374 -0
- package/migration-guides/11.27.7-to-11.28.0.md +350 -0
- package/package.json +6 -104
- package/src/core/common/decorators/restricted.decorator.ts +67 -20
- package/src/core/common/exceptions/access-denied.exception.ts +49 -0
- package/src/core/common/helpers/clone.helper.ts +110 -0
- package/src/core/common/helpers/db.helper.ts +14 -161
- package/src/core/common/helpers/id.helper.ts +198 -0
- package/src/core/common/helpers/input.helper.ts +21 -88
- package/src/core/common/helpers/service.helper.ts +6 -4
- package/src/core/common/inputs/combined-filter.input.ts +10 -57
- package/src/core/common/inputs/filter.input.ts +92 -1
- package/src/core/common/services/config.service.ts +4 -1
- package/src/core/modules/ai/core-ai.constants.ts +92 -0
- package/src/core/modules/ai/index.ts +1 -0
- package/src/core/modules/ai/interfaces/ai-interaction-record.interface.ts +34 -0
- package/src/core/modules/ai/services/core-ai-budget.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-connection.service.ts +5 -6
- package/src/core/modules/ai/services/core-ai-conversation.service.ts +5 -6
- package/src/core/modules/ai/services/core-ai-interaction.service.ts +6 -7
- package/src/core/modules/ai/services/core-ai-mode.service.ts +8 -2
- package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +7 -4
- package/src/core/modules/ai/services/core-ai-prompt.service.ts +8 -2
- package/src/core/modules/ai/services/core-ai-slot.service.ts +7 -4
- package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +8 -4
- package/src/core/modules/ai/services/core-ai.service.ts +8 -11
- package/src/core/modules/auth/guards/roles.guard.ts +5 -4
- package/src/core/modules/better-auth/better-auth-roles.guard.ts +15 -7
- package/src/core/modules/better-auth/better-auth.config.ts +16 -0
- package/src/core/modules/better-auth/core-better-auth.constants.ts +73 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +5 -6
- package/src/core/modules/better-auth/core-better-auth.module.ts +34 -8
- package/src/core/modules/better-auth/core-better-auth.registry.ts +53 -0
- package/src/core/modules/better-auth/core-better-auth.service.ts +19 -12
- package/src/core/modules/better-auth/index.ts +9 -0
- package/src/core/modules/tenant/core-tenant-member.model.ts +8 -3
- package/src/core/modules/tenant/core-tenant.guard.ts +26 -11
- package/src/core/modules/tus/tus.constants.ts +25 -0
- package/src/core/modules/tus/tus.module.ts +7 -2
- package/src/index.ts +1 -0
- package/src/server/modules/user/user.service.ts +3 -2
|
@@ -949,6 +949,41 @@ The `process()` method in `ModuleService` is the **primary** way to handle CRUD
|
|
|
949
949
|
+---------------------------------------------------------------+
|
|
950
950
|
```
|
|
951
951
|
|
|
952
|
+
### Status Codes: 401 vs 403 (v11.28.0+)
|
|
953
|
+
|
|
954
|
+
One policy across **all five** permission layers — the role guards, the tenant guard, `check()` /
|
|
955
|
+
`checkRights`, `checkRestricted()` (object and field level), and a model's `securityCheck()`:
|
|
956
|
+
|
|
957
|
+
| Situation | Status | Thrown by |
|
|
958
|
+
|-----------|--------|-----------|
|
|
959
|
+
| Requester is **not authenticated** | **401** `ErrorCode.UNAUTHORIZED` | guards, `accessDeniedException(undefined)` |
|
|
960
|
+
| Requester **is authenticated** but lacks a right | **403** `ErrorCode.ACCESS_DENIED` | guards, `accessDeniedException(user)` |
|
|
961
|
+
| Resource is locked via `S_NO_ONE` | **403**, always — even for anonymous requesters | guards, `check()` |
|
|
962
|
+
|
|
963
|
+
`S_NO_ONE` is 403 for everyone because authenticating can never unlock it; a 401 would tell the
|
|
964
|
+
client to retry after logging in, which is a lie.
|
|
965
|
+
|
|
966
|
+
**Why this matters:** SPA auth layers commonly treat 401 as "session expired" and clear the session
|
|
967
|
+
(the `@lenne.tech/nuxt-extensions` auth interceptor patches `$fetch`/`fetch` globally and does exactly
|
|
968
|
+
this). A permission error answered with 401 therefore logs the user out of the whole app. With this
|
|
969
|
+
policy a frontend may treat 401 as "session invalid" — with one exception:
|
|
970
|
+
`ErrorCode.EMAIL_VERIFICATION_REQUIRED` is a legitimate 401 (no session exists yet at sign-in) that
|
|
971
|
+
must **not** trigger a logout. Branch on the ErrorCode, not on the status alone.
|
|
972
|
+
|
|
973
|
+
**Writing new denial code:** use the exported factory rather than hand-rolling the decision. It
|
|
974
|
+
returns the **native** `ForbiddenException` / `UnauthorizedException`, so `instanceof` checks and
|
|
975
|
+
`@Catch(...)` filters in consuming projects keep working:
|
|
976
|
+
|
|
977
|
+
```typescript
|
|
978
|
+
import { accessDeniedException } from '@lenne.tech/nest-server';
|
|
979
|
+
|
|
980
|
+
// In a service, a custom guard, or a model's securityCheck():
|
|
981
|
+
throw accessDeniedException(currentUser);
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
See `src/core/common/exceptions/access-denied.exception.ts` and
|
|
985
|
+
`migration-guides/11.27.7-to-11.28.0.md`.
|
|
986
|
+
|
|
952
987
|
### Depth-Based Optimization (v11.23.0+)
|
|
953
988
|
|
|
954
989
|
When `process()` is called from within another `process()` call (service cascades like A.create → B.create → C.create), steps 4–6 are **conditionally skipped** on inner calls to avoid redundant work:
|
|
@@ -1095,6 +1130,10 @@ Controls who can access a resolver/controller method. Evaluated by the RolesGuar
|
|
|
1095
1130
|
|
|
1096
1131
|
Controls who can see or modify specific properties. Evaluated by `CheckResponseInterceptor` (output) and `checkRights()` (input).
|
|
1097
1132
|
|
|
1133
|
+
On **output** a denied field is silently removed (no exception). On **input** the request is
|
|
1134
|
+
rejected: **403** for an authenticated requester, **401** for an anonymous one, and **403 always**
|
|
1135
|
+
for `S_NO_ONE` — see [Status Codes: 401 vs 403](#status-codes-401-vs-403-v11280) above.
|
|
1136
|
+
|
|
1098
1137
|
```typescript
|
|
1099
1138
|
export class User extends CorePersistenceModel {
|
|
1100
1139
|
// Only admins or the user themselves can see the email
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# Migration Guide: 11.27.6 → 11.27.7
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **New Features** | None. Four new leaf modules (`core-better-auth.constants.ts`, `core-better-auth.registry.ts`, `id.helper.ts`, `clone.helper.ts`) — the tokens and helpers they hold were already public and stay reachable from their old locations. The registry is module-internal and deliberately **not** exported. |
|
|
9
|
+
| **Public API** | Unchanged. Verified by diffing the full export surface of `src/index.ts` on both versions: **nothing removed, nothing renamed**. |
|
|
10
|
+
| **Bugfixes** | **(1)** Applications compiled with **SWC** (`nest start -b swc`, `nest build -b swc`) no longer crash at startup with `ReferenceError: Cannot access 'BETTER_AUTH_INSTANCE' before initialization` when BetterAuth is enabled. **(2)** A direct import of `core/common/inputs/combined-filter.input` no longer crashes under SWC with `ReferenceError: Cannot access 'CombinedFilterInput' before initialization`. **(3)** A consumer setting `betterAuth.options.advanced.defaultCookieAttributes` no longer silently strips the `Secure` flag from session cookies on an `https://` deployment. |
|
|
11
|
+
| **Security** | See bugfix (3). If your project sets `advanced.defaultCookieAttributes` for any reason (cookie partitioning, a custom domain) **and** runs on an `https://` baseURL, your session cookies were being sent without `Secure`. Updating fixes this with no action required. |
|
|
12
|
+
| **Deprecations** | The BetterAuth DI tokens re-exported from `core-better-auth.module` / `core-better-auth.service` are now marked deprecated in their docblocks. Import them from `core-better-auth.constants` (or the package root) instead. Both paths keep working. |
|
|
13
|
+
| **Toolchain** | The framework repo moved to **pnpm 11** (see the note below). **npm consumers are unaffected** — you do not inherit the framework's `packageManager` or `pnpm-workspace.yaml`. Relevant only if you contribute to the framework or mirror its toolchain. |
|
|
14
|
+
| **Migration Effort** | **npm mode:** 0 minutes (automatic) — `pnpm update` is enough. **Vendor mode:** see the note below — the BetterAuth change is an atomic file set. |
|
|
15
|
+
|
|
16
|
+
This is a **startup-crash + cookie-security bugfix release**. No source-code or
|
|
17
|
+
config changes are required in consuming projects.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Toolchain: pnpm 11 (framework contributors only)
|
|
22
|
+
|
|
23
|
+
**npm consumers of `@lenne.tech/nest-server` can ignore this section** — installing the
|
|
24
|
+
package does not import our package manager or its config.
|
|
25
|
+
|
|
26
|
+
The framework repo itself upgraded from pnpm 10 to **pnpm 11**, for one concrete reason:
|
|
27
|
+
**`pnpm audit` is broken on every pnpm 10.x** — npm retired the legacy audit endpoint
|
|
28
|
+
(HTTP 410) and only pnpm 11 uses the working bulk-advisory endpoint. The upgrade also
|
|
29
|
+
required migrating pnpm's config, because pnpm 11 has breaking changes:
|
|
30
|
+
|
|
31
|
+
- **The `pnpm` field in `package.json` is gone.** All pnpm settings moved to
|
|
32
|
+
`pnpm-workspace.yaml`: `overrides`, `allowBuilds`, `nodeLinker`, `autoInstallPeers`,
|
|
33
|
+
`strictPeerDependencies`, `peerDependencyRules`. `.npmrc` is now auth/registry only.
|
|
34
|
+
- **`allowBuilds` replaced `onlyBuiltDependencies`** — a map that must classify *every*
|
|
35
|
+
package with an install script as `true` (build it, e.g. `bcrypt`) or `false` (don't,
|
|
36
|
+
e.g. the `@scarf/scarf` telemetry). An unclassified build-script package makes
|
|
37
|
+
`pnpm install` exit non-zero.
|
|
38
|
+
- **Security overrides pruned 36 → 9.** Only entries still load-bearing (removing them
|
|
39
|
+
lets the package resolve back into its vulnerable range) were kept; `pnpm audit` on
|
|
40
|
+
pnpm 11 confirms the pruned set is still clean.
|
|
41
|
+
|
|
42
|
+
CI (`pnpm/action-setup`) and Docker (`corepack`) follow the `packageManager` field, so no
|
|
43
|
+
workflow or Dockerfile edits were needed — they use pnpm 11 automatically.
|
|
44
|
+
|
|
45
|
+
**If you develop against or vendor the framework's toolchain:** switch to pnpm 11
|
|
46
|
+
(`corepack use pnpm@11` or let corepack follow `packageManager`), and move any
|
|
47
|
+
`package.json#pnpm` settings you have into `pnpm-workspace.yaml`. See
|
|
48
|
+
`.claude/rules/package-management.md` for the full pattern.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quick Migration
|
|
53
|
+
|
|
54
|
+
No code changes required.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Update package
|
|
58
|
+
pnpm add @lenne.tech/nest-server@11.27.7
|
|
59
|
+
|
|
60
|
+
# Verify build
|
|
61
|
+
pnpm run build
|
|
62
|
+
|
|
63
|
+
# Run tests
|
|
64
|
+
pnpm test
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## What's Fixed in 11.27.7
|
|
70
|
+
|
|
71
|
+
### Startup crash under the SWC compiler
|
|
72
|
+
|
|
73
|
+
**The symptom.** With BetterAuth enabled and the app started or built via SWC:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
ReferenceError: Cannot access 'BETTER_AUTH_INSTANCE' before initialization
|
|
77
|
+
at core-better-auth.module.ts
|
|
78
|
+
at core-better-auth.service.ts
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This affected anyone using `nest start -b swc` / `nest build -b swc` — including
|
|
82
|
+
the `start:dev:swc` and `start:local:swc` scripts shipped in the starter. Projects
|
|
83
|
+
building with the default `tsc` builder were **not** affected.
|
|
84
|
+
|
|
85
|
+
**The bug.** The three BetterAuth dependency-injection tokens were split across two
|
|
86
|
+
files that imported each other:
|
|
87
|
+
|
|
88
|
+
- `BETTER_AUTH_INSTANCE` was declared in `core-better-auth.module.ts`
|
|
89
|
+
- `BETTER_AUTH_CONFIG` and `BETTER_AUTH_COOKIE_DOMAIN` were declared in `core-better-auth.service.ts`
|
|
90
|
+
|
|
91
|
+
The service imported the first token from the module, and the module imported the
|
|
92
|
+
other two — plus the service class itself — back from the service. A genuine import
|
|
93
|
+
cycle.
|
|
94
|
+
|
|
95
|
+
The cycle alone would have been survivable. What made it fatal is *when* the token
|
|
96
|
+
is read: `@Inject(BETTER_AUTH_INSTANCE)` is a **constructor-parameter decorator**,
|
|
97
|
+
and decorator arguments are evaluated when the class is *defined* — that is, while
|
|
98
|
+
the module is still initializing. On a cycle, the importing side reads a `const`
|
|
99
|
+
that is still in its temporal dead zone.
|
|
100
|
+
|
|
101
|
+
Under `tsc`/CommonJS the evaluation order happened to work out. Under SWC it did
|
|
102
|
+
not, and the app died at startup.
|
|
103
|
+
|
|
104
|
+
**The fix.** All three tokens moved into a new **import-free leaf module**,
|
|
105
|
+
`core-better-auth.constants.ts`. A file with zero imports can never be
|
|
106
|
+
mid-evaluation when someone imports it — in any module system, under any compiler —
|
|
107
|
+
so the initialization order is now deterministic everywhere.
|
|
108
|
+
|
|
109
|
+
A second leaf, `core-better-auth.registry.ts`, does the same for the
|
|
110
|
+
`BetterAuthTokenService` reference that `BetterAuthRolesGuard` looks up statically,
|
|
111
|
+
removing the last import cycle inside the module. As a side effect this also fixes a
|
|
112
|
+
test-isolation leak: `CoreBetterAuthModule.reset()` previously did not clear that
|
|
113
|
+
reference, so a token service could survive into the next testing module.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Deprecations
|
|
118
|
+
|
|
119
|
+
The tokens are still re-exported from their old locations, so **every existing import
|
|
120
|
+
keeps working**:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Still works (now @deprecated)
|
|
124
|
+
import { BETTER_AUTH_INSTANCE } from '@lenne.tech/nest-server/dist/core/modules/better-auth/core-better-auth.module';
|
|
125
|
+
import { BETTER_AUTH_CONFIG } from '@lenne.tech/nest-server/dist/core/modules/better-auth/core-better-auth.service';
|
|
126
|
+
|
|
127
|
+
// Preferred
|
|
128
|
+
import { BETTER_AUTH_CONFIG, BETTER_AUTH_INSTANCE } from '@lenne.tech/nest-server';
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Importing from the package root (`@lenne.tech/nest-server`) was already the
|
|
132
|
+
recommended path and is unaffected — **most projects need to change nothing.**
|
|
133
|
+
|
|
134
|
+
The deprecated re-exports will be removed in a future MINOR.
|
|
135
|
+
|
|
136
|
+
> **The token string values are unchanged** (`'BETTER_AUTH_INSTANCE'`,
|
|
137
|
+
> `'BETTER_AUTH_CONFIG'`, `'BETTER_AUTH_COOKIE_DOMAIN'`), so `@Inject('BETTER_AUTH_INSTANCE')`
|
|
138
|
+
> written as a string literal keeps resolving. A new unit test pins these values
|
|
139
|
+
> precisely because they are part of the public contract.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Vendor Mode: five atomic file sets
|
|
144
|
+
|
|
145
|
+
Projects that vendor the framework core (`projects/api/src/core/`) sync via the
|
|
146
|
+
`/lt-dev:backend:update-nest-server-core` agent rather than `pnpm update`.
|
|
147
|
+
|
|
148
|
+
Every change in this release works the same way: a symbol moved into a new **leaf**
|
|
149
|
+
file, and its old home now re-exports it. That makes each one an **atomic set** —
|
|
150
|
+
adopt the edits without the new file and you get an unresolvable import and a broken
|
|
151
|
+
build.
|
|
152
|
+
|
|
153
|
+
**Seven files are new.** If a sync is interrupted, verify all seven exist before
|
|
154
|
+
building:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
core/modules/better-auth/core-better-auth.constants.ts
|
|
158
|
+
core/modules/better-auth/core-better-auth.registry.ts
|
|
159
|
+
core/modules/ai/core-ai.constants.ts
|
|
160
|
+
core/modules/ai/interfaces/ai-interaction-record.interface.ts
|
|
161
|
+
core/modules/tus/tus.constants.ts
|
|
162
|
+
core/common/helpers/id.helper.ts
|
|
163
|
+
core/common/helpers/clone.helper.ts
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
> **Expect merge conflicts on locally-patched files.** If your project has local edits
|
|
167
|
+
> in `db.helper.ts`, `input.helper.ts`, `filter.input.ts`, `restricted.decorator.ts`
|
|
168
|
+
> or any `ai/services/*.service.ts`, this release moves code *out* of them — the sync
|
|
169
|
+
> will conflict there. The resolution is always the same: keep your local logic, keep
|
|
170
|
+
> the new `import` + `export … from` lines pointing at the leaf, and do not restore the
|
|
171
|
+
> declarations that moved out.
|
|
172
|
+
|
|
173
|
+
**Set 1 — BetterAuth DI tokens**
|
|
174
|
+
|
|
175
|
+
| File | Change |
|
|
176
|
+
|------|--------|
|
|
177
|
+
| `core/modules/better-auth/core-better-auth.constants.ts` | 🆕 the token leaf |
|
|
178
|
+
| `core/modules/better-auth/core-better-auth.registry.ts` | 🆕 the token-service leaf |
|
|
179
|
+
| `core/modules/better-auth/core-better-auth.module.ts` | takes tokens from the constants leaf; delegates the token-service lookup to the registry |
|
|
180
|
+
| `core/modules/better-auth/core-better-auth.service.ts` | takes tokens from the constants leaf |
|
|
181
|
+
| `core/modules/better-auth/better-auth-roles.guard.ts` | reads the registry instead of importing the module |
|
|
182
|
+
| `core/modules/better-auth/index.ts` | exports the constants leaf (the registry is deliberately **not** exported — it is module-internal) |
|
|
183
|
+
|
|
184
|
+
**Set 2 — core helpers** (this is what takes `restricted.decorator` off its cycles)
|
|
185
|
+
|
|
186
|
+
| File | Change |
|
|
187
|
+
|------|--------|
|
|
188
|
+
| `core/common/helpers/id.helper.ts` | 🆕 the ID cluster (`equalIds`, `getIncludedIds`, `getStringIds`, `getObjectIds`) |
|
|
189
|
+
| `core/common/helpers/clone.helper.ts` | 🆕 `clone`, `deepFreeze` |
|
|
190
|
+
| `core/common/helpers/db.helper.ts` | ID cluster removed; **re-exports it** from `id.helper` |
|
|
191
|
+
| `core/common/helpers/input.helper.ts` | `clone`/`deepFreeze` removed; **re-exports them** from `clone.helper`; takes `equalIds` from `id.helper` |
|
|
192
|
+
| `core/common/services/config.service.ts` | takes `clone`/`deepFreeze` from `clone.helper` |
|
|
193
|
+
| `core/common/decorators/restricted.decorator.ts` | takes the ID helpers from `id.helper`; its three exports become hoisted `function` declarations |
|
|
194
|
+
|
|
195
|
+
**Set 3 — filter inputs**
|
|
196
|
+
|
|
197
|
+
| File | Change |
|
|
198
|
+
|------|--------|
|
|
199
|
+
| `core/common/inputs/filter.input.ts` | now declares **both** `CombinedFilterInput` (first) and `FilterInput` — the order is load-bearing |
|
|
200
|
+
| `core/common/inputs/combined-filter.input.ts` | reduced to a re-export shim |
|
|
201
|
+
|
|
202
|
+
**Set 4 — AI module** (22 DI tokens + the type that closed the last AI cycle)
|
|
203
|
+
|
|
204
|
+
| File | Change |
|
|
205
|
+
|------|--------|
|
|
206
|
+
| `core/modules/ai/core-ai.constants.ts` | 🆕 all 22 `AI_*_MODEL` / `AI_*_CLASS` tokens |
|
|
207
|
+
| `core/modules/ai/interfaces/ai-interaction-record.interface.ts` | 🆕 `AiInteractionRecord` |
|
|
208
|
+
| `core/modules/ai/services/*.service.ts` (11 files) | tokens removed; each **re-exports its own** from the constants leaf |
|
|
209
|
+
| `core/modules/ai/services/core-ai.service.ts` | `AiInteractionRecord` removed; **re-exports it** from the interface leaf |
|
|
210
|
+
| `core/modules/ai/services/core-ai-interaction.service.ts` | takes the type from the interface leaf — no longer imports `core-ai.service` at all |
|
|
211
|
+
| `core/modules/ai/index.ts` | exports the interface leaf |
|
|
212
|
+
|
|
213
|
+
> If your project registered custom AI models via these tokens, nothing changes —
|
|
214
|
+
> the values are unchanged and every old import path still resolves.
|
|
215
|
+
|
|
216
|
+
**Set 5 — TUS module**
|
|
217
|
+
|
|
218
|
+
| File | Change |
|
|
219
|
+
|------|--------|
|
|
220
|
+
| `core/modules/tus/tus.constants.ts` | 🆕 `TUS_CONFIG` |
|
|
221
|
+
| `core/modules/tus/tus.module.ts` | token removed; **re-exports it** from the leaf |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
> ### Do not "clean up" the re-export lines
|
|
226
|
+
>
|
|
227
|
+
> `db.helper.ts`, `input.helper.ts`, `combined-filter.input.ts`, `core-better-auth.module.ts`,
|
|
228
|
+
> `core-better-auth.service.ts`, `tus.module.ts`, `core-ai.service.ts` and all eleven
|
|
229
|
+
> `ai/services/*.service.ts` files now carry `export { … } from './…'` lines that look
|
|
230
|
+
> exactly like dead code.
|
|
231
|
+
>
|
|
232
|
+
> They are not. They are the *entire* backward-compatibility layer: this package has no
|
|
233
|
+
> `exports` map, so consumers deep-import these files directly, and deleting one line
|
|
234
|
+
> breaks every one of them — **with nothing turning red**, because the symbol is still
|
|
235
|
+
> exported from the package root.
|
|
236
|
+
>
|
|
237
|
+
> The framework pins them with a test (`tests/unit/import-cycle-invariants.spec.ts`) for
|
|
238
|
+
> exactly this reason. Keep the equivalent lines in your vendored copy.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Also fixed: `CombinedFilterInput` crashed on a direct import
|
|
243
|
+
|
|
244
|
+
Same bug class, different module. `FilterInput` and `CombinedFilterInput` lived in
|
|
245
|
+
two files that imported each other, and `FilterInput` referenced
|
|
246
|
+
`CombinedFilterInput` **eagerly** — in a decorator argument and in the `design:type`
|
|
247
|
+
metadata `emitDecoratorMetadata` emits. Under SWC:
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
require('.../core/common/inputs/combined-filter.input.js')
|
|
251
|
+
→ ReferenceError: Cannot access 'CombinedFilterInput' before initialization
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This only stayed hidden because importing from the package root pulls `filter.input`
|
|
255
|
+
in first. A **deep import** of `combined-filter.input` — plausible in vendor mode or
|
|
256
|
+
in a unit test — hit the crash.
|
|
257
|
+
|
|
258
|
+
Both classes now live in `filter.input.ts` (declaration order matters), and
|
|
259
|
+
`combined-filter.input.ts` re-exports `CombinedFilterInput`. **Every existing import
|
|
260
|
+
path still resolves**, including deep imports of either file.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Also fixed (security): `Secure` could be stripped from session cookies
|
|
265
|
+
|
|
266
|
+
If a project sets `betterAuth.options.advanced.defaultCookieAttributes` — for cookie
|
|
267
|
+
partitioning, a custom domain, anything — that object **wholesale-replaced** the
|
|
268
|
+
framework's own `{ secure: true }`, because the merge was shallow. On an `https://`
|
|
269
|
+
baseURL the result was session cookies without the `Secure` attribute, forwarded
|
|
270
|
+
verbatim by BetterAuth's native handlers (2FA verify, social callback, magic link).
|
|
271
|
+
No error, no warning.
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
// Before 11.27.7 — on an https baseURL:
|
|
275
|
+
betterAuth: { options: { advanced: { defaultCookieAttributes: { partitioned: true } } } }
|
|
276
|
+
// → Set-Cookie: iam.session_token=…; HttpOnly; SameSite=Lax ← Secure GONE
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The merge is now deep for that one key: `secure: true` is the base and your keys
|
|
280
|
+
spread over it. An **explicit** `{ secure: false }` still wins — this guards against
|
|
281
|
+
silent clobbering, not against a deliberate choice.
|
|
282
|
+
|
|
283
|
+
**No action required.** If you set `defaultCookieAttributes` and run on HTTPS, the
|
|
284
|
+
fix applies automatically on update.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## New: SWC regression guard
|
|
289
|
+
|
|
290
|
+
The bug had the worst possible failure profile — **it passed a fully green CI and
|
|
291
|
+
crashed only for consumers.** `nest build` uses `tsc`, and vitest runs SWC through
|
|
292
|
+
Vite's module runner, whose getter-based live bindings tolerate import cycles. So
|
|
293
|
+
neither the build nor 2000+ tests could see it.
|
|
294
|
+
|
|
295
|
+
A new check step closes that hole:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
pnpm run check:swc-tdz
|
|
299
|
+
# = nest build -b swc -p tsconfig.swc-tdz.json && node scripts/check-swc-tdz.mjs
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
It compiles with SWC to CommonJS and loads **every compiled module as its own entry
|
|
303
|
+
point** under Node's CJS loader — the exact path that fails.
|
|
304
|
+
|
|
305
|
+
Loading only the package barrel is *not* sufficient, and that is not a theoretical
|
|
306
|
+
point: whether such a cycle throws depends on which module the graph is entered
|
|
307
|
+
through. The `CombinedFilterInput` crash above was invisible to a barrel-only load,
|
|
308
|
+
because the barrel happens to pull `filter.input` in first. Requiring each file
|
|
309
|
+
separately is what surfaces it.
|
|
310
|
+
|
|
311
|
+
It builds into its own throwaway directory (`dist-swc-tdz/`, removed on success,
|
|
312
|
+
kept on failure so you can inspect the emit), so it never touches your real `dist/`.
|
|
313
|
+
It is wired into `check:raw` — ahead of the test step, so a cycle fails in seconds
|
|
314
|
+
rather than after the full suite — and into the CI workflow.
|
|
315
|
+
|
|
316
|
+
### Copying it into your own project
|
|
317
|
+
|
|
318
|
+
**Worth doing if you build or run with SWC** (`nest start -b swc`, `start:dev:swc`):
|
|
319
|
+
it costs a few seconds, needs no database, and is the only thing that catches this
|
|
320
|
+
bug class.
|
|
321
|
+
|
|
322
|
+
Note that `scripts/` is **not** part of the published npm package, so you cannot
|
|
323
|
+
copy it out of `node_modules`. Take the three pieces from the repository:
|
|
324
|
+
|
|
325
|
+
- [`scripts/check-swc-tdz.mjs`](https://github.com/lenneTech/nest-server/blob/develop/scripts/check-swc-tdz.mjs)
|
|
326
|
+
- [`tsconfig.swc-tdz.json`](https://github.com/lenneTech/nest-server/blob/develop/tsconfig.swc-tdz.json)
|
|
327
|
+
- the `check:swc-tdz` script entry in `package.json`, plus `dist-swc-tdz` in `.gitignore`
|
|
328
|
+
|
|
329
|
+
Then add `pnpm run check:swc-tdz` to your own `check` chain (before the tests, so it
|
|
330
|
+
fails fast) and to CI.
|
|
331
|
+
|
|
332
|
+
> **Vendor-mode projects should treat this as required, not optional.** You copy
|
|
333
|
+
> `src/core/` into your own tree, where a local edit can re-introduce exactly this
|
|
334
|
+
> kind of cycle — and your `tsc` build and your test suite will both stay green while
|
|
335
|
+
> it does.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Compatibility Notes
|
|
340
|
+
|
|
341
|
+
| Pattern | Status |
|
|
342
|
+
|---------|--------|
|
|
343
|
+
| Importing tokens from `@lenne.tech/nest-server` (package root) | ✅ Unchanged |
|
|
344
|
+
| Deep-importing tokens from `core-better-auth.module` / `.service` | ✅ Works, now `@deprecated` |
|
|
345
|
+
| `@Inject('BETTER_AUTH_INSTANCE')` as a string literal | ✅ Unchanged (values pinned by test) |
|
|
346
|
+
| Custom `{ provide: BETTER_AUTH_INSTANCE, useValue: ... }` overrides | ✅ Unchanged |
|
|
347
|
+
| Extending `BetterAuthRolesGuard` | ✅ Unchanged public behavior |
|
|
348
|
+
| `CoreBetterAuthModule.getTokenServiceInstance()` | ✅ Unchanged (now delegates to the registry) |
|
|
349
|
+
| Building with `tsc` (default) | ✅ Was never affected |
|
|
350
|
+
| Building with SWC | ✅ **Fixed** |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Troubleshooting
|
|
355
|
+
|
|
356
|
+
**`ReferenceError: Cannot access 'BETTER_AUTH_INSTANCE' before initialization`**
|
|
357
|
+
You are still on ≤ 11.27.6 with an SWC build. Update to 11.27.7.
|
|
358
|
+
|
|
359
|
+
**Vendor mode: `Cannot find module './core-better-auth.constants'`**
|
|
360
|
+
The sync did not create the new leaf file(s). Re-run the core update and confirm
|
|
361
|
+
both `core-better-auth.constants.ts` and `core-better-auth.registry.ts` exist in
|
|
362
|
+
`src/core/modules/better-auth/`.
|
|
363
|
+
|
|
364
|
+
**Deprecation warnings on the token imports**
|
|
365
|
+
Expected. Switch the import to the package root (`@lenne.tech/nest-server`) or to
|
|
366
|
+
`core-better-auth.constants`. No behavior change either way.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Module Documentation
|
|
371
|
+
|
|
372
|
+
- [BetterAuth README](../src/core/modules/better-auth/README.md)
|
|
373
|
+
- [BetterAuth Integration Checklist](../src/core/modules/better-auth/INTEGRATION-CHECKLIST.md)
|
|
374
|
+
- [BetterAuth Customization](../src/core/modules/better-auth/CUSTOMIZATION.md)
|