@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.
Files changed (162) hide show
  1. package/.claude/rules/architecture.md +66 -0
  2. package/.claude/rules/better-auth.md +88 -2
  3. package/.claude/rules/package-management.md +23 -14
  4. package/.claude/rules/role-system.md +90 -0
  5. package/.claude/rules/versioning.md +17 -2
  6. package/CLAUDE.md +8 -2
  7. package/FRAMEWORK-API.md +27 -1
  8. package/dist/core/common/decorators/restricted.decorator.d.ts +4 -4
  9. package/dist/core/common/decorators/restricted.decorator.js +29 -26
  10. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  11. package/dist/core/common/exceptions/access-denied.exception.d.ts +4 -0
  12. package/dist/core/common/exceptions/access-denied.exception.js +12 -0
  13. package/dist/core/common/exceptions/access-denied.exception.js.map +1 -0
  14. package/dist/core/common/helpers/clone.helper.d.ts +6 -0
  15. package/dist/core/common/helpers/clone.helper.js +67 -0
  16. package/dist/core/common/helpers/clone.helper.js.map +1 -0
  17. package/dist/core/common/helpers/db.helper.d.ts +2 -14
  18. package/dist/core/common/helpers/db.helper.js +13 -106
  19. package/dist/core/common/helpers/db.helper.js.map +1 -1
  20. package/dist/core/common/helpers/id.helper.d.ts +15 -0
  21. package/dist/core/common/helpers/id.helper.js +104 -0
  22. package/dist/core/common/helpers/id.helper.js.map +1 -0
  23. package/dist/core/common/helpers/input.helper.d.ts +1 -6
  24. package/dist/core/common/helpers/input.helper.js +15 -72
  25. package/dist/core/common/helpers/input.helper.js.map +1 -1
  26. package/dist/core/common/helpers/service.helper.js +4 -3
  27. package/dist/core/common/helpers/service.helper.js.map +1 -1
  28. package/dist/core/common/inputs/combined-filter.input.d.ts +1 -12
  29. package/dist/core/common/inputs/combined-filter.input.js +2 -51
  30. package/dist/core/common/inputs/combined-filter.input.js.map +1 -1
  31. package/dist/core/common/inputs/filter.input.d.ts +10 -1
  32. package/dist/core/common/inputs/filter.input.js +40 -5
  33. package/dist/core/common/inputs/filter.input.js.map +1 -1
  34. package/dist/core/common/services/config.service.js +5 -5
  35. package/dist/core/common/services/config.service.js.map +1 -1
  36. package/dist/core/modules/ai/core-ai.constants.d.ts +22 -0
  37. package/dist/core/modules/ai/core-ai.constants.js +26 -0
  38. package/dist/core/modules/ai/core-ai.constants.js.map +1 -0
  39. package/dist/core/modules/ai/index.d.ts +1 -0
  40. package/dist/core/modules/ai/index.js +1 -0
  41. package/dist/core/modules/ai/index.js.map +1 -1
  42. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.d.ts +17 -0
  43. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js +3 -0
  44. package/dist/core/modules/ai/interfaces/ai-interaction-record.interface.js.map +1 -0
  45. package/dist/core/modules/ai/services/core-ai-budget.service.d.ts +1 -2
  46. package/dist/core/modules/ai/services/core-ai-budget.service.js +7 -5
  47. package/dist/core/modules/ai/services/core-ai-budget.service.js.map +1 -1
  48. package/dist/core/modules/ai/services/core-ai-connection-preference.service.d.ts +1 -2
  49. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js +7 -5
  50. package/dist/core/modules/ai/services/core-ai-connection-preference.service.js.map +1 -1
  51. package/dist/core/modules/ai/services/core-ai-connection.service.d.ts +1 -2
  52. package/dist/core/modules/ai/services/core-ai-connection.service.js +7 -5
  53. package/dist/core/modules/ai/services/core-ai-connection.service.js.map +1 -1
  54. package/dist/core/modules/ai/services/core-ai-conversation.service.d.ts +1 -2
  55. package/dist/core/modules/ai/services/core-ai-conversation.service.js +7 -5
  56. package/dist/core/modules/ai/services/core-ai-conversation.service.js.map +1 -1
  57. package/dist/core/modules/ai/services/core-ai-interaction.service.d.ts +2 -3
  58. package/dist/core/modules/ai/services/core-ai-interaction.service.js +7 -5
  59. package/dist/core/modules/ai/services/core-ai-interaction.service.js.map +1 -1
  60. package/dist/core/modules/ai/services/core-ai-mode.service.d.ts +1 -2
  61. package/dist/core/modules/ai/services/core-ai-mode.service.js +7 -5
  62. package/dist/core/modules/ai/services/core-ai-mode.service.js.map +1 -1
  63. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.d.ts +1 -3
  64. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js +7 -5
  65. package/dist/core/modules/ai/services/core-ai-prompt-hint.service.js.map +1 -1
  66. package/dist/core/modules/ai/services/core-ai-prompt.service.d.ts +1 -2
  67. package/dist/core/modules/ai/services/core-ai-prompt.service.js +7 -5
  68. package/dist/core/modules/ai/services/core-ai-prompt.service.js.map +1 -1
  69. package/dist/core/modules/ai/services/core-ai-slot.service.d.ts +1 -2
  70. package/dist/core/modules/ai/services/core-ai-slot.service.js +7 -5
  71. package/dist/core/modules/ai/services/core-ai-slot.service.js.map +1 -1
  72. package/dist/core/modules/ai/services/core-ai-tool-grant.service.d.ts +1 -2
  73. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js +7 -5
  74. package/dist/core/modules/ai/services/core-ai-tool-grant.service.js.map +1 -1
  75. package/dist/core/modules/ai/services/core-ai-tool-policy.service.d.ts +1 -2
  76. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js +7 -5
  77. package/dist/core/modules/ai/services/core-ai-tool-policy.service.js.map +1 -1
  78. package/dist/core/modules/ai/services/core-ai.service.d.ts +2 -17
  79. package/dist/core/modules/ai/services/core-ai.service.js.map +1 -1
  80. package/dist/core/modules/auth/guards/roles.guard.js +2 -2
  81. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  82. package/dist/core/modules/better-auth/better-auth-roles.guard.js +3 -3
  83. package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
  84. package/dist/core/modules/better-auth/better-auth.config.js +6 -0
  85. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  86. package/dist/core/modules/better-auth/core-better-auth.constants.d.ts +3 -0
  87. package/dist/core/modules/better-auth/core-better-auth.constants.js +7 -0
  88. package/dist/core/modules/better-auth/core-better-auth.constants.js.map +1 -0
  89. package/dist/core/modules/better-auth/core-better-auth.controller.js +1 -3
  90. package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
  91. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +1 -2
  92. package/dist/core/modules/better-auth/core-better-auth.module.js +23 -20
  93. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  94. package/dist/core/modules/better-auth/core-better-auth.registry.d.ts +4 -0
  95. package/dist/core/modules/better-auth/core-better-auth.registry.js +16 -0
  96. package/dist/core/modules/better-auth/core-better-auth.registry.js.map +1 -0
  97. package/dist/core/modules/better-auth/core-better-auth.service.d.ts +1 -2
  98. package/dist/core/modules/better-auth/core-better-auth.service.js +7 -6
  99. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  100. package/dist/core/modules/better-auth/index.d.ts +1 -0
  101. package/dist/core/modules/better-auth/index.js +1 -0
  102. package/dist/core/modules/better-auth/index.js.map +1 -1
  103. package/dist/core/modules/tenant/core-tenant-member.model.js +3 -3
  104. package/dist/core/modules/tenant/core-tenant-member.model.js.map +1 -1
  105. package/dist/core/modules/tenant/core-tenant.guard.js +7 -6
  106. package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
  107. package/dist/core/modules/tus/tus.constants.d.ts +1 -0
  108. package/dist/core/modules/tus/tus.constants.js +5 -0
  109. package/dist/core/modules/tus/tus.constants.js.map +1 -0
  110. package/dist/core/modules/tus/tus.module.d.ts +1 -1
  111. package/dist/core/modules/tus/tus.module.js +8 -6
  112. package/dist/core/modules/tus/tus.module.js.map +1 -1
  113. package/dist/index.d.ts +1 -0
  114. package/dist/index.js +1 -0
  115. package/dist/index.js.map +1 -1
  116. package/dist/server/modules/user/user.service.js +1 -1
  117. package/dist/server/modules/user/user.service.js.map +1 -1
  118. package/dist/tsconfig.build.tsbuildinfo +1 -1
  119. package/docs/REQUEST-LIFECYCLE.md +39 -0
  120. package/migration-guides/11.27.6-to-11.27.7.md +374 -0
  121. package/migration-guides/11.27.7-to-11.28.0.md +350 -0
  122. package/package.json +6 -104
  123. package/src/core/common/decorators/restricted.decorator.ts +67 -20
  124. package/src/core/common/exceptions/access-denied.exception.ts +49 -0
  125. package/src/core/common/helpers/clone.helper.ts +110 -0
  126. package/src/core/common/helpers/db.helper.ts +14 -161
  127. package/src/core/common/helpers/id.helper.ts +198 -0
  128. package/src/core/common/helpers/input.helper.ts +21 -88
  129. package/src/core/common/helpers/service.helper.ts +6 -4
  130. package/src/core/common/inputs/combined-filter.input.ts +10 -57
  131. package/src/core/common/inputs/filter.input.ts +92 -1
  132. package/src/core/common/services/config.service.ts +4 -1
  133. package/src/core/modules/ai/core-ai.constants.ts +92 -0
  134. package/src/core/modules/ai/index.ts +1 -0
  135. package/src/core/modules/ai/interfaces/ai-interaction-record.interface.ts +34 -0
  136. package/src/core/modules/ai/services/core-ai-budget.service.ts +8 -4
  137. package/src/core/modules/ai/services/core-ai-connection-preference.service.ts +8 -4
  138. package/src/core/modules/ai/services/core-ai-connection.service.ts +5 -6
  139. package/src/core/modules/ai/services/core-ai-conversation.service.ts +5 -6
  140. package/src/core/modules/ai/services/core-ai-interaction.service.ts +6 -7
  141. package/src/core/modules/ai/services/core-ai-mode.service.ts +8 -2
  142. package/src/core/modules/ai/services/core-ai-prompt-hint.service.ts +7 -4
  143. package/src/core/modules/ai/services/core-ai-prompt.service.ts +8 -2
  144. package/src/core/modules/ai/services/core-ai-slot.service.ts +7 -4
  145. package/src/core/modules/ai/services/core-ai-tool-grant.service.ts +8 -4
  146. package/src/core/modules/ai/services/core-ai-tool-policy.service.ts +8 -4
  147. package/src/core/modules/ai/services/core-ai.service.ts +8 -11
  148. package/src/core/modules/auth/guards/roles.guard.ts +5 -4
  149. package/src/core/modules/better-auth/better-auth-roles.guard.ts +15 -7
  150. package/src/core/modules/better-auth/better-auth.config.ts +16 -0
  151. package/src/core/modules/better-auth/core-better-auth.constants.ts +73 -0
  152. package/src/core/modules/better-auth/core-better-auth.controller.ts +5 -6
  153. package/src/core/modules/better-auth/core-better-auth.module.ts +34 -8
  154. package/src/core/modules/better-auth/core-better-auth.registry.ts +53 -0
  155. package/src/core/modules/better-auth/core-better-auth.service.ts +19 -12
  156. package/src/core/modules/better-auth/index.ts +9 -0
  157. package/src/core/modules/tenant/core-tenant-member.model.ts +8 -3
  158. package/src/core/modules/tenant/core-tenant.guard.ts +26 -11
  159. package/src/core/modules/tus/tus.constants.ts +25 -0
  160. package/src/core/modules/tus/tus.module.ts +7 -2
  161. package/src/index.ts +1 -0
  162. 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)