@lenne.tech/nest-server 11.21.2 → 11.22.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 (71) 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/migration-guides.md +149 -0
  6. package/.claude/rules/module-deprecation.md +214 -0
  7. package/.claude/rules/module-inheritance.md +97 -0
  8. package/.claude/rules/package-management.md +112 -0
  9. package/.claude/rules/role-system.md +146 -0
  10. package/.claude/rules/testing.md +120 -0
  11. package/.claude/rules/versioning.md +53 -0
  12. package/CLAUDE.md +172 -0
  13. package/dist/core/common/interfaces/server-options.interface.d.ts +10 -0
  14. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +25 -25
  15. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
  16. package/dist/core/modules/better-auth/core-better-auth.service.js +8 -4
  17. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  18. package/dist/core/modules/error-code/error-code.module.js.map +1 -1
  19. package/dist/core/modules/tenant/core-tenant.guard.d.ts +1 -0
  20. package/dist/core/modules/tenant/core-tenant.guard.js +59 -4
  21. package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -1
  22. package/dist/core/modules/tenant/core-tenant.helpers.js.map +1 -1
  23. package/dist/core.module.d.ts +3 -3
  24. package/dist/core.module.js +17 -4
  25. package/dist/core.module.js.map +1 -1
  26. package/dist/server/server.module.js +6 -6
  27. package/dist/server/server.module.js.map +1 -1
  28. package/dist/test/test.helper.d.ts +6 -2
  29. package/dist/test/test.helper.js +28 -6
  30. package/dist/test/test.helper.js.map +1 -1
  31. package/dist/tsconfig.build.tsbuildinfo +1 -1
  32. package/docs/REQUEST-LIFECYCLE.md +1256 -0
  33. package/docs/error-codes.md +446 -0
  34. package/migration-guides/11.10.x-to-11.11.x.md +266 -0
  35. package/migration-guides/11.11.x-to-11.12.x.md +323 -0
  36. package/migration-guides/11.12.x-to-11.13.0.md +612 -0
  37. package/migration-guides/11.13.x-to-11.14.0.md +348 -0
  38. package/migration-guides/11.14.x-to-11.15.0.md +262 -0
  39. package/migration-guides/11.15.0-to-11.15.3.md +118 -0
  40. package/migration-guides/11.15.x-to-11.16.0.md +497 -0
  41. package/migration-guides/11.16.x-to-11.17.0.md +130 -0
  42. package/migration-guides/11.17.x-to-11.18.0.md +393 -0
  43. package/migration-guides/11.18.x-to-11.19.0.md +151 -0
  44. package/migration-guides/11.19.x-to-11.20.0.md +170 -0
  45. package/migration-guides/11.20.x-to-11.21.0.md +216 -0
  46. package/migration-guides/11.21.0-to-11.21.1.md +194 -0
  47. package/migration-guides/11.21.1-to-11.21.2.md +114 -0
  48. package/migration-guides/11.21.2-to-11.21.3.md +175 -0
  49. package/migration-guides/11.21.x-to-11.22.0.md +224 -0
  50. package/migration-guides/11.3.x-to-11.4.x.md +233 -0
  51. package/migration-guides/11.6.x-to-11.7.x.md +394 -0
  52. package/migration-guides/11.7.x-to-11.8.x.md +318 -0
  53. package/migration-guides/11.8.x-to-11.9.x.md +322 -0
  54. package/migration-guides/11.9.x-to-11.10.x.md +571 -0
  55. package/migration-guides/TEMPLATE.md +113 -0
  56. package/package.json +8 -3
  57. package/src/core/common/interfaces/server-options.interface.ts +83 -16
  58. package/src/core/modules/better-auth/CUSTOMIZATION.md +24 -17
  59. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +5 -5
  60. package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +29 -25
  61. package/src/core/modules/better-auth/core-better-auth.service.ts +13 -9
  62. package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +42 -12
  63. package/src/core/modules/error-code/error-code.module.ts +4 -9
  64. package/src/core/modules/tenant/INTEGRATION-CHECKLIST.md +13 -2
  65. package/src/core/modules/tenant/README.md +26 -1
  66. package/src/core/modules/tenant/core-tenant.guard.ts +142 -11
  67. package/src/core/modules/tenant/core-tenant.helpers.ts +6 -2
  68. package/src/core.module.ts +52 -10
  69. package/src/server/server.module.ts +7 -9
  70. package/src/test/README.md +47 -0
  71. package/src/test/test.helper.ts +55 -6
