@lenne.tech/nest-server 11.21.3 → 11.22.1

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 (57) hide show
  1. package/.claude/rules/architecture.md +79 -0
  2. package/.claude/rules/better-auth.md +262 -0
  3. package/.claude/rules/configurable-features.md +308 -0
  4. package/.claude/rules/core-modules.md +205 -0
  5. package/.claude/rules/framework-compatibility.md +79 -0
  6. package/.claude/rules/migration-guides.md +149 -0
  7. package/.claude/rules/module-deprecation.md +214 -0
  8. package/.claude/rules/module-inheritance.md +97 -0
  9. package/.claude/rules/package-management.md +112 -0
  10. package/.claude/rules/role-system.md +146 -0
  11. package/.claude/rules/testing.md +120 -0
  12. package/.claude/rules/versioning.md +53 -0
  13. package/CLAUDE.md +174 -0
  14. package/FRAMEWORK-API.md +231 -0
  15. package/dist/core/common/interfaces/server-options.interface.d.ts +10 -0
  16. package/dist/core/modules/error-code/error-code.module.js.map +1 -1
  17. package/dist/core.module.d.ts +3 -3
  18. package/dist/core.module.js +17 -4
  19. package/dist/core.module.js.map +1 -1
  20. package/dist/server/modules/file/file-info.model.d.ts +1 -5
  21. package/dist/server/modules/user/user.model.d.ts +1 -5
  22. package/dist/server/server.module.js +6 -6
  23. package/dist/server/server.module.js.map +1 -1
  24. package/dist/tsconfig.build.tsbuildinfo +1 -1
  25. package/docs/REQUEST-LIFECYCLE.md +1256 -0
  26. package/docs/error-codes.md +446 -0
  27. package/migration-guides/11.10.x-to-11.11.x.md +266 -0
  28. package/migration-guides/11.11.x-to-11.12.x.md +323 -0
  29. package/migration-guides/11.12.x-to-11.13.0.md +612 -0
  30. package/migration-guides/11.13.x-to-11.14.0.md +348 -0
  31. package/migration-guides/11.14.x-to-11.15.0.md +262 -0
  32. package/migration-guides/11.15.0-to-11.15.3.md +118 -0
  33. package/migration-guides/11.15.x-to-11.16.0.md +497 -0
  34. package/migration-guides/11.16.x-to-11.17.0.md +130 -0
  35. package/migration-guides/11.17.x-to-11.18.0.md +393 -0
  36. package/migration-guides/11.18.x-to-11.19.0.md +151 -0
  37. package/migration-guides/11.19.x-to-11.20.0.md +170 -0
  38. package/migration-guides/11.20.x-to-11.21.0.md +216 -0
  39. package/migration-guides/11.21.0-to-11.21.1.md +194 -0
  40. package/migration-guides/11.21.1-to-11.21.2.md +114 -0
  41. package/migration-guides/11.21.2-to-11.21.3.md +175 -0
  42. package/migration-guides/11.21.x-to-11.22.0.md +224 -0
  43. package/migration-guides/11.22.0-to-11.22.1.md +105 -0
  44. package/migration-guides/11.3.x-to-11.4.x.md +233 -0
  45. package/migration-guides/11.6.x-to-11.7.x.md +394 -0
  46. package/migration-guides/11.7.x-to-11.8.x.md +318 -0
  47. package/migration-guides/11.8.x-to-11.9.x.md +322 -0
  48. package/migration-guides/11.9.x-to-11.10.x.md +571 -0
  49. package/migration-guides/TEMPLATE.md +113 -0
  50. package/package.json +25 -18
  51. package/src/core/common/interfaces/server-options.interface.ts +83 -16
  52. package/src/core/modules/better-auth/CUSTOMIZATION.md +24 -17
  53. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +5 -5
  54. package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +42 -12
  55. package/src/core/modules/error-code/error-code.module.ts +4 -9
  56. package/src/core.module.ts +52 -10
  57. package/src/server/server.module.ts +7 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.21.3",
3
+ "version": "11.22.1",
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",
@@ -14,7 +14,8 @@
14
14
  "homepage": "https://github.com/lenneTech/nest-server",
15
15
  "license": "MIT",
