@lenne.tech/nest-server 11.22.0 → 11.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/configurable-features.md +1 -0
- package/.claude/rules/framework-compatibility.md +79 -0
- package/CLAUDE.md +60 -0
- package/FRAMEWORK-API.md +235 -0
- package/dist/core/common/decorators/restricted.decorator.js +21 -4
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +1 -0
- package/dist/core/common/services/crud.service.d.ts +4 -1
- package/dist/core/common/services/crud.service.js +24 -2
- package/dist/core/common/services/crud.service.js.map +1 -1
- package/dist/core/common/services/module.service.d.ts +3 -2
- package/dist/core/common/services/module.service.js +43 -20
- package/dist/core/common/services/module.service.js.map +1 -1
- package/dist/core/common/services/request-context.service.d.ts +3 -0
- package/dist/core/common/services/request-context.service.js +12 -0
- package/dist/core/common/services/request-context.service.js.map +1 -1
- package/dist/server/modules/file/file-info.model.d.ts +1 -5
- package/dist/server/modules/user/user.model.d.ts +1 -5
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +25 -2
- package/docs/native-driver-security.md +153 -0
- package/docs/process-performance-optimization.md +493 -0
- package/migration-guides/11.22.0-to-11.22.1.md +105 -0
- package/migration-guides/11.22.x-to-11.23.0.md +235 -0
- package/package.json +33 -31
- package/src/core/common/decorators/restricted.decorator.ts +44 -4
- package/src/core/common/interfaces/server-options.interface.ts +8 -0
- package/src/core/common/services/crud.service.ts +77 -5
- package/src/core/common/services/module.service.ts +96 -35
- package/src/core/common/services/request-context.service.ts +47 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Migration Guide: 11.22.0 → 11.22.1
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **New Features** | `FRAMEWORK-API.md` — auto-generated compact API reference shipped with npm package |
|
|
9
|
+
| **Bugfixes** | Security override updates (minimatch, undici, srvx, path-to-regexp) |
|
|
10
|
+
| **Dependency Updates** | NestJS 11.1.18, mongoose 9.4.1 |
|
|
11
|
+
| **Migration Effort** | ~2 minutes (optional, backward compatible) |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Migration (No Breaking Changes)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Update package
|
|
19
|
+
pnpm add @lenne.tech/nest-server@11.22.1
|
|
20
|
+
|
|
21
|
+
# Verify build
|
|
22
|
+
pnpm run build
|
|
23
|
+
|
|
24
|
+
# Run tests
|
|
25
|
+
pnpm test
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No code changes required. All existing patterns continue to work.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## What's New in 11.22.1
|
|
33
|
+
|
|
34
|
+
### 1. FRAMEWORK-API.md — Auto-Generated API Reference
|
|
35
|
+
|
|
36
|
+
The npm package now ships a compact, machine-readable API reference (`FRAMEWORK-API.md`) that is
|
|
37
|
+
auto-generated from source code during `pnpm run build`. It contains:
|
|
38
|
+
|
|
39
|
+
- `CoreModule.forRoot()` overload signatures
|
|
40
|
+
- All configuration interfaces (`IServerOptions`, `IBetterAuth`, `IMultiTenancy`, etc.) with types and defaults
|
|
41
|
+
- `ServiceOptions` interface
|
|
42
|
+
- `CrudService` public method signatures
|
|
43
|
+
- Core module listing with documentation status
|
|
44
|
+
|
|
45
|
+
**For AI-assisted development:** Update your project's `CLAUDE.md` to reference this file:
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
## Framework: @lenne.tech/nest-server
|
|
49
|
+
When working with framework features, read source and docs from:
|
|
50
|
+
node_modules/@lenne.tech/nest-server/CLAUDE.md
|
|
51
|
+
node_modules/@lenne.tech/nest-server/FRAMEWORK-API.md
|
|
52
|
+
node_modules/@lenne.tech/nest-server/src/core/modules/*/INTEGRATION-CHECKLIST.md
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This enables AI tools to quickly understand the framework's complete API surface without
|
|
56
|
+
reading every source file individually.
|
|
57
|
+
|
|
58
|
+
### 2. Dependency Updates
|
|
59
|
+
|
|
60
|
+
| Package | From | To | Type |
|
|
61
|
+
|---------|------|----|------|
|
|
62
|
+
| `@nestjs/common` | 11.1.17 | 11.1.18 | patch |
|
|
63
|
+
| `@nestjs/core` | 11.1.17 | 11.1.18 | patch |
|
|
64
|
+
| `@nestjs/platform-express` | 11.1.17 | 11.1.18 | patch |
|
|
65
|
+
| `@nestjs/websockets` | 11.1.17 | 11.1.18 | patch |
|
|
66
|
+
| `@nestjs/testing` (dev) | 11.1.17 | 11.1.18 | patch |
|
|
67
|
+
| `mongoose` | 9.3.3 | 9.4.1 | minor |
|
|
68
|
+
|
|
69
|
+
### 3. Security Override Updates
|
|
70
|
+
|
|
71
|
+
Updated security overrides to latest patched versions:
|
|
72
|
+
|
|
73
|
+
| Override | From | To |
|
|
74
|
+
|----------|------|----|
|
|
75
|
+
| `minimatch` | 3.1.4 / 9.0.7 / 10.2.4 | 3.1.5 / 9.0.9 / 10.2.5 |
|
|
76
|
+
| `undici` | 7.24.3 | 7.24.7 |
|
|
77
|
+
| `srvx` | 0.11.13 | 0.11.15 |
|
|
78
|
+
| `path-to-regexp` | 8.4.1 | 8.4.2 |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Compatibility Notes
|
|
83
|
+
|
|
84
|
+
| Pattern | Status |
|
|
85
|
+
|---------|--------|
|
|
86
|
+
| All existing `CoreModule.forRoot()` patterns | Works unchanged |
|
|
87
|
+
| `ICoreModuleOverrides` from 11.22.0 | Works unchanged |
|
|
88
|
+
| mongoose queries and schemas | Compatible (9.4.1 is backward compatible) |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Troubleshooting
|
|
93
|
+
|
|
94
|
+
| Issue | Solution |
|
|
95
|
+
|-------|----------|
|
|
96
|
+
| `FRAMEWORK-API.md` not found in `node_modules` | Ensure `@lenne.tech/nest-server@11.22.1` is installed |
|
|
97
|
+
| Override conflicts after update | Run `pnpm install` to apply updated lockfile |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## References
|
|
102
|
+
|
|
103
|
+
- [FRAMEWORK-API.md](../FRAMEWORK-API.md) — The new API reference
|
|
104
|
+
- [Framework Compatibility Rules](../.claude/rules/framework-compatibility.md) — Maintenance obligations
|
|
105
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) (reference implementation)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Migration Guide: 11.22.x → 11.23.0
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | `mainDbModel.collection` and `mainDbModel.db` blocked via TypeScript (`SafeModel`) |
|
|
8
|
+
| **New Features** | `process()` depth-based optimization, `debugProcessInput` config, `getNativeCollection(reason)`, `getNativeConnection(reason)`, restricted metadata cache |
|
|
9
|
+
| **Performance** | ~70% less memory on nested service cascades, eliminated redundant `JSON.stringify` and recursive `process()` calls |
|
|
10
|
+
| **Migration Effort** | ~5 minutes if using `mainDbModel.db`; 0 minutes otherwise |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quick Migration (No `mainDbModel.db` Usage)
|
|
15
|
+
|
|
16
|
+
If your project does **not** access `this.mainDbModel.collection` or `this.mainDbModel.db` directly:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Update package
|
|
20
|
+
pnpm add @lenne.tech/nest-server@11.23.0
|
|
21
|
+
|
|
22
|
+
# Verify build
|
|
23
|
+
pnpm run build
|
|
24
|
+
|
|
25
|
+
# Run tests
|
|
26
|
+
pnpm test
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
No code changes required. All performance optimizations activate automatically.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Breaking Changes
|
|
34
|
+
|
|
35
|
+
### `mainDbModel` Type Changed to `SafeModel` (TypeScript Only)
|
|
36
|
+
|
|
37
|
+
`mainDbModel` is now typed as `SafeModel<T>` which is `Omit<Model<T>, 'collection' | 'db'>`. This is a **compile-time-only** change — runtime behavior is identical.
|
|
38
|
+
|
|
39
|
+
**What breaks:** Direct access to `.collection` or `.db` on `this.mainDbModel` produces a TypeScript error.
|
|
40
|
+
|
|
41
|
+
**What does NOT break:** All Mongoose Model methods (`find`, `findById`, `insertMany`, `updateOne`, `aggregate`, `bulkWrite`, etc.) continue to work unchanged.
|
|
42
|
+
|
|
43
|
+
#### If You Use `mainDbModel.collection`
|
|
44
|
+
|
|
45
|
+
**Before:**
|
|
46
|
+
```typescript
|
|
47
|
+
// Direct native collection access — bypasses ALL Mongoose plugins
|
|
48
|
+
await this.mainDbModel.collection.insertOne(doc);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**After (Option A — use Mongoose Model method):**
|
|
52
|
+
```typescript
|
|
53
|
+
// Mongoose method — plugins (Tenant, Audit, RoleGuard, Password) fire correctly
|
|
54
|
+
await this.mainDbModel.insertMany([doc]);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**After (Option B — intentional native access with logging):**
|
|
58
|
+
```typescript
|
|
59
|
+
// Logged escape hatch — requires justification
|
|
60
|
+
const col = this.getNativeCollection('Migration: bulk-import historical data without tenant context');
|
|
61
|
+
await col.insertOne(doc);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### If You Use `mainDbModel.db`
|
|
65
|
+
|
|
66
|
+
**Before:**
|
|
67
|
+
```typescript
|
|
68
|
+
// Native DB access via model — bypasses ALL Mongoose plugins
|
|
69
|
+
const count = await this.mainDbModel.db.db.collection('chatmessages').countDocuments({ ... });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**After (Option A — inject the target model):**
|
|
73
|
+
```typescript
|
|
74
|
+
// Inject the model via @InjectModel and use Mongoose methods
|
|
75
|
+
constructor(@InjectModel('ChatMessage') private chatMessageModel: Model<ChatMessageDocument>) {}
|
|
76
|
+
|
|
77
|
+
const count = await this.chatMessageModel.countDocuments({ ... });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**After (Option B — intentional native access with logging):**
|
|
81
|
+
```typescript
|
|
82
|
+
// Logged escape hatch — requires justification
|
|
83
|
+
const conn = this.getNativeConnection('Statistics: count chatmessages across all tenants');
|
|
84
|
+
const count = await conn.db.collection('chatmessages').countDocuments({ ... });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### If You Use `getModel()`
|
|
88
|
+
|
|
89
|
+
`getModel()` continues to return the full `Model` type (including `.collection` and `.db`). No change needed.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## What's New in 11.23.0
|
|
94
|
+
|
|
95
|
+
### 1. process() Pipeline Performance Optimization
|
|
96
|
+
|
|
97
|
+
The `process()` method in `ModuleService` now tracks nesting depth via `RequestContext`. On nested calls (service cascades like A.create → B.create → C.create), redundant pipeline steps are skipped:
|
|
98
|
+
|
|
99
|
+
| Step | Outermost Call | Nested Calls |
|
|
100
|
+
|------|---------------|--------------|
|
|
101
|
+
| prepareInput | Runs | Runs |
|
|
102
|
+
| checkRights (input) | Runs | Runs |
|
|
103
|
+
| serviceFunc | Runs | Runs |
|
|
104
|
+
| processFieldSelection (populate) | Runs | **Skipped** (unless explicit `populate` is set) |
|
|
105
|
+
| prepareOutput (model mapping) | Runs | **Skipped** (secret removal still active) |
|
|
106
|
+
| checkRights (output) | Runs | **Skipped** |
|
|
107
|
+
|
|
108
|
+
**Security is maintained** through three layers:
|
|
109
|
+
1. Input checkRights always runs at every depth
|
|
110
|
+
2. Output checkRights runs at depth 0 (outermost call)
|
|
111
|
+
3. `CheckSecurityInterceptor` (Safety Net) runs on the final HTTP response
|
|
112
|
+
|
|
113
|
+
**Estimated savings for an 8-level service cascade:** ~70% less memory (~120 KB instead of ~400 KB).
|
|
114
|
+
|
|
115
|
+
No configuration needed — this activates automatically.
|
|
116
|
+
|
|
117
|
+
### 2. Lean Query for Rights Checking
|
|
118
|
+
|
|
119
|
+
The `process()` pipeline previously called `this.get()` to resolve `dbObject` for authorization checks, which triggered a **recursive** `process()` call. It now uses a direct lean query:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Before: recursive process() call with full pipeline
|
|
123
|
+
const dbObject = await this.get(id);
|
|
124
|
+
|
|
125
|
+
// After: single lean query + map
|
|
126
|
+
const rawDoc = await this.mainDbModel.findById(id).lean().exec();
|
|
127
|
+
const dbObject = mainModelConstructor.map(rawDoc);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This preserves ALL fields (including `createdBy`) needed for `S_CREATOR` and `S_SELF` checks, while avoiding the overhead of a full pipeline pass.
|
|
131
|
+
|
|
132
|
+
### 3. `debugProcessInput` Configuration Option
|
|
133
|
+
|
|
134
|
+
The previous `JSON.stringify` debug comparison that ran on **every** `process()` call is now behind a config flag:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// config.env.ts — enable only for debugging
|
|
138
|
+
{
|
|
139
|
+
debugProcessInput: true, // default: false
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
When disabled (default), two `JSON.stringify` calls per `process()` invocation are eliminated.
|
|
144
|
+
|
|
145
|
+
### 4. Restricted Metadata Cache
|
|
146
|
+
|
|
147
|
+
`getRestricted()` results (from `@Restricted()` decorators) are now cached per class + property. Since decorator metadata is static (set at compile time and never changes), this eliminates hundreds of `Reflect.getMetadata()` lookups per request for objects with many properties.
|
|
148
|
+
|
|
149
|
+
### 5. Native Driver Security (`SafeModel`, `getNativeCollection`, `getNativeConnection`)
|
|
150
|
+
|
|
151
|
+
Two new protected methods in `CrudService` provide logged escape hatches for legitimate native MongoDB driver access:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Get the native MongoDB Collection for this model's collection
|
|
155
|
+
protected getNativeCollection(reason: string): Collection
|
|
156
|
+
|
|
157
|
+
// Get the Mongoose Connection (for cross-collection access, schema-less collections)
|
|
158
|
+
protected getNativeConnection(reason: string): Connection
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Both methods:
|
|
162
|
+
- Require a non-empty justification string
|
|
163
|
+
- Log a `[SECURITY]` warning on every call
|
|
164
|
+
- Throw an `Error` if no reason is provided
|
|
165
|
+
|
|
166
|
+
The `SafeModel<T>` type is exported for use in custom service implementations.
|
|
167
|
+
|
|
168
|
+
### 6. RequestContext.processDepth API
|
|
169
|
+
|
|
170
|
+
New methods for tracking `process()` nesting depth:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Get current depth (0 = outermost or no process() call)
|
|
174
|
+
RequestContext.getProcessDepth(): number
|
|
175
|
+
|
|
176
|
+
// Run a function with incremented depth
|
|
177
|
+
RequestContext.runWithIncrementedProcessDepth<T>(fn: () => T): T
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
These are used internally by `process()` but are also available for custom pipeline implementations.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Compatibility Notes
|
|
185
|
+
|
|
186
|
+
| Pattern | Status |
|
|
187
|
+
|---------|--------|
|
|
188
|
+
| All `CoreModule.forRoot()` patterns | Works unchanged |
|
|
189
|
+
| `CrudService` subclasses | Works unchanged (SafeModel is transparent for Mongoose methods) |
|
|
190
|
+
| Custom `process()` calls with `populate` option | Works unchanged (explicit populate overrides nested skip) |
|
|
191
|
+
| `getModel()` callers | Works unchanged (returns full Model type) |
|
|
192
|
+
| `@Restricted()` decorators | Works unchanged (cache is transparent) |
|
|
193
|
+
| Services using `this.mainDbModel.find/save/update/etc.` | Works unchanged |
|
|
194
|
+
| Services using `this.mainDbModel.collection` | **TypeScript error** — use `getNativeCollection(reason)` or Mongoose methods |
|
|
195
|
+
| Services using `this.mainDbModel.db` | **TypeScript error** — use `getNativeConnection(reason)` or inject target model |
|
|
196
|
+
| Services using `@InjectConnection` | Works unchanged (Connection is not affected by SafeModel) |
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Troubleshooting
|
|
201
|
+
|
|
202
|
+
| Issue | Solution |
|
|
203
|
+
|-------|----------|
|
|
204
|
+
| TypeScript error on `mainDbModel.collection` | Replace with Mongoose Model method or `this.getNativeCollection('reason')` |
|
|
205
|
+
| TypeScript error on `mainDbModel.db` | Replace with `this.getNativeConnection('reason')` or inject the target model via `@InjectModel` |
|
|
206
|
+
| `getModel()` return type mismatch | `getModel()` returns full `Model` — no change needed |
|
|
207
|
+
| `processFieldSelection` type error in custom override | Update parameter type to accept `SafeModel` (see `ModuleService.processFieldSelection`) |
|
|
208
|
+
| Debug logs from `prepareInput` disappeared | Set `debugProcessInput: true` in config to re-enable |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Module Documentation
|
|
213
|
+
|
|
214
|
+
### Native Driver Security
|
|
215
|
+
|
|
216
|
+
- **Security Policy:** [`docs/native-driver-security.md`](../docs/native-driver-security.md)
|
|
217
|
+
- **Performance Optimization:** [`docs/process-performance-optimization.md`](../docs/process-performance-optimization.md)
|
|
218
|
+
|
|
219
|
+
### Affected Source Files
|
|
220
|
+
|
|
221
|
+
| File | Changes |
|
|
222
|
+
|------|---------|
|
|
223
|
+
| `src/core/common/services/module.service.ts` | `SafeModel` type, `mainDbModel` type change, `process()` depth optimization, lean query, debug config |
|
|
224
|
+
| `src/core/common/services/crud.service.ts` | `getNativeCollection(reason)`, `getNativeConnection(reason)`, `getModel()` cast |
|
|
225
|
+
| `src/core/common/services/request-context.service.ts` | `processDepth` field, `getProcessDepth()`, `runWithIncrementedProcessDepth()` |
|
|
226
|
+
| `src/core/common/decorators/restricted.decorator.ts` | Metadata cache, `_.uniq()` optimization |
|
|
227
|
+
| `src/core/common/interfaces/server-options.interface.ts` | `debugProcessInput` config option |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## References
|
|
232
|
+
|
|
233
|
+
- [Native Driver Security Policy](../docs/native-driver-security.md)
|
|
234
|
+
- [process() Performance Optimization](../docs/process-performance-optimization.md)
|
|
235
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) (reference implementation)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.23.0",
|
|
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,26 +79,27 @@
|
|
|
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.
|
|
82
|
-
"@nestjs/core": "11.1.
|
|
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.
|
|
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.
|
|
92
|
+
"@nestjs/websockets": "11.1.18",
|
|
92
93
|
"@tus/file-store": "2.0.0",
|
|
93
94
|
"@tus/server": "2.3.0",
|
|
95
|
+
"@types/supertest": "7.2.0",
|
|
94
96
|
"bcrypt": "6.0.0",
|
|
95
97
|
"better-auth": "1.5.5",
|
|
96
98
|
"class-transformer": "0.5.1",
|
|
97
99
|
"class-validator": "0.15.1",
|
|
98
100
|
"compression": "1.8.1",
|
|
99
101
|
"cookie-parser": "1.4.7",
|
|
100
|
-
"dotenv": "17.4.
|
|
102
|
+
"dotenv": "17.4.1",
|
|
101
103
|
"ejs": "5.0.1",
|
|
102
104
|
"express": "5.2.1",
|
|
103
105
|
"graphql": "16.13.2",
|
|
@@ -108,56 +110,55 @@
|
|
|
108
110
|
"json-to-graphql-query": "2.3.0",
|
|
109
111
|
"lodash": "4.18.1",
|
|
110
112
|
"mongodb": "7.1.1",
|
|
111
|
-
"mongoose": "9.
|
|
113
|
+
"mongoose": "9.4.1",
|
|
112
114
|
"multer": "2.1.1",
|
|
113
115
|
"node-mailjet": "6.0.11",
|
|
114
|
-
"nodemailer": "8.0.
|
|
116
|
+
"nodemailer": "8.0.5",
|
|
115
117
|
"passport": "0.7.0",
|
|
116
118
|
"passport-jwt": "4.0.1",
|
|
117
119
|
"reflect-metadata": "0.2.2",
|
|
118
120
|
"rfdc": "1.4.1",
|
|
119
121
|
"rxjs": "7.8.2",
|
|
122
|
+
"supertest": "7.2.2",
|
|
120
123
|
"ts-morph": "27.0.2",
|
|
121
124
|
"yuml-diagram": "1.2.0"
|
|
122
125
|
},
|
|
123
126
|
"devDependencies": {
|
|
124
127
|
"@compodoc/compodoc": "1.2.1",
|
|
125
|
-
"@nestjs/cli": "11.0.
|
|
128
|
+
"@nestjs/cli": "11.0.18",
|
|
126
129
|
"@nestjs/schematics": "11.0.10",
|
|
127
|
-
"@nestjs/testing": "11.1.
|
|
130
|
+
"@nestjs/testing": "11.1.18",
|
|
128
131
|
"@swc/cli": "0.8.1",
|
|
129
|
-
"@swc/core": "1.15.
|
|
132
|
+
"@swc/core": "1.15.24",
|
|
130
133
|
"@types/compression": "1.8.1",
|
|
131
134
|
"@types/cookie-parser": "1.4.10",
|
|
132
135
|
"@types/ejs": "3.1.5",
|
|
133
136
|
"@types/express": "5.0.6",
|
|
134
137
|
"@types/lodash": "4.17.24",
|
|
135
138
|
"@types/multer": "2.1.0",
|
|
136
|
-
"@types/node": "25.5.
|
|
137
|
-
"@types/nodemailer": "
|
|
139
|
+
"@types/node": "25.5.2",
|
|
140
|
+
"@types/nodemailer": "8.0.0",
|
|
138
141
|
"@types/passport": "1.0.17",
|
|
139
|
-
"@
|
|
140
|
-
"@vitest/
|
|
141
|
-
"@vitest/ui": "4.1.2",
|
|
142
|
+
"@vitest/coverage-v8": "4.1.3",
|
|
143
|
+
"@vitest/ui": "4.1.3",
|
|
142
144
|
"ansi-colors": "4.1.3",
|
|
143
145
|
"find-file-up": "2.0.1",
|
|
144
146
|
"husky": "9.1.7",
|
|
145
147
|
"nodemon": "3.1.14",
|
|
146
148
|
"npm-watch": "0.13.0",
|
|
147
149
|
"otpauth": "9.5.0",
|
|
148
|
-
"oxfmt": "0.
|
|
149
|
-
"oxlint": "1.
|
|
150
|
+
"oxfmt": "0.44.0",
|
|
151
|
+
"oxlint": "1.59.0",
|
|
150
152
|
"rimraf": "6.1.3",
|
|
151
|
-
"supertest": "7.2.2",
|
|
152
153
|
"ts-node": "10.9.2",
|
|
153
154
|
"tsconfig-paths": "4.2.0",
|
|
154
155
|
"tus-js-client": "4.3.1",
|
|
155
156
|
"typescript": "5.9.3",
|
|
156
157
|
"unplugin-swc": "1.5.9",
|
|
157
|
-
"vite": "7.3.
|
|
158
|
+
"vite": "7.3.2",
|
|
158
159
|
"vite-plugin-node": "7.0.0",
|
|
159
160
|
"vite-tsconfig-paths": "6.1.1",
|
|
160
|
-
"vitest": "4.1.
|
|
161
|
+
"vitest": "4.1.3"
|
|
161
162
|
},
|
|
162
163
|
"main": "dist/index.js",
|
|
163
164
|
"types": "dist/index.d.ts",
|
|
@@ -170,6 +171,7 @@
|
|
|
170
171
|
"src/**/*",
|
|
171
172
|
"bin/**/*",
|
|
172
173
|
"CLAUDE.md",
|
|
174
|
+
"FRAMEWORK-API.md",
|
|
173
175
|
".claude/rules/**/*",
|
|
174
176
|
"docs/**/*",
|
|
175
177
|
"migration-guides/**/*"
|
|
@@ -177,26 +179,26 @@
|
|
|
177
179
|
"watch": {
|
|
178
180
|
"build:dev": "src"
|
|
179
181
|
},
|
|
180
|
-
"packageManager": "pnpm@10.
|
|
182
|
+
"packageManager": "pnpm@10.33.0",
|
|
181
183
|
"pnpm": {
|
|
182
184
|
"overrides": {
|
|
183
|
-
"minimatch@<3.1.
|
|
184
|
-
"minimatch@>=9.0.0 <9.0.
|
|
185
|
-
"minimatch@>=10.0.0 <10.2.
|
|
186
|
-
"rollup@>=4.0.0 <4.60.1": "4.60.1",
|
|
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",
|
|
187
188
|
"ajv@<6.14.0": "6.14.0",
|
|
188
189
|
"ajv@>=7.0.0-alpha.0 <8.18.0": "8.18.0",
|
|
189
|
-
"undici@>=7.0.0 <7.24.
|
|
190
|
-
"srvx@<0.11.
|
|
190
|
+
"undici@>=7.0.0 <7.24.7": "7.24.7",
|
|
191
|
+
"srvx@<0.11.15": "0.11.15",
|
|
191
192
|
"handlebars@>=4.0.0 <4.7.9": "4.7.9",
|
|
192
193
|
"brace-expansion@<1.1.13": "1.1.13",
|
|
193
194
|
"brace-expansion@>=4.0.0 <5.0.5": "5.0.5",
|
|
194
195
|
"picomatch@<2.3.2": "2.3.2",
|
|
195
196
|
"picomatch@>=4.0.0 <4.0.4": "4.0.4",
|
|
196
|
-
"path-to-regexp@>=8.0.0 <8.4.
|
|
197
|
+
"path-to-regexp@>=8.0.0 <8.4.2": "8.4.2",
|
|
197
198
|
"kysely@>=0.26.0 <0.28.15": "0.28.15",
|
|
198
199
|
"lodash@>=4.0.0 <4.18.0": "4.18.1",
|
|
199
|
-
"defu@<=6.1.
|
|
200
|
+
"defu@<=6.1.6": "6.1.7",
|
|
201
|
+
"vite@>=7.0.0 <7.3.2": "7.3.2"
|
|
200
202
|
},
|
|
201
203
|
"onlyBuiltDependencies": [
|
|
202
204
|
"bcrypt",
|
|
@@ -44,6 +44,19 @@ export const Restricted = (...rolesOrMember: RestrictedType): ClassDecorator & P
|
|
|
44
44
|
return Reflect.metadata(restrictedMetaKey, rolesOrMember);
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Cache for Restricted metadata — decorators are static, metadata never changes at runtime.
|
|
49
|
+
* WeakMap<CacheTarget, Map<propertyKey | '__class__', RestrictedType>>
|
|
50
|
+
*
|
|
51
|
+
* Uses WeakMap so that dynamically-generated or hot-reloaded class constructors can be GC'd
|
|
52
|
+
* when no longer reachable (prevents unbounded growth in test suites and dev watch mode).
|
|
53
|
+
*
|
|
54
|
+
* CacheTarget is the class constructor (for instances) or the class itself (when object IS a constructor).
|
|
55
|
+
* This distinction is critical: getRestricted(data.constructor) passes a class as `object`,
|
|
56
|
+
* and (classFunction).constructor === Function for ALL classes — so we must use the class itself.
|
|
57
|
+
*/
|
|
58
|
+
const restrictedMetadataCache = new WeakMap<object, Map<string, RestrictedType>>();
|
|
59
|
+
|
|
47
60
|
/**
|
|
48
61
|
* Get restricted data for (property of) object
|
|
49
62
|
*/
|
|
@@ -51,10 +64,36 @@ export const getRestricted = (object: unknown, propertyKey?: string): Restricted
|
|
|
51
64
|
if (!object) {
|
|
52
65
|
return null;
|
|
53
66
|
}
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
|
|
68
|
+
// Determine cache target: use the class constructor for instances, the object itself for classes.
|
|
69
|
+
// When object IS a constructor (typeof === 'function'), using object.constructor would give Function
|
|
70
|
+
// for ALL classes, causing cache collisions.
|
|
71
|
+
const cacheTarget: object | undefined =
|
|
72
|
+
typeof object === 'function' ? (object as object) : (object as any).constructor;
|
|
73
|
+
if (!cacheTarget) {
|
|
74
|
+
return propertyKey
|
|
75
|
+
? Reflect.getMetadata(restrictedMetaKey, object, propertyKey)
|
|
76
|
+
: Reflect.getMetadata(restrictedMetaKey, object);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let classCache = restrictedMetadataCache.get(cacheTarget);
|
|
80
|
+
if (!classCache) {
|
|
81
|
+
classCache = new Map();
|
|
82
|
+
restrictedMetadataCache.set(cacheTarget, classCache);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const cacheKey = propertyKey || '__class__';
|
|
86
|
+
if (classCache.has(cacheKey)) {
|
|
87
|
+
return classCache.get(cacheKey);
|
|
56
88
|
}
|
|
57
|
-
|
|
89
|
+
|
|
90
|
+
// Cache miss: perform Reflect lookup and cache the result
|
|
91
|
+
const metadata = propertyKey
|
|
92
|
+
? Reflect.getMetadata(restrictedMetaKey, object, propertyKey)
|
|
93
|
+
: Reflect.getMetadata(restrictedMetaKey, object);
|
|
94
|
+
|
|
95
|
+
classCache.set(cacheKey, metadata);
|
|
96
|
+
return metadata;
|
|
58
97
|
};
|
|
59
98
|
|
|
60
99
|
/**
|
|
@@ -257,7 +296,8 @@ export const checkRestricted = (
|
|
|
257
296
|
|
|
258
297
|
// Check restricted
|
|
259
298
|
const restricted = getRestricted(data, propertyKey) || [];
|
|
260
|
-
const concatenatedRestrictions =
|
|
299
|
+
const concatenatedRestrictions =
|
|
300
|
+
config.mergeRoles && objectRestrictions.length ? _.uniq(objectRestrictions.concat(restricted)) : restricted;
|
|
261
301
|
const valid = validateRestricted(concatenatedRestrictions);
|
|
262
302
|
|
|
263
303
|
// Check rights
|
|
@@ -1109,6 +1109,14 @@ export interface IServerOptions {
|
|
|
1109
1109
|
CronExpression | CronJobConfigWithTimeZone | CronJobConfigWithUtcOffset | Date | Falsy | string
|
|
1110
1110
|
>;
|
|
1111
1111
|
|
|
1112
|
+
/**
|
|
1113
|
+
* When true, logs a debug message when prepareInput() changes the input type during process().
|
|
1114
|
+
* Enable only for debugging — has performance cost due to JSON.stringify on every process() call.
|
|
1115
|
+
*
|
|
1116
|
+
* @default false
|
|
1117
|
+
*/
|
|
1118
|
+
debugProcessInput?: boolean;
|
|
1119
|
+
|
|
1112
1120
|
/**
|
|
1113
1121
|
* SMTP and template configuration for sending emails
|
|
1114
1122
|
*/
|