@@ -0,0 +1,79 @@
1
+ # Code Architecture
2
+
3
+ ## Framework Stack
4
+
5
+ - **NestJS** - Server framework
6
+ - **GraphQL** - API layer (Apollo Server)
7
+ - **MongoDB** - Database (Mongoose ODM)
8
+
9
+ ## Two-Layer Structure
10
+
11
+ 1. **Core Layer** (`src/core/`) - Reusable framework components (exported to consumers)
12
+ 2. **Server Layer** (`src/server/`) - Internal test/demo implementation (not exported)
13
+
14
+ ## Core Module (`src/core.module.ts`)
15
+
16
+ - Dynamic module providing base functionality
17
+ - Configures GraphQL with Apollo Server (can be disabled via `graphQl: false`), MongoDB with Mongoose
18
+ - Provides global services: ConfigService, EmailService, TemplateService
19
+ - Sets up security interceptors, validation pipes, complexity plugins (when GraphQL enabled)
20
+ - Handles GraphQL subscriptions with authentication
21
+
22
+ ## Configuration System (`src/config.env.ts`)
23
+
24
+ Environment-based configuration (development, local, production) with multiple sources:
25
+
26
+ - Direct environment variables in config file
27
+ - `NEST_SERVER_CONFIG` JSON environment variable
28
+ - `NSC__*` prefixed single environment variables
29
+
30
+ Key areas: JWT, MongoDB, GraphQL, email, security, static assets
31
+
32
+ ## Core Common Components (`src/core/common/`)
33
+
34
+ | Type | Components |
35
+ |------|------------|
36
+ | **Decorators** | `@Restricted()`, `@Roles()`, `@CurrentUser()`, `@UnifiedField()` |
37
+ | **Helpers** | Database, GraphQL, filtering, validation utilities |
38
+ | **Security** | Response/security interceptors, input validation pipes |
39
+ | **Scalars** | Custom GraphQL scalars (Date, JSON, Any) |
40
+ | **Services** | CRUD operations, email (Mailjet/SMTP), template rendering |
41
+
42
+ ## Core Modules (`src/core/modules/`)
43
+
44
+ | Module | Purpose |
45
+ |--------|---------|
46
+ | **Auth** | JWT authentication, refresh tokens, role-based access |
47
+ | **BetterAuth** | Modern auth integration (2FA, Passkey, Social) |
48
+ | **ErrorCode** | Centralized error codes with unique identifiers |
49
+ | **File** | File upload/download with GridFS storage |
50
+ | **HealthCheck** | Application health monitoring |
51
+ | **Migrate** | Database migration utilities |
52
+ | **SystemSetup** | Initial admin creation for fresh deployments |
53
+ | **Tus** | Resumable file uploads via tus.io protocol |
54
+ | **User** | Core user management functionality |
55
+
56
+ ## Security Implementation
57
+
58
+ - `@Restricted()` - Field-level access control
59
+ - `@Roles()` - Method-level authorization
60
+ - `CheckResponseInterceptor` - Filters restricted fields
61
+ - `CheckSecurityInterceptor` - Processes `securityCheck()` methods
62
+
63
+ ## Model Inheritance
64
+
65
+ - `CorePersistenceModel` - Base for database entities
66
+ - `CoreModel` - Base for GraphQL types
67
+ - Automatic ID handling with custom Mongoose plugin
68
+
69
+ ## Input Validation
70
+
71
+ - `MapAndValidatePipe` - Automatic validation with inheritance-aware checking
72
+ - `@UnifiedField()` - Single decorator for GraphQL, Swagger, and validation (replaces separate `@Field`, `@ApiProperty`, `@IsOptional`, etc.)
73
+ - Automatic input property whitelisting — properties without `@UnifiedField` are stripped (default) or rejected
74
+ - `@UnifiedField({ exclude: true })` — explicitly exclude a property from input (hidden from schema, rejected at runtime)
75
+ - `@UnifiedField({ exclude: false })` — explicitly re-enable a property excluded by a parent class
76
+ - Configurable via `security.mapAndValidatePipe.nonWhitelistedFields`: `'strip'` (default), `'error'`, or `false`
77
+ - Custom decorator parameters (`@CurrentUser()`, `@RESTServiceOptions()`, etc.) and basic types (`String`, `Number`, etc.) are skipped — no validation or whitelist check
78
+ - Recursive nested object/array checking via `nestedTypeRegistry`
79
+ - Core args classes for filtering/pagination
@@ -0,0 +1,262 @@
1
+ ---
2
+ paths: src/core/modules/better-auth/**
3
+ ---
4
+
5
+ # Better-Auth Module Development Rules
6
+
7
+ These rules apply when working in `src/core/modules/better-auth/`.
8
+
9
+ ## 1. Maximize Better-Auth Standard Compliance
10
+
11
+ When making changes to the Better-Auth module:
12
+
13
+ - **Stay as close as possible to Better-Auth's standard behavior**
14
+ - **Minimize custom implementations** - use Better-Auth's built-in functionality wherever possible
15
+ - **Never bypass or disable security mechanisms** provided by Better-Auth
16
+ - **Maintain update compatibility** - changes must not break when Better-Auth releases updates
17
+
18
+ ### Rationale
19
+
20
+ Better-Auth is a security-critical library. Custom implementations:
21
+ - May introduce security vulnerabilities
22
+ - Can break with Better-Auth updates
23
+ - Add maintenance burden
24
+ - May not benefit from Better-Auth's security audits
25
+
26
+ ### Example: Adapter Pattern
27
+
28
+ When extending functionality (e.g., JWT mode for Passkey), prefer adapter patterns:
29
+
30
+ ```typescript
31
+ // GOOD: Adapter that bridges to Better-Auth's mechanisms
32
+ // - Uses Better-Auth's verificationToken
33
+ // - Lets Better-Auth handle all WebAuthn logic
34
+ // - Only bridges the cookie gap for JWT mode
35
+
36
+ // BAD: Custom implementation that replaces Better-Auth logic
37
+ // - Stores challenges separately
38
+ // - Implements own verification
39
+ // - Bypasses Better-Auth's security checks
40
+ ```
41
+
42
+ ## 2. Security-First Implementation
43
+
44
+ All Better-Auth code must be implemented with maximum security:
45
+
46
+ ### Mandatory Security Measures
47
+
48
+ 1. **Cryptographically secure IDs** - Use `crypto.randomBytes(32)` for tokens/IDs
49
+ 2. **TTL-based expiration** - All temporary data must have automatic cleanup
50
+ 3. **One-time use** - Tokens/challenges must be deleted after use
51
+ 4. **Secrets protection** - Never expose internal tokens to clients
52
+ 5. **Cookie signing** - Use proper HMAC signatures for cookies
53
+
54
+ ### Security Review Checklist
55
+
56
+ Before completing any Better-Auth changes:
57
+
58
+ - [ ] No secrets/tokens exposed to client (only opaque IDs)
59
+ - [ ] All temporary data has TTL-based expiration
60
+ - [ ] One-time tokens are deleted after use
61
+ - [ ] Cryptographically secure random generation used
62
+ - [ ] No OWASP Top 10 vulnerabilities introduced
63
+ - [ ] Cookie signing uses proper HMAC with application secret
64
+ - [ ] Rate limiting considered for authentication endpoints
65
+ - [ ] Input validation on all user-supplied data
66
+
67
+ ### Example: Challenge Storage
68
+
69
+ ```typescript
70
+ // Security measures applied:
71
+ // 1. challengeId: 256-bit entropy (crypto.randomBytes(32))
72
+ // 2. verificationToken: never sent to client
73
+ // 3. TTL index: automatic MongoDB cleanup
74
+ // 4. One-time use: deleted after verification
75
+ // 5. User binding: challenges tied to specific user
76
+ ```
77
+
78
+ ## 3. Comprehensive Testing Requirements
79
+
80
+ All Better-Auth changes require comprehensive tests:
81
+
82
+ ### Test Requirements
83
+
84
+ 1. **New functionality tests** - Cover all new features completely
85
+ 2. **Security tests** - Verify authentication/authorization works correctly
86
+ 3. **Edge case tests** - Token expiration, invalid input, race conditions
87
+ 4. **Regression tests** - Existing tests must pass (adapt if needed)
88
+
89
+ ### Pre-Commit Checklist
90
+
91
+ Before completing any Better-Auth changes:
92
+
93
+ ```bash
94
+ # All tests must pass
95
+ pnpm test
96
+
97
+ # Specific test file for targeted changes
98
+ pnpm test -- tests/stories/better-auth-*.ts
99
+ ```
100
+
101
+ ### Test Categories for Better-Auth
102
+
103
+ | Category | Focus | Example Tests |
104
+ |----------|-------|---------------|
105
+ | Authentication | Login/logout flows | `better-auth-api.story.test.ts` |
106
+ | Authorization | Role-based access | `better-auth-rest-security.e2e-spec.ts` |
107
+ | Security | Token validation, rate limiting | `better-auth-rate-limit.story.test.ts` |
108
+ | Integration | Module initialization | `better-auth-integration.story.test.ts` |
109
+ | Plugins | 2FA, Passkey, Social Login | `better-auth-plugins.story.test.ts` |
110
+
111
+ ### Adapting Existing Tests
112
+
113
+ When changes affect existing test expectations:
114
+
115
+ 1. **Understand why** the test was written that way
116
+ 2. **Verify the change is correct** - not breaking intended behavior
117
+ 3. **Update test** to match new correct behavior
118
+ 4. **Document** why the test was changed in commit message
119
+
120
+ ## 4. Customization Patterns
121
+
122
+ When a project needs custom BetterAuth behavior, follow these patterns:
123
+
124
+ ### Module Registration Patterns
125
+
126
+ | Pattern | Use When | Configuration |
127
+ |---------|----------|---------------|
128
+ | **Zero-Config** | No customization needed | `CoreModule.forRoot(envConfig)` |
129
+ | **Overrides Parameter** (recommended) | Custom Controller/Resolver | `CoreModule.forRoot(envConfig, { betterAuth: { controller, resolver } })` |
130
+ | **Separate Module** | Full control, additional providers | `betterAuth: { autoRegister: false }` |
131
+
132
+ ### Pattern Selection Decision Tree
133
+
134
+ 1. Does the project need custom Controller or Resolver?
135
+ - No → Use Zero-Config (Pattern 1)
136
+ - Yes → Continue to 2
137
+
138
+ 2. Does the project need additional providers or complex module structure?
139
+ - No → Use Overrides Parameter (Pattern 2) - pass `{ betterAuth: { controller, resolver } }` as second arg to `CoreModule.forRoot()`
140
+ - Yes → Use Separate Module (Pattern 3) - set `autoRegister: false`
141
+
142
+ ### Critical: Resolver Decorator Re-declaration
143
+
144
+ When customizing the Resolver, **ALL decorators MUST be re-declared**:
145
+
146
+ ```typescript
147
+ // WRONG - method won't appear in GraphQL schema!
148
+ override async betterAuthSignUp(...) {
149
+ return super.betterAuthSignUp(...);
150
+ }
151
+
152
+ // CORRECT - all decorators re-declared
153
+ @Mutation(() => BetterAuthAuthModel)
154
+ @Roles(RoleEnum.S_EVERYONE)
155
+ override async betterAuthSignUp(...) {
156
+ return super.betterAuthSignUp(...);
157
+ }
158
+ ```
159
+
160
+ **Why:** GraphQL schema is built from decorators at compile time. Parent class is `isAbstract: true`.
161
+
162
+ ### Email Template Customization
163
+
164
+ Templates are resolved in order:
165
+ 1. `<template>-<locale>.ejs` in project templates
166
+ 2. `<template>.ejs` in project templates
167
+ 3. `<template>-<locale>.ejs` in nest-server (fallback)
168
+ 4. `<template>.ejs` in nest-server (fallback)
169
+
170
+ To override: Create `src/templates/email-verification-de.ejs` in the project.
171
+
172
+ ### Avoiding "forRoot() called twice" Warning
173
+
174
+ If you see this warning, the project has duplicate registration:
175
+
176
+ **Solutions:**
177
+ 1. Use the `overrides` parameter on `CoreModule.forRoot()` (Pattern 2): `CoreModule.forRoot(envConfig, { betterAuth: { controller, resolver } })`
178
+ 2. Set `betterAuth.autoRegister: false` (Pattern 3)
179
+
180
+ **See:** `src/core/modules/better-auth/CUSTOMIZATION.md` for complete documentation.
181
+
182
+ ## 5. RolesGuard Architecture
183
+
184
+ ### Two Guard Implementations
185
+
186
+ The BetterAuth module provides two RolesGuard implementations:
187
+
188
+ | Guard | Used In | Key Characteristics |
189
+ |-------|---------|---------------------|
190
+ | `RolesGuard` | Legacy + Hybrid Mode | Extends `AuthGuard(JWT)`, supports Passport |
191
+ | `BetterAuthRolesGuard` | IAM-Only Mode | No Passport, no constructor dependencies |
192
+
193
+ ### Why BetterAuthRolesGuard Exists
194
+
195
+ **Problem:** `AuthGuard()` from `@nestjs/passport` is a **mixin** that generates `design:paramtypes` metadata. When `RolesGuard extends AuthGuard(JWT)` is registered as `APP_GUARD` in a dynamic module (Pattern 3: `autoRegister: false`), NestJS DI fails to inject `Reflector` and `ModuleRef`.
196
+
197
+ **Error:** `Reflector not available - RolesGuard cannot function without it`
198
+
199
+ **Solution:** `BetterAuthRolesGuard` with NO constructor dependencies:
200
+
201
+ ```typescript
202
+ @Injectable()
203
+ export class BetterAuthRolesGuard implements CanActivate {
204
+ // NO constructor dependencies - avoids mixin DI conflict
205
+
206
+ async canActivate(context: ExecutionContext): Promise<boolean> {
207
+ // Use Reflect.getMetadata directly (not NestJS Reflector)
208
+ const roles = Reflect.getMetadata('roles', context.getHandler());
209
+
210
+ // Access services via static module reference
211
+ const tokenService = CoreBetterAuthModule.getTokenServiceInstance();
212
+
213
+ // ... role checking logic identical to RolesGuard
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### Guard Selection Logic
219
+
220
+ In `CoreBetterAuthModule.createDeferredModule()`:
221
+
222
+ ```typescript
223
+ // IAM-Only Mode: Use BetterAuthRolesGuard (no Passport dependency)
224
+ providers: [
225
+ BetterAuthRolesGuard,
226
+ { provide: APP_GUARD, useExisting: BetterAuthRolesGuard },
227
+ ]
228
+ ```
229
+
230
+ In `CoreAuthModule` (Legacy Mode):
231
+
232
+ ```typescript
233
+ // Legacy Mode: Use RolesGuard (extends AuthGuard for Passport support)
234
+ providers: [
235
+ { provide: APP_GUARD, useClass: RolesGuard },
236
+ ]
237
+ ```
238
+
239
+ ### Security Equivalence
240
+
241
+ Both guards implement identical security logic:
242
+ - Same `@Roles()` decorator processing
243
+ - Same role checks (S_USER, S_EVERYONE, S_VERIFIED, S_SELF, S_CREATOR, S_NO_ONE)
244
+ - Same token verification (via BetterAuthTokenService)
245
+ - Same error responses (401 Unauthorized, 403 Forbidden)
246
+
247
+ ### When Working on Guards
248
+
249
+ 1. **Changes to role logic** → Update BOTH guards
250
+ 2. **New system roles** → Add to BOTH guards
251
+ 3. **Token verification changes** → Update `BetterAuthTokenService` (shared by both)
252
+ 4. **Testing** → Test both Legacy Mode and IAM-Only Mode
253
+
254
+ ## Summary
255
+
256
+ | Principle | Requirement |
257
+ |-----------|-------------|
258
+ | Standard Compliance | Stay close to Better-Auth, minimize custom code |
259
+ | Security | Maximum security, thorough review before completion |
260
+ | Testing | Full coverage, all tests pass, security tests included |
261
+ | Customization | Use correct registration pattern, re-declare Resolver decorators |
262
+ | Guards | Maintain both RolesGuard and BetterAuthRolesGuard in sync |
@@ -0,0 +1,308 @@
1
+ # Configurable Features Pattern
2
+
3
+ This document describes the standard pattern for implementing optional, configurable features in @lenne.tech/nest-server.
4
+
5
+ ## "Presence Implies Enabled" Pattern
6
+
7
+ When implementing configurable features, follow this pattern for activation logic:
8
+
9
+ ### Rules
10
+
11
+ 1. **No configuration** (`undefined` or `null`): Feature is **disabled** (backward compatible)
12
+ 2. **Empty object** (`{}`): Feature is **enabled** with all default values
13
+ 3. **Partial configuration** (`{ max: 5 }`): Feature is **enabled**, missing values use defaults
14
+ 4. **Explicit disable** (`{ enabled: false, ... }`): Feature is **disabled**, allows pre-configuration
15
+
16
+ ### Benefits
17
+
18
+ - **Backward Compatible**: Existing projects without config continue to work unchanged
19
+ - **Efficient**: No need to set `enabled: true` redundantly when already providing config
20
+ - **Flexible**: Can pre-configure without activating via `enabled: false`
21
+ - **Intuitive**: Providing a config object signals intent to use the feature
22
+
23
+ ### Implementation Example
24
+
25
+ ```typescript
26
+ interface IFeatureConfig {
27
+ enabled?: boolean; // Optional - presence of config implies true
28
+ max?: number;
29
+ windowSeconds?: number;
30
+ }
31
+
32
+ const DEFAULT_CONFIG: Required<IFeatureConfig> = {
33
+ enabled: false, // Default is false, but overridden by presence
34
+ max: 10,
35
+ windowSeconds: 60,
36
+ };
37
+
38
+ class FeatureService {
39
+ private config: Required<IFeatureConfig> = DEFAULT_CONFIG;
40
+
41
+ /**
42
+ * Configure the feature
43
+ *
44
+ * Follows the "presence implies enabled" pattern:
45
+ * - If config is undefined/null: feature stays disabled (backward compatible)
46
+ * - If config is an object (even empty {}): feature is enabled by default
47
+ * - Unless `enabled: false` is explicitly set
48
+ */
49
+ configure(config: IFeatureConfig | undefined | null): void {
50
+ // No config = stay disabled (backward compatible)
51
+ if (config === undefined || config === null) {
52
+ return;
53
+ }
54
+
55
+ // Presence of config implies enabled, unless explicitly disabled
56
+ const enabled = config.enabled !== false;
57
+
58
+ this.config = {
59
+ ...DEFAULT_CONFIG,
60
+ ...config,
61
+ enabled,
62
+ };
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### Usage Examples
68
+
69
+ ```typescript
70
+ // config.env.ts
71
+
72
+ // Feature disabled (no config)
73
+ // rateLimit: undefined // or just don't define it
74
+
75
+ // Feature enabled with all defaults
76
+ auth: {
77
+ rateLimit: {}
78
+ }
79
+
80
+ // Feature enabled with custom max
81
+ auth: {
82
+ rateLimit: { max: 20 }
83
+ }
84
+
85
+ // Feature enabled with full configuration
86
+ auth: {
87
+ rateLimit: {
88
+ max: 10,
89
+ windowSeconds: 60,
90
+ message: 'Too many requests'
91
+ }
92
+ }
93
+
94
+ // Pre-configured but disabled (for testing or gradual rollout)
95
+ auth: {
96
+ rateLimit: {
97
+ enabled: false,
98
+ max: 10,
99
+ windowSeconds: 60
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Boolean Shorthand Pattern
105
+
106
+ For simple enable/disable scenarios, support `boolean | object` configuration:
107
+
108
+ ### Rules
109
+
110
+ 1. **`true`**: Feature is **enabled** with all default values
111
+ 2. **`false`**: Feature is **disabled**
112
+ 3. **`{}`**: Feature is **enabled** with all default values (same as `true`)
113
+ 4. **`{ option: value }`**: Feature is **enabled** with custom settings
114
+ 5. **`{ enabled: false }`**: Feature is **disabled** (allows pre-configuration)
115
+ 6. **`undefined`**: Feature is **disabled** (default)
116
+
117
+ ### Benefits
118
+
119
+ - **Concise**: `jwt: true` instead of `jwt: {}`
120
+ - **Readable**: Clear intent at a glance
121
+ - **Flexible**: Can still use objects for customization
122
+
123
+ ### Implementation Example
124
+
125
+ ```typescript
126
+ // Interface definition
127
+ interface IBetterAuth {
128
+ jwt?: boolean | IBetterAuthJwtConfig;
129
+ twoFactor?: boolean | IBetterAuthTwoFactorConfig;
130
+ passkey?: boolean | IBetterAuthPasskeyConfig;
131
+ }
132
+
133
+ interface IBetterAuthJwtConfig {
134
+ enabled?: boolean;
135
+ expiresIn?: string;
136
+ }
137
+
138
+ // Helper functions
139
+ function isPluginEnabled<T extends { enabled?: boolean }>(
140
+ config: boolean | T | undefined
141
+ ): boolean {
142
+ if (config === undefined) return false;
143
+ if (typeof config === 'boolean') return config;
144
+ return config.enabled !== false;
145
+ }
146
+
147
+ function getPluginConfig<T extends { enabled?: boolean }>(
148
+ config: boolean | T | undefined
149
+ ): T | undefined {
150
+ if (!isPluginEnabled(config)) return undefined;
151
+ if (typeof config === 'boolean') return {} as T;
152
+ return config;
153
+ }
154
+
155
+ // Usage in build logic
156
+ const jwtConfig = getPluginConfig(config.jwt);
157
+ if (jwtConfig) {
158
+ plugins.push(jwt({ expirationTime: jwtConfig.expiresIn || '15m' }));
159
+ }
160
+ ```
161
+
162
+ ### Usage Examples
163
+
164
+ ```typescript
165
+ // config.env.ts
166
+
167
+ betterAuth: {
168
+ // Boolean shorthand - enable with defaults
169
+ jwt: true,
170
+ twoFactor: true,
171
+ passkey: true,
172
+ }
173
+
174
+ // Equivalent to:
175
+ betterAuth: {
176
+ jwt: {},
177
+ twoFactor: {},
178
+ passkey: {},
179
+ }
180
+
181
+ // Mixed - some with defaults, some customized
182
+ betterAuth: {
183
+ jwt: true, // Enable with defaults
184
+ twoFactor: { appName: 'My App' }, // Enable with custom settings
185
+ passkey: false, // Explicitly disabled
186
+ }
187
+
188
+ // Pre-configured but disabled
189
+ betterAuth: {
190
+ jwt: { enabled: false, expiresIn: '1h' }, // Ready to enable later
191
+ }
192
+ ```
193
+
194
+ ## Applied Features
195
+
196
+ This pattern is currently applied to:
197
+
198
+ | Feature | Config Path | Pattern | Default Values |
199
+ |---------|-------------|---------|----------------|
200
+ | Legacy Auth Rate Limiting | `auth.rateLimit` | Presence Implies Enabled | `max: 10`, `windowSeconds: 60` |
201
+ | BetterAuth Rate Limiting | `betterAuth.rateLimit` | Presence Implies Enabled | `max: 10`, `windowSeconds: 60` |
202
+ | BetterAuth JWT Plugin | `betterAuth.jwt` | Boolean Shorthand | `expiresIn: '15m'` |
203
+ | BetterAuth 2FA Plugin | `betterAuth.twoFactor` | Boolean Shorthand | `appName: 'Nest Server'` |
204
+ | BetterAuth Passkey Plugin | `betterAuth.passkey` | Boolean Shorthand | `rpName: 'Nest Server'` |
205
+ | BetterAuth Cross-Subdomain Cookies | `betterAuth.crossSubDomainCookies` | Boolean Shorthand | `domain: auto (appUrl → baseUrl without api. prefix)` |
206
+ | BetterAuth Disable Sign-Up | `betterAuth.emailAndPassword.disableSignUp` | Explicit Boolean | `false` (sign-up enabled) |
207
+ | System Setup | `systemSetup` | Enabled by Default (when BetterAuth active) | `initialAdmin: undefined` |
208
+ | GraphQL | `graphQl` | Explicit Disable (`false`) | Enabled (full GraphQL stack) |
209
+ | Mongoose Password Plugin | `security.mongoosePasswordPlugin` | Boolean Shorthand | `true` (enabled), `skipPatterns: []` |
210
+ | Mongoose Role Guard Plugin | `security.mongooseRoleGuardPlugin` | Boolean Shorthand | `true` (enabled), `allowedRoles: []`. Bypass: `RequestContext.runWithBypassRoleGuard()` or `force: true` |
211
+ | Mongoose Audit Fields Plugin | `security.mongooseAuditFieldsPlugin` | Boolean Shorthand | `true` (enabled) |
212
+ | Response Model Interceptor | `security.responseModelInterceptor` | Boolean Shorthand | `true` (enabled), `debug: false` |
213
+ | Translate Response Interceptor | `security.translateResponseInterceptor` | Boolean Shorthand | `true` (enabled) |
214
+ | Secret Fields Removal | `security.secretFields` | Array | `['password', 'verificationToken', ...]` |
215
+ | Multi-Tenancy | `multiTenancy` | Presence Implies Enabled | `headerName: 'x-tenant-id'`, `membershipModel: 'TenantMember'`, `adminBypass: true`, `excludeSchemas: []`, `roleHierarchy: { member: 1, manager: 2, owner: 3 }`, `cacheTtlMs: 30000` (0 disables, process-local). System roles (`S_EVERYONE`, `S_USER`, `S_VERIFIED`) are checked as OR alternatives before real roles; method-level system roles take precedence; membership validated for context when system role grants access + header present. Hierarchy roles use level comparison, normal roles use exact match. Use `DefaultHR` or `createHierarchyRoles()` for type-safe role constants. Bypass: `RequestContext.runWithBypassTenantGuard()`. Cache invalidation: `CoreTenantGuard.invalidateUser(userId)` / `invalidateAll()` |
216
+ | BetterAuth Tenant Skip | `betterAuth.skipTenantCheck` | Explicit Boolean | `true` (default). When `true` and no `X-Tenant-Id` header is sent, IAM endpoints (controller + resolver) skip `CoreTenantGuard` tenant validation. When header IS present, normal membership validation runs regardless. Set `false` for tenant-aware auth scenarios (subdomain-based, invite links, SSO per tenant) |
217
+
218
+ ## Module Override Pattern (via `ICoreModuleOverrides`)
219
+
220
+ For replacing default controllers, resolvers, or services of auto-registered core modules.
221
+
222
+ ### Why a separate `overrides` parameter?
223
+
224
+ NestJS registers controllers at module scan time — there is no mechanism to replace them after registration.
225
+ When `CoreModule.forRoot()` auto-registers a module (e.g., ErrorCodeModule), the only way to use a custom controller
226
+ is to pass it **before** registration happens. A separate `overrides` parameter on `CoreModule.forRoot()` keeps
227
+ class references (code) cleanly separated from environment configuration (strings/numbers).
228
+
229
+ ### Usage
230
+
231
+ ```typescript
232
+ // IAM-only mode
233
+ CoreModule.forRoot(envConfig, {
234
+ errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
235
+ betterAuth: { resolver: BetterAuthResolver },
236
+ })
237
+
238
+ // Legacy mode
239
+ CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig, {
240
+ errorCode: { controller: ErrorCodeController, service: ErrorCodeService },
241
+ })
242
+ ```
243
+
244
+ ### Available Override Fields
245
+
246
+ | Module | Fields | Description |
247
+ |--------|--------|-------------|
248
+ | `errorCode` | `controller`, `service` | Custom error code endpoint and/or service |
249
+ | `betterAuth` | `controller`, `resolver` | Custom IAM REST controller and/or GraphQL resolver |
250
+
251
+ ### Rules
252
+
253
+ 1. Overrides take precedence over `betterAuth.controller`/`resolver` in config (backward compatible)
254
+ 2. Only auto-registered modules are affected — `autoRegister: false` modules are imported separately
255
+ 3. The `ICoreModuleOverrides` interface enforces type safety per module
256
+
257
+ ### Alternative: `autoRegister: false`
258
+
259
+ For complex setups requiring additional providers or a custom module structure, disable auto-registration
260
+ and import the module separately:
261
+
262
+ ```typescript
263
+ // config.env.ts
264
+ errorCode: { autoRegister: false }
265
+ betterAuth: { autoRegister: false }
266
+
267
+ // server.module.ts
268
+ @Module({
269
+ imports: [
270
+ CoreModule.forRoot(envConfig),
271
+ ErrorCodeModule.forRoot({ controller: MyController, service: MyService }),
272
+ BetterAuthModule.forRoot({ controller: MyController, resolver: MyResolver }),
273
+ ],
274
+ })
275
+ ```
276
+
277
+ ## Checklist for New Configurable Features
278
+
279
+ When adding a new configurable feature:
280
+
281
+ ### For "Presence Implies Enabled" Pattern:
282
+
283
+ - [ ] Define interface with `enabled?: boolean` as optional property
284
+ - [ ] Set `enabled: false` in DEFAULT_CONFIG
285
+ - [ ] Implement "presence implies enabled" logic in configure method
286
+ - [ ] Document all default values in interface JSDoc
287
+ - [ ] Add tests for: undefined config, empty object, partial config, explicit disable
288
+
289
+ ### For "Boolean Shorthand" Pattern:
290
+
291
+ - [ ] Define separate interface for config options (e.g., `IBetterAuthJwtConfig`)
292
+ - [ ] Use union type: `property?: boolean | IPropertyConfig`
293
+ - [ ] Implement `isPluginEnabled()` helper for boolean/object handling
294
+ - [ ] Implement `getPluginConfig()` helper to normalize to object
295
+ - [ ] Add tests for: `true`, `false`, `{}`, `{ option: value }`, `{ enabled: false }`, `undefined`
296
+
297
+ ### For "Module Override" Pattern:
298
+
299
+ - [ ] Add override fields to `ICoreModuleOverrides` interface
300
+ - [ ] Pass overrides through in `CoreModule.forRoot()` to the module's `forRoot()`
301
+ - [ ] Ensure the module's `forRoot()` accepts controller/resolver/service parameters
302
+ - [ ] Update this document with the new override fields
303
+ - [ ] Update module's INTEGRATION-CHECKLIST.md
304
+
305
+ ### For Both Patterns:
306
+
307
+ - [ ] Update this document with the new feature
308
+ - [ ] Export new interfaces in `src/index.ts` (if needed)