16
16
  "scripts": {
17
- "build": "rimraf dist && nest build && pnpm run build:copy-types && pnpm run build:copy-templates && pnpm run build:add-type-references",
17
+ "build": "rimraf dist && nest build && pnpm run build:copy-types && pnpm run build:copy-templates && pnpm run build:add-type-references && pnpm run build:framework-api",
18
+ "build:framework-api": "npx tsx scripts/generate-framework-api.ts",
18
19
  "build:copy-types": "mkdir -p dist/types && cp src/types/*.d.ts dist/types/",
19
20
  "build:copy-templates": "mkdir -p dist/core/modules/migrate/templates && cp src/core/modules/migrate/templates/migration-project.template.ts dist/core/modules/migrate/templates/",
20
21
  "build:add-type-references": "node scripts/add-type-references.js",
@@ -78,17 +79,17 @@
78
79
  "@better-auth/passkey": "1.5.5",
79
80
  "@getbrevo/brevo": "3.0.1",
80
81
  "@nestjs/apollo": "13.2.4",
81
- "@nestjs/common": "11.1.17",
82
- "@nestjs/core": "11.1.17",
82
+ "@nestjs/common": "11.1.18",
83
+ "@nestjs/core": "11.1.18",
83
84
  "@nestjs/graphql": "13.2.4",
84
85
  "@nestjs/jwt": "11.0.2",
85
86
  "@nestjs/mongoose": "11.0.4",
86
87
  "@nestjs/passport": "11.0.5",
87
- "@nestjs/platform-express": "11.1.17",
88
+ "@nestjs/platform-express": "11.1.18",
88
89
  "@nestjs/schedule": "6.1.1",
89
90
  "@nestjs/swagger": "11.2.6",
90
91
  "@nestjs/terminus": "11.1.1",
91
- "@nestjs/websockets": "11.1.17",
92
+ "@nestjs/websockets": "11.1.18",
92
93
  "@tus/file-store": "2.0.0",
93
94
  "@tus/server": "2.3.0",
94
95
  "bcrypt": "6.0.0",
@@ -108,7 +109,7 @@
108
109
  "json-to-graphql-query": "2.3.0",
109
110
  "lodash": "4.18.1",
110
111
  "mongodb": "7.1.1",
111
- "mongoose": "9.3.3",
112
+ "mongoose": "9.4.1",
112
113
  "multer": "2.1.1",
113
114
  "node-mailjet": "6.0.11",
114
115
  "nodemailer": "8.0.4",
@@ -124,16 +125,16 @@
124
125
  "@compodoc/compodoc": "1.2.1",
125
126
  "@nestjs/cli": "11.0.17",
126
127
  "@nestjs/schematics": "11.0.10",
127
- "@nestjs/testing": "11.1.17",
128
+ "@nestjs/testing": "11.1.18",
128
129
  "@swc/cli": "0.8.1",
129
- "@swc/core": "1.15.21",
130
+ "@swc/core": "1.15.24",
130
131
  "@types/compression": "1.8.1",
131
132
  "@types/cookie-parser": "1.4.10",
132
133
  "@types/ejs": "3.1.5",
133
134
  "@types/express": "5.0.6",
134
135
  "@types/lodash": "4.17.24",
135
136
  "@types/multer": "2.1.0",
136
- "@types/node": "25.5.0",
137
+ "@types/node": "25.5.2",
137
138
  "@types/nodemailer": "7.0.11",
138
139
  "@types/passport": "1.0.17",
139
140
  "@types/supertest": "7.2.0",
@@ -168,7 +169,12 @@
168
169
  "files": [
169
170
  "dist/**/*",
170
171
  "src/**/*",
171
- "bin/**/*"
172
+ "bin/**/*",
173
+ "CLAUDE.md",
174
+ "FRAMEWORK-API.md",
175
+ ".claude/rules/**/*",
176
+ "docs/**/*",
177
+ "migration-guides/**/*"
172
178
  ],
173
179
  "watch": {
174
180
  "build:dev": "src"
@@ -176,22 +182,23 @@
176
182
  "packageManager": "pnpm@10.29.2",
177
183
  "pnpm": {
178
184
  "overrides": {
179
- "minimatch@<3.1.4": "3.1.4",
180
- "minimatch@>=9.0.0 <9.0.7": "9.0.7",
181
- "minimatch@>=10.0.0 <10.2.3": "10.2.4",
185
+ "minimatch@<3.1.5": "3.1.5",
186
+ "minimatch@>=9.0.0 <9.0.9": "9.0.9",
187
+ "minimatch@>=10.0.0 <10.2.5": "10.2.5",
182
188
  "rollup@>=4.0.0 <4.60.1": "4.60.1",
183
189
  "ajv@<6.14.0": "6.14.0",
184
190
  "ajv@>=7.0.0-alpha.0 <8.18.0": "8.18.0",
185
- "undici@>=7.0.0 <7.24.0": "7.24.3",
186
- "srvx@<0.11.13": "0.11.13",
191
+ "undici@>=7.0.0 <7.24.7": "7.24.7",
192
+ "srvx@<0.11.15": "0.11.15",
187
193
  "handlebars@>=4.0.0 <4.7.9": "4.7.9",
188
194
  "brace-expansion@<1.1.13": "1.1.13",
189
195
  "brace-expansion@>=4.0.0 <5.0.5": "5.0.5",
190
196
  "picomatch@<2.3.2": "2.3.2",
191
197
  "picomatch@>=4.0.0 <4.0.4": "4.0.4",
192
- "path-to-regexp@>=8.0.0 <8.4.0": "8.4.1",
198
+ "path-to-regexp@>=8.0.0 <8.4.2": "8.4.2",
193
199
  "kysely@>=0.26.0 <0.28.15": "0.28.15",
194
- "lodash@>=4.0.0 <4.18.0": "4.18.1"
200
+ "lodash@>=4.0.0 <4.18.0": "4.18.1",
201
+ "defu@<=6.1.4": "6.1.6"
195
202
  },
196
203
  "onlyBuiltDependencies": [
197
204
  "bcrypt",
@@ -2117,16 +2117,12 @@ interface IBetterAuthBase {
2117
2117
  * Custom controller class to use instead of the default CoreBetterAuthController.
2118
2118
  * The class should extend CoreBetterAuthController.
2119
2119
  *
2120
- * This allows projects to customize REST endpoints via config instead of creating
2121
- * a separate module. Use this with CoreModule.forRoot(envConfig) (IAM-only mode).
2122
- *
2123
- * @example
2120
+ * @deprecated Since 11.22.0 Use the `overrides` parameter on `CoreModule.forRoot()` instead:
2124
2121
  * ```typescript
2125
- * // config.env.ts
2126
- * betterAuth: {
2127
- * controller: IamController,
2128
- * }
2122
+ * CoreModule.forRoot(envConfig, { betterAuth: { controller: IamController } })
2129
2123
  * ```
2124
+ * This separates class references from environment configuration. The config field
2125
+ * still works for backward compatibility but `overrides` takes precedence.
2130
2126
  *
2131
2127
  * @since 11.14.0
2132
2128
  */
@@ -2342,16 +2338,12 @@ interface IBetterAuthBase {
2342
2338
  * Custom resolver class to use instead of the default DefaultBetterAuthResolver.
2343
2339
  * The class should extend CoreBetterAuthResolver.
2344
2340
  *
2345
- * This allows projects to customize GraphQL operations via config instead of creating
2346
- * a separate module. Use this with CoreModule.forRoot(envConfig) (IAM-only mode).
2347
- *
2348
- * @example
2341
+ * @deprecated Since 11.22.0 Use the `overrides` parameter on `CoreModule.forRoot()` instead:
2349
2342
  * ```typescript
2350
- * // config.env.ts
2351
- * betterAuth: {
2352
- * resolver: IamResolver,
2353
- * }
2343
+ * CoreModule.forRoot(envConfig, { betterAuth: { resolver: IamResolver } })
2354
2344
  * ```
2345
+ * This separates class references from environment configuration. The config field
2346
+ * still works for backward compatibility but `overrides` takes precedence.
2355
2347
  *
2356
2348
  * @since 11.14.0
2357
2349
  */
@@ -2663,3 +2655,78 @@ interface IBetterAuthWithPasskey extends IBetterAuthBase {
2663
2655
  */
2664
2656
  trustedOrigins: string[];
2665
2657
  }
2658
+
2659
+ /**
2660
+ * Override default implementations of core module components.
2661
+ *
2662
+ * Use this as the second parameter of `CoreModule.forRoot()` to replace
2663
+ * default controllers, resolvers, or services with project-specific implementations.
2664
+ *
2665
+ * This keeps class references (code) separate from environment configuration (strings/numbers)
2666
+ * and ensures each module's `forRoot()` is called exactly once — preventing duplicate
2667
+ * controller registration.
2668
+ *
2669
+ * Each core module that supports customization has a typed entry here.
2670
+ * Only specified components are overridden — unset fields use defaults.
2671
+ *
2672
+ * @example
2673
+ * ```typescript
2674
+ * // IAM-only mode with overrides
2675
+ * CoreModule.forRoot(envConfig, {
2676
+ * errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
2677
+ * betterAuth: { resolver: BetterAuthResolver },
2678
+ * })
2679
+ *
2680
+ * // Legacy mode with overrides
2681
+ * CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig, {
2682
+ * errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
2683
+ * betterAuth: { controller: BetterAuthController, resolver: BetterAuthResolver },
2684
+ * })
2685
+ * ```
2686
+ *
2687
+ * @since 11.22.0
2688
+ */
2689
+ export interface ICoreModuleOverrides {
2690
+ /**
2691
+ * Override BetterAuth controller and/or resolver.
2692
+ *
2693
+ * The custom controller must extend `CoreBetterAuthController`.
2694
+ * The custom resolver must extend `CoreBetterAuthResolver` and re-declare
2695
+ * all GraphQL decorators (`@Mutation`, `@Query`, `@Roles`).
2696
+ *
2697
+ * @example
2698
+ * ```typescript
2699
+ * {
2700
+ * betterAuth: {
2701
+ * controller: BetterAuthController,
2702
+ * resolver: BetterAuthResolver,
2703
+ * },
2704
+ * }
2705
+ * ```
2706
+ */
2707
+ betterAuth?: {
2708
+ controller?: Type<any>;
2709
+ resolver?: Type<any>;
2710
+ };
2711
+
2712
+ /**
2713
+ * Override ErrorCode controller and/or service.
2714
+ *
2715
+ * The custom controller can be standalone (recommended) or extend `CoreErrorCodeController`.
2716
+ * The custom service must extend `CoreErrorCodeService`.
2717
+ *
2718
+ * @example
2719
+ * ```typescript
2720
+ * {
2721
+ * errorCode: {
2722
+ * controller: ErrorCodeController,
2723
+ * service: ErrorCodeService,
2724
+ * },
2725
+ * }
2726
+ * ```
2727
+ */
2728
+ errorCode?: {
2729
+ controller?: Type<any>;
2730
+ service?: Type<any>;
2731
+ };
2732
+ }
@@ -41,29 +41,23 @@ export class ServerModule {}
41
41
  - Default `CoreBetterAuthController` and `DefaultBetterAuthResolver` are registered
42
42
  - No additional configuration needed
43
43
 
44
- ### Pattern 2: Config-based Controller/Resolver (Recommended for Customization)
44
+ ### Pattern 2: Overrides Parameter (Recommended for Customization)
45
45
 
46
46
  **Use when:** Need custom Controller or Resolver, but don't need a separate module.
47
47
 
48
- ```typescript
49
- // config.env.ts
50
- import { IamController } from './server/modules/iam/iam.controller';
51
- import { IamResolver } from './server/modules/iam/iam.resolver';
52
-
53
- const config = {
54
- betterAuth: {
55
- controller: IamController, // Custom controller class
56
- resolver: IamResolver, // Custom resolver class
57
- // ... other betterAuth config
58
- },
59
- };
60
- ```
61
-
62
48
  ```typescript
63
49
  // server.module.ts
50
+ import { IamController } from './modules/iam/iam.controller';
51
+ import { IamResolver } from './modules/iam/iam.resolver';
52
+
64
53
  @Module({
65
54
  imports: [
66
- CoreModule.forRoot(envConfig), // Uses custom controller/resolver from config
55
+ CoreModule.forRoot(envConfig, {
56
+ betterAuth: {
57
+ controller: IamController,
58
+ resolver: IamResolver,
59
+ },
60
+ }),
67
61
  ],
68
62
  })
69
63
  export class ServerModule {}
@@ -71,9 +65,22 @@ export class ServerModule {}
71
65
 
72
66
  **What happens:**
73
67
 
74
- - CoreModule passes `controller`/`resolver` to `CoreBetterAuthModule.forRoot()`
68
+ - CoreModule passes overrides to `CoreBetterAuthModule.forRoot()`
75
69
  - Your custom classes are registered instead of defaults
76
70
  - Single registration point, no duplicate imports
71
+ - Class references stay separate from environment config
72
+
73
+ **Alternative:** Set `controller`/`resolver` directly in config (backward compatible):
74
+
75
+ ```typescript
76
+ // config.env.ts — still works but overrides parameter is preferred
77
+ const config = {
78
+ betterAuth: {
79
+ controller: IamController,
80
+ resolver: IamResolver,
81
+ },
82
+ };
83
+ ```
77
84
 
78
85
  ### Pattern 3: Separate Module (autoRegister: false)
79
86
 
@@ -23,11 +23,11 @@
23
23
 
24
24
  ### Registration Patterns (Quick Reference)
25
25
 
26
- | Pattern | Use When | Configuration |
27
- | ------------------- | ---------------------------------- | -------------------------------------------------- |
28
- | **Zero-Config** | No customization needed | Just use `CoreModule.forRoot(envConfig)` |
29
- | **Config-based** | Custom Controller/Resolver | Add `controller`/`resolver` to `betterAuth` config |
30
- | **Separate Module** | Full control, additional providers | Set `autoRegister: false` in config |
26
+ | Pattern | Use When | Configuration |
27
+ | --------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------- |
28
+ | **Zero-Config** | No customization needed | Just use `CoreModule.forRoot(envConfig)` |
29
+ | **Overrides** (recommended) | Custom Controller/Resolver | Pass `overrides` to `CoreModule.forRoot(envConfig, { betterAuth: { controller, resolver } })` |
30
+ | **Separate Module** | Full control, additional providers | Set `autoRegister: false` in config |
31
31
 
32
32
  **Details:** See [CUSTOMIZATION.md](./CUSTOMIZATION.md#module-registration-patterns)
33
33
 
@@ -141,16 +141,15 @@ NestJS @Global() modules use "first wins" for provider registration. Without thi
141
141
 
142
142
  **Update:** `src/server/server.module.ts`
143
143
 
144
+ Use the `overrides` parameter of `CoreModule.forRoot()` (recommended since v11.22.0):
145
+
144
146
  ```typescript
145
- import { ErrorCodeModule as CoreErrorCodeModule } from '@lenne.tech/nest-server';
146
147
  import { ErrorCodeService } from './modules/error-code/error-code.service';
147
148
 
148
149
  @Module({
149
150
  imports: [
150
- CoreModule.forRoot(...),
151
- // Register with custom service
152
- CoreErrorCodeModule.forRoot({
153
- service: ErrorCodeService,
151
+ CoreModule.forRoot(envConfig, {
152
+ errorCode: { service: ErrorCodeService },
154
153
  }),
155
154
  // ... other modules
156
155
  ],
@@ -158,6 +157,22 @@ import { ErrorCodeService } from './modules/error-code/error-code.service';
158
157
  export class ServerModule {}
159
158
  ```
160
159
 
160
+ **Alternative** (for complex setups): disable auto-registration and import separately:
161
+
162
+ ```typescript
163
+ import { ErrorCodeModule as CoreErrorCodeModule } from '@lenne.tech/nest-server';
164
+ import { ErrorCodeService } from './modules/error-code/error-code.service';
165
+
166
+ @Module({
167
+ imports: [
168
+ CoreModule.forRoot({ ...envConfig, errorCode: { autoRegister: false } }),
169
+ CoreErrorCodeModule.forRoot({ service: ErrorCodeService }),
170
+ // ... other modules
171
+ ],
172
+ })
173
+ export class ServerModule {}
174
+ ```
175
+
161
176
  ---
162
177
 
163
178
  ## Scenario C: Custom Controller (For Custom Routes)
@@ -168,7 +183,7 @@ Use this when you need:
168
183
  - Different route paths
169
184
  - Additional REST endpoints
170
185
 
171
- **No custom module needed!** Use Core `ErrorCodeModule.forRoot()` with your custom controller and service.
186
+ **No custom module needed!** Use the `overrides` parameter of `CoreModule.forRoot()`.
172
187
 
173
188
  ### 1. Create Files
174
189
 
@@ -183,13 +198,29 @@ Files needed:
183
198
 
184
199
  **No `error-code.module.ts` needed!**
185
200
 
186
- ### 2. Disable Auto-Registration
201
+ ### 2. Register via CoreModule.forRoot() Overrides (Recommended)
187
202
 
188
- Same as Scenario B, Step 3.
203
+ **Update:** `src/server/server.module.ts`
189
204
 
190
- ### 3. Register via Core ErrorCodeModule
205
+ ```typescript
206
+ import { ErrorCodeController } from './modules/error-code/error-code.controller';
207
+ import { ErrorCodeService } from './modules/error-code/error-code.service';
191
208
 
192
- **Update:** `src/server/server.module.ts`
209
+ @Module({
210
+ imports: [
211
+ CoreModule.forRoot(envConfig, {
212
+ errorCode: {
213
+ controller: ErrorCodeController,
214
+ service: ErrorCodeService,
215
+ },
216
+ }),
217
+ // ... other modules
218
+ ],
219
+ })
220
+ export class ServerModule {}
221
+ ```
222
+
223
+ **Alternative** (for complex setups): disable auto-registration and import separately:
193
224
 
194
225
  ```typescript
195
226
  import { ErrorCodeModule } from '@lenne.tech/nest-server';
@@ -198,8 +229,7 @@ import { ErrorCodeService } from './modules/error-code/error-code.service';
198
229
 
199
230
  @Module({
200
231
  imports: [
201
- CoreModule.forRoot(...),
202
- // Use Core ErrorCodeModule with custom service and controller
232
+ CoreModule.forRoot({ ...envConfig, errorCode: { autoRegister: false } }),
203
233
  ErrorCodeModule.forRoot({
204
234
  controller: ErrorCodeController,
205
235
  service: ErrorCodeService,
@@ -12,8 +12,10 @@ import { IErrorCodeModuleConfig } from './interfaces/error-code.interfaces';
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * // Basic usage (auto-register in CoreModule)
16
- * // No explicit import needed - included in CoreModule
15
+ * // Basic usage (auto-register in CoreModule via overrides)
16
+ * CoreModule.forRoot(envConfig, {
17
+ * errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
18
+ * })
17
19
  *
18
20
  * // Extended usage (with custom error registry - RECOMMENDED)
19
21
  * const ProjectErrors = {
@@ -25,13 +27,6 @@ import { IErrorCodeModuleConfig } from './interfaces/error-code.interfaces';
25
27
  * } as const satisfies IErrorRegistry;
26
28
  *
27
29
  * ErrorCodeModule.forRoot({ additionalErrorRegistry: ProjectErrors })
28
- *
29
- * // Extended usage (with custom controller and service)
30
- * ErrorCodeModule.forRoot({
31
- * additionalErrorRegistry: ProjectErrors,
32
- * controller: ErrorCodeController,
33
- * service: ErrorCodeService,
34
- * })
35
30
  * ```
36
31
  */
37
32
  @Global()
@@ -12,7 +12,7 @@ import { CheckResponseInterceptor } from './core/common/interceptors/check-respo
12
12
  import { CheckSecurityInterceptor } from './core/common/interceptors/check-security.interceptor';
13
13
  import { ResponseModelInterceptor } from './core/common/interceptors/response-model.interceptor';
14
14
  import { TranslateResponseInterceptor } from './core/common/interceptors/translate-response.interceptor';
15
- import { IServerOptions } from './core/common/interfaces/server-options.interface';
15
+ import { ICoreModuleOverrides, IServerOptions } from './core/common/interfaces/server-options.interface';
16
16
  import { RequestContextMiddleware } from './core/common/middleware/request-context.middleware';
17
17
  import { MapAndValidatePipe } from './core/common/pipes/map-and-validate.pipe';
18
18
  import { ComplexityPlugin } from './core/common/plugins/complexity.plugin';
@@ -95,6 +95,12 @@ export class CoreModule implements NestModule {
95
95
  *
96
96
  * ```typescript
97
97
  * CoreModule.forRoot(envConfig)
98
+ *
99
+ * // With module overrides (custom controllers/resolvers/services)
100
+ * CoreModule.forRoot(envConfig, {
101
+ * errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
102
+ * betterAuth: { resolver: BetterAuthResolver },
103
+ * })
98
104
  * ```
99
105
  *
100
106
  * Use this for new projects that only use BetterAuth (IAM) for authentication.
@@ -109,6 +115,11 @@ export class CoreModule implements NestModule {
109
115
  *
110
116
  * ```typescript
111
117
  * CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig)
118
+ *
119
+ * // With module overrides
120
+ * CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig, {
121
+ * errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
122
+ * })
112
123
  * ```
113
124
  *
114
125
  * @deprecated This 3-parameter signature is deprecated for new projects.
@@ -127,22 +138,36 @@ export class CoreModule implements NestModule {
127
138
  *
128
139
  * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
129
140
  */
130
- static forRoot(options: Partial<IServerOptions>): DynamicModule;
141
+ static forRoot(options: Partial<IServerOptions>, overrides?: ICoreModuleOverrides): DynamicModule;
131
142
  /**
132
143
  * @deprecated Use the single-parameter signature `CoreModule.forRoot(envConfig)` for new projects.
133
144
  * This 3-parameter signature is for existing projects during migration to IAM.
134
145
  */
135
- static forRoot(AuthService: any, AuthModule: any, options: Partial<IServerOptions>): DynamicModule;
146
+ static forRoot(
147
+ AuthService: any,
148
+ AuthModule: any,
149
+ options: Partial<IServerOptions>,
150
+ overrides?: ICoreModuleOverrides,
151
+ ): DynamicModule;
136
152
  static forRoot(
137
153
  authServiceOrOptions: any,
138
154
  authModuleOrUndefined?: any,
139
155
  optionsOrUndefined?: Partial<IServerOptions>,
156
+ overridesOrUndefined?: ICoreModuleOverrides,
140
157
  ): DynamicModule {
141
- // Detect which signature was used
142
- const isIamOnlyMode = authModuleOrUndefined === undefined && optionsOrUndefined === undefined;
158
+ // Detect which signature was used:
159
+ // IAM-only: forRoot(config, overrides?) first arg is a plain object (config)
160
+ // Legacy: forRoot(AuthService, AuthModule, config, overrides?) — first arg is a class (function)
161
+ const isIamOnlyMode = typeof authServiceOrOptions !== 'function';
143
162
  const AuthService = isIamOnlyMode ? null : authServiceOrOptions;
144
163
  const AuthModule = isIamOnlyMode ? null : authModuleOrUndefined;
145
164
  const options: Partial<IServerOptions> = isIamOnlyMode ? authServiceOrOptions : optionsOrUndefined;
165
+ // For IAM-only mode: overrides is the 2nd param; for legacy mode: it's the 4th param.
166
+ // The cast is safe: the public overloads guarantee the 2nd arg is ICoreModuleOverrides | undefined
167
+ // in IAM-only mode (typeof first arg !== 'function'), never a DynamicModule (AuthModule).
168
+ const overrides: ICoreModuleOverrides | undefined = isIamOnlyMode
169
+ ? (authModuleOrUndefined as ICoreModuleOverrides | undefined)
170
+ : overridesOrUndefined;
146
171
 
147
172
  // Process config
148
173
  let cors = {};
@@ -316,11 +341,21 @@ export class CoreModule implements NestModule {
316
341
  const errorCodeConfig = config.errorCode;
317
342
  const isErrorCodeAutoRegister = errorCodeConfig?.autoRegister !== false;
318
343
 
344
+ if (!isErrorCodeAutoRegister && (overrides?.errorCode?.controller || overrides?.errorCode?.service)) {
345
+ console.warn(
346
+ 'CoreModule: errorCode overrides are ignored because errorCode.autoRegister is false. ' +
347
+ 'Either remove autoRegister: false or pass controller/service to your own ErrorCodeModule.forRoot() call.',
348
+ );
349
+ }
350
+
319
351
  if (isErrorCodeAutoRegister) {
320
352
  // Always use forRoot() - it registers the controller and handles configuration
353
+ // Overrides take precedence over config for controller/service
321
354
  imports.push(
322
355
  ErrorCodeModule.forRoot({
323
356
  additionalErrorRegistry: errorCodeConfig?.additionalErrorRegistry,
357
+ controller: overrides?.errorCode?.controller,
358
+ service: overrides?.errorCode?.service,
324
359
  }),
325
360
  );
326
361
  }
@@ -357,24 +392,31 @@ export class CoreModule implements NestModule {
357
392
  // autoRegister: false means the project imports its own BetterAuthModule separately
358
393
  const isAutoRegisterDisabled = typeof betterAuthConfig === 'object' && betterAuthConfig?.autoRegister === false;
359
394
 
360
- // Extract custom controller/resolver from config (Pattern 2: Config-based)
395
+ // Extract custom controller/resolver: overrides take precedence over config fields
361
396
  const configController = typeof betterAuthConfig === 'object' ? betterAuthConfig?.controller : undefined;
362
397
  const configResolver = typeof betterAuthConfig === 'object' ? betterAuthConfig?.resolver : undefined;
363
398
 
399
+ if (isAutoRegisterDisabled && (overrides?.betterAuth?.controller || overrides?.betterAuth?.resolver)) {
400
+ console.warn(
401
+ 'CoreModule: betterAuth overrides are ignored because betterAuth.autoRegister is false. ' +
402
+ 'Either remove autoRegister: false or pass controller/resolver to your own BetterAuthModule.forRoot() call.',
403
+ );
404
+ }
405
+
364
406
  if (isBetterAuthEnabled) {
365
407
  if ((isIamOnlyMode && !isAutoRegisterDisabled) || isAutoRegister) {
366
408
  imports.push(
367
409
  CoreBetterAuthModule.forRoot({
368
410
  config: betterAuthConfig === true ? {} : betterAuthConfig || {},
369
- // Pass custom controller/resolver from config (Pattern 2)
370
- controller: configController,
411
+ // Overrides take precedence over config fields (backward compatible)
412
+ controller: overrides?.betterAuth?.controller || configController,
371
413
  // Pass JWT secrets for backwards compatibility fallback
372
414
  fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
373
415
  // In IAM-only mode, register RolesGuard globally to enforce @Roles() decorators
374
416
  // In Legacy mode (autoRegister), RolesGuard is already registered via CoreAuthModule
375
417
  registerRolesGuardGlobally: isIamOnlyMode,
376
- // Pass custom resolver from config (Pattern 2)
377
- resolver: configResolver,
418
+ // Overrides take precedence over config fields (backward compatible)
419
+ resolver: overrides?.betterAuth?.resolver || configResolver,
378
420
  // Pass server-level URLs for Passkey auto-detection
379
421
  // When env: 'local', defaults are: baseUrl=localhost:3000, appUrl=localhost:3001
380
422
  serverAppUrl: config.appUrl,
@@ -7,7 +7,6 @@ import { Any } from '../core/common/scalars/any.scalar';
7
7
  import { DateScalar } from '../core/common/scalars/date.scalar';
8
8
  import { JSON } from '../core/common/scalars/json.scalar';
9
9
  import { CoreAuthService } from '../core/modules/auth/services/core-auth.service';
10
- import { ErrorCodeModule } from '../core/modules/error-code/error-code.module';
11
10
  import { TusModule } from '../core/modules/tus';
12
11
  import { CronJobs } from './common/services/cron-jobs.service';
13
12
  import { AuthController } from './modules/auth/auth.controller';
@@ -35,7 +34,13 @@ import { ServerController } from './server.controller';
35
34
  imports: [
36
35
  // Include CoreModule for standard processes
37
36
  // Note: BetterAuthModule is imported manually below (autoRegister defaults to false)
38
- CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig),
37
+ // ErrorCodeModule is auto-registered by CoreModule with overrides for custom controller/service
38
+ CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig, {
39
+ errorCode: {
40
+ controller: ErrorCodeController,
41
+ service: ErrorCodeService,
42
+ },
43
+ }),
39
44
 
40
45
  // Include cron job handling
41
46
  ScheduleModule.forRoot(),
@@ -49,13 +54,6 @@ import { ServerController } from './server.controller';
49
54
  // This allows project-specific customization via BetterAuthResolver
50
55
  BetterAuthModule.forRoot({}),
51
56
 
52
- // Include ErrorCodeModule with project-specific error codes
53
- // Uses Core ErrorCodeModule.forRoot() with custom service and controller
54
- ErrorCodeModule.forRoot({
55
- controller: ErrorCodeController,
56
- service: ErrorCodeService,
57
- }),
58
-
59
57
  // Include FileModule for file handling
60
58
  FileModule,
61
59