@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,112 @@
1
+ # Package Management Rules
2
+
3
+ This document defines the rules for managing dependencies in package.json.
4
+
5
+ ## Package Manager: pnpm
6
+
7
+ This project uses **pnpm** for development. The `packageManager` field in `package.json` ensures the correct version is used (via Corepack).
8
+
9
+ ```bash
10
+ # Install dependencies
11
+ pnpm install
12
+
13
+ # Add a new package (fixed version)
14
+ pnpm add express@4.18.2
15
+
16
+ # Add a dev dependency
17
+ pnpm add -D typescript@5.3.3
18
+
19
+ # Remove a package
20
+ pnpm remove package-name
21
+ ```
22
+
23
+ ## Fixed Versions Only
24
+
25
+ **All package versions in package.json MUST be exact (fixed) versions.**
26
+
27
+ ### Rules
28
+
29
+ | Allowed | Not Allowed |
30
+ |---------|-------------|
31
+ | `"express": "4.18.2"` | `"express": "^4.18.2"` |
32
+ | `"typescript": "5.3.3"` | `"typescript": "~5.3.3"` |
33
+ | `"lodash": "4.17.21"` | `"lodash": ">=4.0.0"` |
34
+ | | `"lodash": "4.x"` |
35
+ | | `"lodash": "*"` |
36
+
37
+ ### Why Fixed Versions?
38
+
39
+ 1. **Reproducibility**: Every installation produces identical `node_modules`
40
+ 2. **Stability**: No surprise breaking changes from automatic minor/patch updates
41
+ 3. **Security Control**: Updates are intentional and reviewed, not automatic
42
+ 4. **Debugging**: Easier to identify which version introduced issues
43
+ 5. **Framework Responsibility**: As a framework, we must ensure consuming projects get predictable behavior
44
+
45
+ ### When Adding New Packages
46
+
47
+ ```bash
48
+ # CORRECT: Install with exact version
49
+ pnpm add express@4.18.2
50
+
51
+ # Then verify package.json has exact version (no ^ or ~)
52
+ ```
53
+
54
+ If pnpm adds `^` automatically, manually remove it from package.json.
55
+
56
+ ### When Updating Packages
57
+
58
+ ```bash
59
+ # Use pnpm update or manually update
60
+ pnpm update package-name
61
+
62
+ # Or manually edit package.json with exact version
63
+ ```
64
+
65
+ Always:
66
+ 1. Update to exact version
67
+ 2. Run `pnpm install`
68
+ 3. Run `pnpm test`
69
+ 4. Verify no regressions
70
+
71
+ ### Checking for Version Ranges
72
+
73
+ To find packages with version ranges:
74
+
75
+ ```bash
76
+ # Find all dependencies with ^ or ~
77
+ grep -E '"\^|"~' package.json
78
+ ```
79
+
80
+ ### Common Mistakes
81
+
82
+ | Mistake | Fix |
83
+ |---------|-----|
84
+ | pnpm adds `^` by default | Remove `^` after install |
85
+ | Copying from other projects | Verify versions are fixed |
86
+ | Using `pnpm add package` without version | Specify exact version: `pnpm add package@1.2.3` |
87
+
88
+ ## devDependencies
89
+
90
+ The same rules apply to devDependencies. All versions must be fixed.
91
+
92
+ ## peerDependencies
93
+
94
+ peerDependencies may use ranges when necessary for compatibility with consuming projects, but this should be minimized.
95
+
96
+ ## Lock File
97
+
98
+ The `pnpm-lock.yaml` file must always be committed. It provides additional reproducibility even if someone accidentally introduces a version range.
99
+
100
+ ## Overrides
101
+
102
+ Package overrides are configured in the `pnpm.overrides` section of `package.json`:
103
+
104
+ ```json
105
+ {
106
+ "pnpm": {
107
+ "overrides": {
108
+ "lodash": "4.17.23"
109
+ }
110
+ }
111
+ }
112
+ ```
@@ -0,0 +1,146 @@
1
+ # Role System (RoleEnum)
2
+
3
+ The role system distinguishes between **real roles** and **system roles**.
4
+
5
+ ## Real Roles
6
+
7
+ Actual roles stored in `user.roles` array in the database:
8
+
9
+ - `RoleEnum.ADMIN` - Administrator role
10
+
11
+ ## System Roles (S_ Prefix)
12
+
13
+ System roles are used for **runtime checks only** and must **NEVER** be stored in `user.roles`:
14
+
15
+ | Role | Purpose | Check Logic |
16
+ |------|---------|-------------|
17
+ | `S_USER` | User is logged in | `currentUser` exists |
18
+ | `S_VERIFIED` | User is verified | `user.verified \|\| user.verifiedAt \|\| user.emailVerified` |
19
+ | `S_CREATOR` | User created the object | `object.createdBy === user.id` |
20
+ | `S_SELF` | User is accessing own data | `object.id === user.id` |
21
+ | `S_EVERYONE` | Public access | Always true |
22
+ | `S_NO_ONE` | Locked access | Always false |
23
+
24
+ ## Critical Rule
25
+
26
+ ```typescript
27
+ // CORRECT: Using S_ roles in decorators (runtime checks)
28
+ @Roles(RoleEnum.S_USER)
29
+ @Restricted(RoleEnum.S_VERIFIED)
30
+
31
+ // WRONG: Storing S_ roles in user.roles array
32
+ roles: [RoleEnum.S_USER] // NEVER do this!
33
+
34
+ // CORRECT: Empty roles or real roles only
35
+ roles: [] // Empty array
36
+ roles: [RoleEnum.ADMIN] // Real role
37
+ ```
38
+
39
+ The `S_` prefix indicates a **system check**, not an actual role. These are evaluated dynamically at runtime based on context (current user, request, object being accessed).
40
+
41
+ ## Role Decorators
42
+
43
+ - `@Roles(RoleEnum.ADMIN)` - Method-level authorization
44
+ - `@Restricted(RoleEnum.S_VERIFIED)` - Field-level access control
45
+
46
+ ## Hierarchy Roles (Multi-Tenancy)
47
+
48
+ When `multiTenancy` is configured, hierarchy roles replace the old `S_TENANT_*` system roles:
49
+
50
+ ```typescript
51
+ import { DefaultHR, createHierarchyRoles } from '@lenne.tech/nest-server';
52
+
53
+ // Default hierarchy
54
+ @Roles(DefaultHR.MEMBER) // any active member (level >= 1)
55
+ @Roles(DefaultHR.MANAGER) // at least manager level (level >= 2)
56
+ @Roles(DefaultHR.OWNER) // highest level (level >= 3)
57
+
58
+ // Custom hierarchy
59
+ const HR = createHierarchyRoles({ viewer: 1, editor: 2, admin: 3, owner: 4 });
60
+ @Roles(HR.EDITOR) // requires level >= 2
61
+
62
+ // Normal (non-hierarchy) roles
63
+ @Roles('auditor') // exact match only, no level compensation
64
+ ```
65
+
66
+ **System role OR semantics in CoreTenantGuard:** System roles (`S_EVERYONE`, `S_USER`, `S_VERIFIED`) are checked as OR alternatives **before** real roles. Method-level system roles take precedence over class-level ones (e.g., class `S_EVERYONE` + method `S_USER` → `S_USER` applies). When a system role grants access and `X-Tenant-Id` header is present, membership is still validated to set tenant context — a non-member gets 403 with `S_USER`/`S_VERIFIED` (admin bypass applies).
67
+
68
+ **Tenant context rule for real roles:** When `X-Tenant-Id` header is present, only `membership.role` is checked. `user.roles` is ignored (except ADMIN bypass). Without header, `user.roles` is checked.
69
+
70
+ Hierarchy roles use **level comparison** (higher includes lower). Normal roles use **exact match**.
71
+
72
+ Both work in `@Roles()` (method-level) and `@Restricted()` (field-level) via unified `checkRoleAccess()`.
73
+
74
+ ## Role Check Implementation
75
+
76
+ The role system is evaluated in:
77
+ - `RolesGuard` / `BetterAuthRolesGuard` - Checks method-level `@Roles()` decorators (passes through non-system roles to `CoreTenantGuard` when multiTenancy active)
78
+ - `CoreTenantGuard` - Validates tenant membership and checks hierarchy/normal roles
79
+ - `CheckResponseInterceptor` - Filters fields based on `@Restricted()` decorators
80
+ - `CheckSecurityInterceptor` - Processes `securityCheck()` methods
81
+
82
+ ## @Roles vs @UseGuards
83
+
84
+ **IMPORTANT: `@Roles()` already handles JWT authentication internally.**
85
+
86
+ ```typescript
87
+ // CORRECT: @Roles alone is sufficient for authentication + authorization
88
+ @Query(() => SomeModel)
89
+ @Roles(RoleEnum.ADMIN)
90
+ async someAdminQuery(): Promise<SomeModel> { }
91
+
92
+ // WRONG: Don't add @UseGuards when @Roles is present
93
+ @Query(() => SomeModel)
94
+ @Roles(RoleEnum.ADMIN)
95
+ @UseGuards(AuthGuard(AuthGuardStrategy.JWT)) // REDUNDANT - Roles already handles this
96
+ async someAdminQuery(): Promise<SomeModel> { }
97
+ ```
98
+
99
+ The `@Roles()` decorator combined with `RolesGuard` automatically:
100
+ 1. Validates the JWT token
101
+ 2. Extracts the user from the token
102
+ 3. Checks if the user has the required role
103
+
104
+ ### When @UseGuards IS Required
105
+
106
+ `@UseGuards(AuthGuard(...))` is only needed in these specific cases:
107
+
108
+ | Case | Example | Reason |
109
+ |------|---------|--------|
110
+ | **Refresh Token** | `@UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH))` | Different strategy than standard JWT |
111
+ | **Custom Strategies** | `@UseGuards(AuthGuard(AuthGuardStrategy.CUSTOM))` | Non-standard authentication flow |
112
+
113
+ ```typescript
114
+ // CORRECT: refreshToken needs JWT_REFRESH strategy (not standard JWT)
115
+ @Mutation(() => CoreAuthModel)
116
+ @Roles(RoleEnum.S_EVERYONE)
117
+ @UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH)) // Required - different strategy!
118
+ async refreshToken(...): Promise<CoreAuthModel> { }
119
+
120
+ // CORRECT: Standard endpoints - @Roles is sufficient
121
+ @Mutation(() => CoreAuthModel)
122
+ @Roles(RoleEnum.S_USER) // Handles JWT auth automatically
123
+ async logout(...): Promise<boolean> { }
124
+
125
+ @Query(() => User)
126
+ @Roles(RoleEnum.ADMIN) // Handles JWT auth automatically
127
+ async getUser(...): Promise<User> { }
128
+ ```
129
+
130
+ ### Common Mistake: Redundant Guards
131
+
132
+ When reviewing code, watch for this anti-pattern:
133
+
134
+ ```typescript
135
+ // WRONG: Redundant - @Roles(S_USER) already validates JWT
136
+ @Roles(RoleEnum.S_USER)
137
+ @UseGuards(AuthGuard(AuthGuardStrategy.JWT)) // DELETE THIS
138
+ async someMethod() { }
139
+
140
+ // WRONG: Redundant - @Roles(ADMIN) already validates JWT
141
+ @Roles(RoleEnum.ADMIN)
142
+ @UseGuards(AuthGuard(AuthGuardStrategy.JWT)) // DELETE THIS
143
+ async adminMethod() { }
144
+ ```
145
+
146
+ **Rule of thumb:** If `@Roles()` uses any role OTHER than `S_EVERYONE`, you don't need `@UseGuards(AuthGuard(JWT))`.
@@ -0,0 +1,120 @@
1
+ # Testing Configuration
2
+
3
+ ## Test Framework
4
+
5
+ - **Vitest** - Primary test framework (migrated from Jest)
6
+ - Configuration: `vitest.config.ts`
7
+ - Test files: `tests/` directory with `.e2e-spec.ts` suffix
8
+
9
+ ## Running Tests
10
+
11
+ ```bash
12
+ # Run all E2E tests (default)
13
+ pnpm test
14
+
15
+ # Run with coverage
16
+ pnpm run test:cov
17
+
18
+ # Run in CI mode
19
+ pnpm run test:ci
20
+
21
+ # Debug open handles
22
+ pnpm run test:e2e-doh
23
+
24
+ # Clean up leftover test artifacts (.txt, .bin files from failed file upload tests)
25
+ pnpm run test:cleanup
26
+ ```
27
+
28
+ ## Test Environment
29
+
30
+ - Environment: `NODE_ENV=local`
31
+ - Database: Local MongoDB instance (`mongodb://127.0.0.1/nest-server-local`)
32
+ - Test helper: `src/test/test.helper.ts`
33
+ - Coverage: Collected from `src/**/*.{ts,js}`
34
+
35
+ ## Test Best Practices
36
+
37
+ 1. **Always run tests before completing changes**: `pnpm test`
38
+ 2. **Production-ready = all tests pass** without errors
39
+ 3. **Use TestHelper** for GraphQL and REST API testing
40
+ 4. **Clean up test data** in `afterAll` hooks
41
+ 5. **Unique test data** - Use timestamps/random strings to avoid conflicts
42
+
43
+ ## Test File Structure
44
+
45
+ ```typescript
46
+ import { Test, TestingModule } from '@nestjs/testing';
47
+ import { TestHelper } from '../../src';
48
+
49
+ describe('Feature Name', () => {
50
+ let testHelper: TestHelper;
51
+
52
+ beforeAll(async () => {
53
+ const moduleFixture = await Test.createTestingModule({
54
+ imports: [ServerModule],
55
+ }).compile();
56
+
57
+ const app = moduleFixture.createNestApplication();
58
+ await app.init();
59
+ testHelper = new TestHelper(app);
60
+ });
61
+
62
+ afterAll(async () => {
63
+ // Cleanup test data
64
+ await app.close();
65
+ });
66
+
67
+ it('should do something', async () => {
68
+ const result = await testHelper.graphQl({
69
+ name: 'someQuery',
70
+ type: TestGraphQLType.QUERY,
71
+ fields: ['id', 'name'],
72
+ });
73
+ expect(result.data.someQuery).toBeDefined();
74
+ });
75
+ });
76
+ ```
77
+
78
+ ## WebSocket/Subscription Tests
79
+
80
+ When testing GraphQL subscriptions, use `httpServer.listen(0)` instead of `app.listen()`:
81
+
82
+ ```typescript
83
+ // CORRECT: No startup log, dynamic port
84
+ await app.init();
85
+ const httpServer = app.getHttpServer();
86
+ await new Promise<void>((resolve) => {
87
+ httpServer.listen(0, '127.0.0.1', () => resolve());
88
+ });
89
+ const port = httpServer.address().port;
90
+ testHelper = new TestHelper(app, `ws://127.0.0.1:${port}/graphql`);
91
+
92
+ // Cleanup in afterAll
93
+ if (httpServer) {
94
+ await new Promise<void>((resolve) => httpServer.close(() => resolve()));
95
+ }
96
+ await app.close();
97
+ ```
98
+
99
+ ```typescript
100
+ // WRONG: Produces "Nest application successfully started" log
101
+ await app.init();
102
+ await app.listen(3030);
103
+ ```
104
+
105
+ **Why:**
106
+ - `app.listen()` triggers NestJS startup log -> noisy test output
107
+ - Dynamic port (`0`) avoids port conflicts between parallel tests
108
+ - Explicit `httpServer.close()` prevents open handle warnings
109
+
110
+ ## TestHelper Reference
111
+
112
+ Full documentation for TestHelper (REST, GraphQL, Cookie support):
113
+ `src/test/README.md` (also available in `node_modules/@lenne.tech/nest-server/src/test/README.md`)
114
+
115
+ ## Common Test Issues
116
+
117
+ - **Tests timeout**: Ensure MongoDB is running
118
+ - **Open handles**: Use `pnpm run test:e2e-doh` to debug
119
+ - **Data conflicts**: Use unique identifiers per test
120
+ - **"NestApplication successfully started" log**: Use `httpServer.listen()` instead of `app.listen()`
@@ -0,0 +1,53 @@
1
+ # Versioning Strategy
2
+
3
+ ## Version Schema: `MAJOR.MINOR.PATCH`
4
+
5
+ | Part | Meaning |
6
+ |---------|--------------------------------------------------------------------|
7
+ | `MAJOR` | Mirrors the NestJS major version (e.g., `11.x.x` = NestJS 11) |
8
+ | `MINOR` | Breaking changes or significant restructuring |
9
+ | `PATCH` | Improvements (bugfixes) or additions (new features) - non-breaking |
10
+
11
+ ## Examples
12
+
13
+ - `11.0.0` -> Initial release for NestJS 11
14
+ - `11.1.0` -> Breaking change or major restructuring within NestJS 11
15
+ - `11.1.5` -> Bugfix or new feature (backward compatible)
16
+
17
+ ## Important Rules
18
+
19
+ 1. **Document breaking changes** clearly in commit messages when incrementing MINOR version
20
+ 2. **Update nest-server-starter** with migration instructions for breaking changes
21
+ 3. **Consider downstream impact** - this package is used by multiple projects
22
+
23
+ ## Release Process
24
+
25
+ 1. Make changes and ensure all tests pass (`pnpm test`)
26
+ 2. Update version in `package.json`
27
+ 3. Build the package (`pnpm run build`)
28
+ 4. Publish to npm
29
+ 5. Update and test in [nest-server-starter](https://github.com/lenneTech/nest-server-starter)
30
+ 6. Commit changes to starter with migration notes
31
+
32
+ ## Package Distribution
33
+
34
+ - **NPM Package**: `@lenne.tech/nest-server`
35
+ - **Main entry**: `dist/index.js`
36
+ - **Types**: `dist/index.d.ts`
37
+ - **Public APIs**: Exported from `src/index.ts`
38
+
39
+ ## Package Development Commands
40
+
41
+ ```bash
42
+ # Build for local development (use with pnpm link)
43
+ pnpm run build:dev
44
+
45
+ # Create tarball for integration testing
46
+ pnpm run build:pack
47
+
48
+ # Clean reinstall with tests and build
49
+ pnpm run reinit
50
+
51
+ # Watch for changes
52
+ pnpm run watch
53
+ ```
package/CLAUDE.md ADDED
@@ -0,0 +1,172 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Quick Reference
6
+
7
+ - [Commands](#common-development-commands) | [Architecture](#code-architecture) | [Guidelines](#development-guidelines) | [Troubleshooting](#debugging--troubleshooting)
8
+ - **Detailed Rules**: See `.claude/rules/` for in-depth documentation
9
+
10
+ ## Self-Improvement Instructions
11
+
12
+ **Claude Code must actively maintain and improve this file and `.claude/rules/`.**
13
+
14
+ After significant development sessions, update with new learnings (patterns, pitfalls, best practices). Keep this file concise (~150 lines) and move detailed topics to `.claude/rules/`.
15
+
16
+ ## Living Documentation
17
+
18
+ The following documents must be kept up to date when making changes that affect them:
19
+
20
+ | Document | Update when... |
21
+ |----------|---------------|
22
+ | `docs/REQUEST-LIFECYCLE.md` | Adding/changing decorators, interceptors, guards, pipes, middleware, plugins, services, modules, or the request/response flow |
23
+ | `migration-guides/` | Releasing MINOR or MAJOR versions (see `.claude/rules/migration-guides.md`) |
24
+ | `.claude/rules/configurable-features.md` | Adding new configurable features |
25
+
26
+ **Rule:** When a code change adds, removes, or modifies a feature listed in `docs/REQUEST-LIFECYCLE.md` (Features Overview, diagrams, decorator reference, configuration, etc.), update the document in the same commit or PR.
27
+
28
+ ## Project Overview
29
+
30
+ **@lenne.tech/nest-server** - An extension layer on top of NestJS for building server applications with GraphQL and MongoDB.
31
+
32
+ - **NPM**: https://www.npmjs.com/package/@lenne.tech/nest-server
33
+ - **GitHub**: https://github.com/lenneTech/nest-server
34
+ - **Starter Project**: https://github.com/lenneTech/nest-server-starter (reference for migrations)
35
+ - **CLI**: https://github.com/lenneTech/cli (`lt server module <Name>`)
36
+ - **Package Manager**: pnpm (development only, published to npm registry)
37
+
38
+ ## Common Development Commands
39
+
40
+ ```bash
41
+ # Building & Running
42
+ pnpm run build # Build (outputs to dist/)
43
+ pnpm start # Start in local mode
44
+ pnpm run start:dev # Development mode with watch
45
+
46
+ # Testing (ALWAYS run before completing changes)
47
+ pnpm test # Run E2E tests (Vitest)
48
+ pnpm run test:cov # With coverage
49
+ pnpm run test:e2e-doh # Debug open handles
50
+ pnpm run test:cleanup # Remove leftover test artifacts (.txt, .bin)
51
+
52
+ # Linting & Formatting
53
+ pnpm run lint # ESLint check
54
+ pnpm run lint:fix # Auto-fix
55
+ pnpm run format # Prettier format
56
+
57
+ # Package Development
58
+ pnpm run build:dev # Build for local development (use with pnpm link)
59
+ pnpm run reinit # Clean reinstall + tests + build
60
+ ```
61
+
62
+ ## Code Architecture
63
+
64
+ **Two-Layer Structure:**
65
+ - `src/core/` - Reusable framework components (exported)
66
+ - `src/server/` - Internal test implementation (not exported)
67
+ - `src/index.ts` - Public API exports
68
+
69
+ **Key Components:**
70
+ - `CoreModule` - Dynamic module with GraphQL (optional, disable via `graphQl: false`), MongoDB, security
71
+ - `src/core/common/` - Decorators, helpers, interceptors, services
72
+ - `src/core/modules/` - Auth, BetterAuth, ErrorCode, File, HealthCheck, Migrate, SystemSetup, Tus, User
73
+
74
+ See `.claude/rules/architecture.md` for detailed documentation.
75
+ See [`docs/REQUEST-LIFECYCLE.md`](docs/REQUEST-LIFECYCLE.md) for the complete request lifecycle, security architecture, and interceptor/decorator reference.
76
+
77
+ ## Development Guidelines
78
+
79
+ ### Core Principles
80
+
81
+ 1. **Module Inheritance Pattern** - Extend through inheritance, not hooks/events
82
+ - See `.claude/rules/module-inheritance.md`
83
+
84
+ 2. **Dynamic Integration** - Components opt-in, configurable, no package modification required
85
+
86
+ 3. **Backward Compatibility** - Changes affect all consuming projects
87
+
88
+ 4. **Test Coverage** - All changes must pass `pnpm test`
89
+
90
+ 5. **Export Management** - New public components → `src/index.ts`
91
+
92
+ ### Role System (Critical)
93
+
94
+ System roles (`S_` prefix) are runtime checks only - **NEVER store in user.roles**:
95
+
96
+ ```typescript
97
+ @Roles(RoleEnum.S_USER) // Correct: runtime check
98
+ roles: [RoleEnum.S_USER] // WRONG: never store S_ roles!
99
+ roles: [RoleEnum.ADMIN] // Correct: real role
100
+ ```
101
+
102
+ See `.claude/rules/role-system.md` for complete documentation.
103
+
104
+ ### Versioning
105
+
106
+ `MAJOR.MINOR.PATCH` where MAJOR = NestJS version, MINOR = breaking changes, PATCH = non-breaking.
107
+
108
+ See `.claude/rules/versioning.md` for release process.
109
+
110
+ ## Environment Configuration
111
+
112
+ ```typescript
113
+ // config.env.ts supports:
114
+ - Direct environment variables
115
+ - NEST_SERVER_CONFIG JSON variable
116
+ - NSC__* prefixed variables
117
+ ```
118
+
119
+ **Key areas:** JWT, MongoDB, GraphQL, email, security, betterAuth
120
+
121
+ ## Debugging & Troubleshooting
122
+
123
+ | Issue | Solution |
124
+ |-------|----------|
125
+ | Tests timeout | Ensure MongoDB running on localhost:27017 |
126
+ | GraphQL introspection fails | Check `config.env.ts` introspection setting |
127
+ | Module not found after adding | Verify export in `src/index.ts`, run `pnpm run build` |
128
+ | Open handles in tests | Run `pnpm run test:e2e-doh` |
129
+
130
+ ## Best Practices
131
+
132
+ 1. **All code, comments, documentation in English**
133
+ 2. **Run tests before completing changes** - `pnpm test`
134
+ 3. **Follow existing patterns** for consistency
135
+ 4. **Never store S_ roles** in user.roles array
136
+ 5. **Use Module Inheritance Pattern** for core modules
137
+ 6. **Document breaking changes** in commits
138
+ 7. **Integration Checklists for Core Modules** - Every core module requiring project integration needs `INTEGRATION-CHECKLIST.md` (see `.claude/rules/core-modules.md`)
139
+ 8. **Don't add redundant @UseGuards** - `@Roles()` already handles JWT auth (see `.claude/rules/role-system.md`)
140
+ 9. **Fixed package versions only** - Never use `^`, `~`, or ranges in package.json (see `.claude/rules/package-management.md`)
141
+
142
+ ## Migration Guides
143
+
144
+ When releasing MINOR or MAJOR versions, create migration guides in `migration-guides/`:
145
+ - Use `migration-guides/TEMPLATE.md` as starting point
146
+ - Always analyze `src/server/` and [nest-server-starter](https://github.com/lenneTech/nest-server-starter)
147
+ - Ask developer for additional projects to analyze
148
+ - See `.claude/rules/migration-guides.md` for complete process
149
+
150
+ ## Modular Rules
151
+
152
+ Detailed documentation in `.claude/rules/`:
153
+
154
+ | File | Content |
155
+ |------|---------|
156
+ | `module-inheritance.md` | Core architectural pattern for extending modules |
157
+ | `role-system.md` | Role system, S_ prefix rules, @Roles vs @UseGuards |
158
+ | `architecture.md` | Detailed code architecture |
159
+ | `testing.md` | Test configuration and best practices |
160
+ | `versioning.md` | Version strategy and release process |
161
+ | `core-modules.md` | Path-scoped rules for `src/core/modules/` incl. Integration Checklist requirements |
162
+ | `better-auth.md` | **Security-critical**: Better-Auth module rules (standard compliance, security, testing) |
163
+ | `module-deprecation.md` | Legacy Auth → BetterAuth migration roadmap |
164
+ | `migration-guides.md` | Process for creating version migration guides |
165
+ | `configurable-features.md` | Configuration patterns: "Presence implies enabled" and "Boolean shorthand" (`true` / `{}`) |
166
+ | `package-management.md` | Fixed package versions only - no `^`, `~`, or ranges |
167
+
168
+ ## In-Depth Documentation
169
+
170
+ | File | Content |
171
+ |------|---------|
172
+ | [`docs/REQUEST-LIFECYCLE.md`](docs/REQUEST-LIFECYCLE.md) | Complete request lifecycle, security architecture, interceptor chain, decorator reference, CrudService pipeline, Safety Net, diagrams |
@@ -317,4 +317,14 @@ interface IBetterAuthWithPasskey extends IBetterAuthBase {
317
317
  passkey: IBetterAuthPasskeyEnabled;
318
318
  trustedOrigins: string[];
319
319
  }
320
+ export interface ICoreModuleOverrides {
321
+ betterAuth?: {
322
+ controller?: Type<any>;
323
+ resolver?: Type<any>;
324
+ };
325
+ errorCode?: {
326
+ controller?: Type<any>;
327
+ service?: Type<any>;
328
+ };
329
+ }
320
330
  export {};
@@ -533,32 +533,32 @@ let CoreBetterAuthUserMapper = class CoreBetterAuthUserMapper {
533
533
  const fullyMigratedUsers = usersWithBoth[0]?.count || 0;
534
534
  const pendingMigrationUsers = totalUsers - fullyMigratedUsers;
535
535
  const migrationPercentage = totalUsers > 0 ? Math.round((fullyMigratedUsers / totalUsers) * 100 * 100) / 100 : 0;
536
- const pendingUsers = await usersCollection
537
- .aggregate([
538
- {
539
- $lookup: {
540
- as: 'accounts',
541
- foreignField: 'userId',
542
- from: 'account',
543
- localField: '_id',
544
- },
545
- },
546
- {
547
- $match: {
548
- $or: [
549
- { iamId: { $exists: false } },
550
- { iamId: null },
551
- {
552
- $and: [{ iamId: { $exists: true, $ne: null } }, { 'accounts.providerId': { $ne: 'credential' } }],
553
- },
554
- ],
555
- },
556
- },
557
- { $limit: 100 },
558
- { $project: { email: 1 } },
559
- ])
536
+ const usersWithoutIamId = await usersCollection
537
+ .find({ $or: [{ iamId: { $exists: false } }, { iamId: null }] })
538
+ .limit(100)
539
+ .project({ email: 1 })
560
540
  .toArray();
561
- const pendingUserEmails = pendingUsers.map((u) => u.email).filter(Boolean);
541
+ const remaining = 100 - usersWithoutIamId.length;
542
+ let usersWithIamButNoAccount = [];
543
+ if (remaining > 0) {
544
+ usersWithIamButNoAccount = await usersCollection
545
+ .aggregate([
546
+ { $match: { iamId: { $exists: true, $ne: null } } },
547
+ {
548
+ $lookup: {
549
+ as: 'accounts',
550
+ foreignField: 'userId',
551
+ from: 'account',
552
+ localField: '_id',
553
+ },
554
+ },
555
+ { $match: { 'accounts.providerId': { $ne: 'credential' } } },
556
+ { $limit: remaining },
557
+ { $project: { email: 1 } },
558
+ ])
559
+ .toArray();
560
+ }
561
+ const pendingUserEmails = [...usersWithoutIamId, ...usersWithIamButNoAccount].map((u) => u.email).filter(Boolean);
562
562
  const canDisableLegacyAuth = totalUsers > 0 && fullyMigratedUsers === totalUsers;
563
563
  return {
564
564
  canDisableLegacyAuth,