@lenne.tech/nest-server 11.26.2 → 11.26.3
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/FRAMEWORK-API.md +1 -1
- package/dist/core/common/decorators/unified-field.decorator.js +2 -7
- package/dist/core/common/decorators/unified-field.decorator.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/migration-guides/11.26.2-to-11.26.3.md +186 -0
- package/package.json +1 -1
- package/src/core/common/decorators/unified-field.decorator.ts +13 -7
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Migration Guide: 11.26.2 → 11.26.3
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **Bugfixes** | `@UnifiedField({ enum: … })` no longer emits a broken, unnamed `$ref` in the generated OpenAPI document under `@nestjs/swagger >= 11.4` — enum fields now produce proper named component schemas (or clean inline enums when `enumName: null` / auto-detection fails) instead of `allOf: [{ $ref: '#/components/schemas/' }]`, which crashed OpenAPI client generators like `@hey-api/openapi-ts` |
|
|
9
|
+
| **New Features** | None |
|
|
10
|
+
| **Migration Effort** | 0 minutes (automatic) — drop-in patch release |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quick Migration
|
|
15
|
+
|
|
16
|
+
No code changes required. The fix applies automatically as soon as the package is updated.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Update package
|
|
20
|
+
pnpm add @lenne.tech/nest-server@11.26.3
|
|
21
|
+
|
|
22
|
+
# Verify build
|
|
23
|
+
pnpm run build
|
|
24
|
+
|
|
25
|
+
# Run tests
|
|
26
|
+
pnpm test
|
|
27
|
+
|
|
28
|
+
# (Optional) regenerate REST client SDK against the new OpenAPI document
|
|
29
|
+
pnpm --filter @your-app/api-sdk openapi-ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## What's Fixed in 11.26.3
|
|
35
|
+
|
|
36
|
+
### OpenAPI: broken empty `$ref` on enum-typed `@UnifiedField` properties
|
|
37
|
+
|
|
38
|
+
**Affects:** Any project that
|
|
39
|
+
|
|
40
|
+
- consumes `@lenne.tech/nest-server` together with `@nestjs/swagger >= 11.4` (this repo pinned to `11.4.2`), AND
|
|
41
|
+
- exposes REST endpoints whose DTOs declare enum fields via `@UnifiedField({ enum: … })`, AND
|
|
42
|
+
- runs an OpenAPI client generator (e.g. `@hey-api/openapi-ts`, `openapi-typescript`, `openapi-generator-cli`) against the bundled OpenAPI document.
|
|
43
|
+
|
|
44
|
+
**Symptom (before 11.26.3):**
|
|
45
|
+
|
|
46
|
+
The decorator passed `type: () => String` to `@nestjs/swagger` ALONGSIDE `enum` + `enumName`. `@nestjs/swagger <= 11.2` silently tolerated the combination, but `@nestjs/swagger >= 11.4` emits a broken, **unnamed** enum reference and never registers the enum under `components.schemas`:
|
|
47
|
+
|
|
48
|
+
```jsonc
|
|
49
|
+
// Generated OpenAPI document — BROKEN
|
|
50
|
+
{
|
|
51
|
+
"components": {
|
|
52
|
+
"schemas": {
|
|
53
|
+
"SomeInput": {
|
|
54
|
+
"properties": {
|
|
55
|
+
"status": {
|
|
56
|
+
"allOf": [
|
|
57
|
+
{ "$ref": "#/components/schemas/" } // ← empty target!
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ← StatusEnum is missing entirely from components.schemas
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Downstream tools crash:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
@hey-api/openapi-ts: Missing $ref pointer "#/components/schemas/". Token "" does not exist.
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Fix (in 11.26.3):**
|
|
75
|
+
|
|
76
|
+
`@UnifiedField` no longer sets `swaggerOpts.type` when the field is an enum. `@nestjs/swagger` derives the schema from `enum` + `enumName` correctly:
|
|
77
|
+
|
|
78
|
+
```jsonc
|
|
79
|
+
// Generated OpenAPI document — CORRECT
|
|
80
|
+
{
|
|
81
|
+
"components": {
|
|
82
|
+
"schemas": {
|
|
83
|
+
"StatusEnum": { "type": "string", "enum": ["draft", "published", "review"] },
|
|
84
|
+
"SomeInput": {
|
|
85
|
+
"properties": {
|
|
86
|
+
"status": {
|
|
87
|
+
"allOf": [
|
|
88
|
+
{ "$ref": "#/components/schemas/StatusEnum" } // ← named, resolvable
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Behaviour matrix:**
|
|
99
|
+
|
|
100
|
+
| `@UnifiedField` form | OpenAPI output before 11.26.3 | OpenAPI output in 11.26.3 |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `{ enum: MyEnum, enumName: 'MyEnum' }` | Empty `$ref`, `MyEnum` missing from `components.schemas` | Named `MyEnum` schema, property uses `$ref` |
|
|
103
|
+
| `{ enum: MyEnum }` + `registerEnum(MyEnum, { name: 'MyEnum' })` | Empty `$ref` | Named `MyEnum` schema, property uses `$ref` |
|
|
104
|
+
| `{ enum: MyEnum, enumName: null }` (opt out) | Empty `$ref` | Inline `enum: [...]`, no `$ref`, no named schema |
|
|
105
|
+
| `{ enum: MyEnum }` without registration | Empty `$ref` | Inline `enum: [...]`, no `$ref`, no named schema |
|
|
106
|
+
| Long-form `{ enum: { enum: MyEnum, enumName: 'MyEnum' } }` (deprecated) | Empty `$ref` | Named `MyEnum` schema, property uses `$ref` (deprecation warning unchanged) |
|
|
107
|
+
| Non-enum fields (`String`, `Number`, `Date`, custom classes, …) | Unchanged | Unchanged |
|
|
108
|
+
|
|
109
|
+
GraphQL schema, class-validator runtime validation (`IsEnum`), Mongoose `@Prop`, and field-level `@Restricted` / `@Roles` behaviour are all bit-for-bit identical to 11.26.2.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Compatibility Notes
|
|
114
|
+
|
|
115
|
+
- **`@nestjs/swagger <= 11.2` consumers:** The previously emitted `type: () => String` was redundant; removing it produces the same enum schema as before. No observable change.
|
|
116
|
+
- **`@nestjs/swagger >= 11.4` consumers:** The OpenAPI document for enum fields changes from a **broken** empty `$ref` to a **correct** named schema (or clean inline enum). This is strictly a defect fix — any client generator that was previously crashing now succeeds.
|
|
117
|
+
- **OpenAPI client generators / SDK consumers:** After updating, regenerate the SDK once. Enum properties that were previously typed `string` (when the generator silently dropped the broken `$ref`) will now be typed as the proper enum union — review the generated SDK once and adjust call-sites if you relied on the loose `string` type.
|
|
118
|
+
- **GraphQL consumers:** No change. The `Field(...)` factory and enum resolution are untouched.
|
|
119
|
+
- **`@UnifiedField` public API:** Unchanged. All option shapes (`enum: MyEnum`, `enumName`, deprecated long-form `{ enum: { … } }`, `enumName: null`) keep their documented semantics.
|
|
120
|
+
- **Mongoose / class-validator:** Unchanged. `@Prop({ type: baseType })` still applied for enum fields; `IsEnum(...)` is still the authoritative validator.
|
|
121
|
+
- **Vendor-mode consumers:** Same fix lands in `src/core/common/decorators/unified-field.decorator.ts`. Sync via `/lt-dev:backend:update-nest-server-core`. No flatten-fix change required.
|
|
122
|
+
- **Hidden / excluded enum fields (`@UnifiedField({ exclude: true })`):** Unaffected — those still hide from the OpenAPI document via `ApiHideProperty()`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Verifying the Fix
|
|
127
|
+
|
|
128
|
+
If you previously hit the empty-`$ref` defect, confirm the regenerated OpenAPI document:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 1. Boot the API and dump the OpenAPI document
|
|
132
|
+
pnpm start &
|
|
133
|
+
curl -s http://localhost:3000/api-json > /tmp/openapi.json
|
|
134
|
+
|
|
135
|
+
# 2. There must be no empty/unnamed component refs
|
|
136
|
+
grep -F '"$ref": "#/components/schemas/"' /tmp/openapi.json && echo 'BROKEN' || echo 'OK'
|
|
137
|
+
|
|
138
|
+
# 3. Every enum used in a DTO must appear in components.schemas
|
|
139
|
+
jq '.components.schemas | keys' /tmp/openapi.json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
A complete regression test ships in `tests/unified-field-enum-swagger.e2e-spec.ts` and inspects the real document built by `SwaggerModule.createDocument()` for:
|
|
143
|
+
|
|
144
|
+
- no empty `$ref` anywhere in the document,
|
|
145
|
+
- a named component schema per enum (string / numeric / array / auto-detected / deprecated long-form),
|
|
146
|
+
- correct enum values and property references,
|
|
147
|
+
- inline-enum fallback for `enumName: null` and for unregistered enums (no empty `$ref`).
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Troubleshooting
|
|
152
|
+
|
|
153
|
+
### After updating, my generated SDK still has an empty `$ref` error
|
|
154
|
+
|
|
155
|
+
Make sure the SDK is regenerated against a **freshly rebuilt** API. Stale `openapi.json` artefacts checked into the consumer repo continue to be broken. Rebuild the API (`pnpm run build`) and re-export the document (`/api-json` or `SwaggerModule.createDocument` snapshot) before running your codegen.
|
|
156
|
+
|
|
157
|
+
### My enum properties used to be typed `string` in the generated client and now they're a strict union
|
|
158
|
+
|
|
159
|
+
That is the corrected behaviour — the previous client was generated against a broken document and silently widened the type. Update call-sites to use the enum union (or import the enum from your shared package). If you need the loose `string` type during the rollout, your codegen typically offers an `--enum-style` flag (e.g. `@hey-api/openapi-ts` → `enums: 'javascript'`) to keep the old shape.
|
|
160
|
+
|
|
161
|
+
### I rely on the deprecated long-form `enum: { enum: MyEnum, enumName: 'MyEnum' }`
|
|
162
|
+
|
|
163
|
+
It still works and now produces the same named schema as the shortcut form. The deprecation warning emitted at decoration time is unchanged. Plan to migrate to the shortcut form (`enum: MyEnum, enumName: 'MyEnum'`) before a future MINOR removes the long form.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Module Documentation
|
|
168
|
+
|
|
169
|
+
### Core Common — `@UnifiedField`
|
|
170
|
+
|
|
171
|
+
- **Decorator:** `src/core/common/decorators/unified-field.decorator.ts`
|
|
172
|
+
- **Architecture notes:** [.claude/rules/architecture.md](../.claude/rules/architecture.md) (Input Validation section)
|
|
173
|
+
- **Reference tests:**
|
|
174
|
+
- `tests/unified-field-enum-swagger.e2e-spec.ts` — OpenAPI schema regression guard (the contract this release restores)
|
|
175
|
+
- `tests/unified-field-enum.e2e-spec.ts` — metadata-level enum behaviour
|
|
176
|
+
- `tests/unified-field-enum-api.e2e-spec.ts` — runtime REST/GraphQL enum behaviour
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## References
|
|
181
|
+
|
|
182
|
+
- [Migration Guide 11.26.1 → 11.26.2](./11.26.1-to-11.26.2.md) — Previous release (`COOKIE_PREFIX` env, cross-layer cookie-prefix lockstep)
|
|
183
|
+
- [Architecture rules — Input Validation](../.claude/rules/architecture.md)
|
|
184
|
+
- [@nestjs/swagger 11.4 release notes](https://github.com/nestjs/swagger/releases) — context for the schema-emission change that surfaced the latent defect
|
|
185
|
+
- [@hey-api/openapi-ts](https://heyapi.dev/) — one of the OpenAPI client generators that was crashing on the broken document
|
|
186
|
+
- [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.26.
|
|
3
|
+
"version": "11.26.3",
|
|
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",
|
|
@@ -408,13 +408,19 @@ export function UnifiedField(opts: UnifiedFieldOptions = {}): PropertyDecorator
|
|
|
408
408
|
swaggerOpts.required = true;
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
// Set type for swagger
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
411
|
+
// Set type for swagger.
|
|
412
|
+
//
|
|
413
|
+
// For enum fields we deliberately do NOT set `type`: @nestjs/swagger derives
|
|
414
|
+
// the schema from `enum` + `enumName` (set further below). Passing
|
|
415
|
+
// `type: () => String` ALONGSIDE `enum`/`enumName` makes @nestjs/swagger
|
|
416
|
+
// >= 11.4 emit a broken, UNNAMED enum reference
|
|
417
|
+
// (`allOf: [{ $ref: '#/components/schemas/' }]`) and never adds the enum to
|
|
418
|
+
// `components.schemas`. That crashes OpenAPI client generators — e.g.
|
|
419
|
+
// @hey-api/openapi-ts fails with «Missing $ref pointer "#/components/schemas/"».
|
|
420
|
+
// (On @nestjs/swagger <= 11.2 the extra `type` was tolerated, which is why
|
|
421
|
+
// this only surfaced after a swagger bump.)
|
|
422
|
+
if (baseType && !normalizedEnum) {
|
|
423
|
+
swaggerOpts.type = baseType;
|
|
418
424
|
}
|
|
419
425
|
|
|
420
426
|
// Set description
|