nestjs-openapi-next 1.0.1 → 1.0.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/README.md +138 -5
- package/dist/document-builder.d.ts +4 -2
- package/dist/document-builder.js +18 -4
- package/dist/explorers/api-parameters.explorer.d.ts +25 -2
- package/dist/interfaces/open-api-spec.interface.d.ts +32 -4
- package/dist/interfaces/schema-object-metadata.interface.d.ts +1 -1
- package/dist/services/schema-object-factory.d.ts +58 -8
- package/dist/services/schema-object-factory.js +2 -1
- package/dist/services/swagger-types-mapper.d.ts +127 -12
- package/dist/swagger-explorer.js +3 -1
- package/dist/swagger-module.d.ts +2 -0
- package/dist/swagger-module.js +369 -4
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
# nestjs-openapi-next
|
|
2
2
|
|
|
3
3
|
`nestjs-openapi-next` is a fork of `@nestjs/swagger` (upstream: `nestjs/swagger`).
|
|
4
|
-
The goal is to keep upstream behavior as compatible as possible while adding a set of **OpenAPI 3.2** features and widely-used **OpenAPI extension fields** (`x-`) that are commonly consumed by tools like Redoc.
|
|
4
|
+
The goal is to keep upstream behavior as compatible as possible while adding a set of **OpenAPI 3.1/3.2** features and widely-used **OpenAPI extension fields** (`x-`) that are commonly consumed by tools like Redoc.
|
|
5
5
|
|
|
6
6
|
## Key differences from upstream
|
|
7
7
|
|
|
8
|
-
- **Richer OpenAPI 3.2 typings**
|
|
9
|
-
- e.g. `TagObject.summary`, `OAuthFlowsObject.deviceAuthorization`, etc.
|
|
8
|
+
- **Richer OpenAPI 3.1/3.2 typings**
|
|
9
|
+
- e.g. `TagObject.summary`, `OAuthFlowsObject.deviceAuthorization`, `LicenseObject.identifier`, etc.
|
|
10
10
|
- **Enhanced tag support**
|
|
11
11
|
- `@ApiTag(options)` (class/controller-level): defines tag metadata (`summary`, `x-displayName`, `description`, `parent`, `kind`, ...) and merges it into the top-level `document.tags`.
|
|
12
12
|
- `x-displayName` support for tags, mirrored with `summary` (setting either results in both fields being written with the same value).
|
|
13
13
|
- Root-level `x-tagGroups` support (commonly used by Redoc). If you use `parent` to form relationships, `x-tagGroups` is auto-derived; you can also set it explicitly via `DocumentBuilder`.
|
|
14
|
-
- **
|
|
14
|
+
- **OAS 3.1 features**
|
|
15
|
+
- JSON Schema Draft 2020-12 alignment (e.g. `$defs`, `prefixItems`, `const`, `contentEncoding`, `contentMediaType`, `contentSchema`).
|
|
16
|
+
- `type` as array support (e.g. `type: ['string', 'null']`) with automatic `nullable` removal.
|
|
17
|
+
- `LicenseObject.identifier` field support via `DocumentBuilder.setLicense()`.
|
|
18
|
+
- `ReferenceObject` with `summary` and `description` override support.
|
|
19
|
+
- `exclusiveMinimum` / `exclusiveMaximum` as number type (changed from boolean in OAS 3.0).
|
|
20
|
+
- **OAS 3.2 features**
|
|
15
21
|
- HTTP `QUERY` method via `@ApiQueryMethod()` (emits `paths['/x'].query`).
|
|
16
22
|
- Streaming responses via `@ApiStreamingResponse()` (`itemSchema`, e.g. SSE `text/event-stream`).
|
|
17
23
|
- OAuth2 Device Authorization Flow typing + `@ApiSecurityDeviceFlow()` helper.
|
|
24
|
+
- `ServerObject.pathPrefix` field support via `DocumentBuilder.addServer()`.
|
|
25
|
+
- `InfoObject.tags` field support via `DocumentBuilder.setInfoTags()`.
|
|
18
26
|
- **Convenience APIs**
|
|
19
27
|
- `DocumentBuilder.addServerWithName()` for a non-standard-but-common `server.name`.
|
|
20
28
|
|
|
21
|
-
Test coverage: `test/openapi-3-2.spec.ts`.
|
|
29
|
+
Test coverage: `test/openapi-3-1.spec.ts`, `test/openapi-3-2.spec.ts`.
|
|
22
30
|
|
|
23
31
|
## Compatibility
|
|
24
32
|
|
|
@@ -230,6 +238,131 @@ export class StripeWebhooksController {
|
|
|
230
238
|
The generated document will contain `document.webhooks.stripeEvent.post`, and the
|
|
231
239
|
corresponding route will **not** be emitted under `document.paths`.
|
|
232
240
|
|
|
241
|
+
### 8) `LicenseObject.identifier` (OAS 3.1)
|
|
242
|
+
|
|
243
|
+
OAS 3.1 added an `identifier` field to `LicenseObject` for SPDX license identifiers:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const config = new DocumentBuilder()
|
|
247
|
+
.setTitle('Example')
|
|
248
|
+
.setVersion('1.0')
|
|
249
|
+
.setLicense('MIT', 'https://opensource.org/licenses/MIT', 'MIT')
|
|
250
|
+
.build();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
This produces:
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
info:
|
|
257
|
+
license:
|
|
258
|
+
name: MIT
|
|
259
|
+
url: https://opensource.org/licenses/MIT
|
|
260
|
+
identifier: MIT
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 9) `ServerObject.pathPrefix` (OAS 3.2)
|
|
264
|
+
|
|
265
|
+
OAS 3.2 added a `pathPrefix` field to `ServerObject`:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const config = new DocumentBuilder()
|
|
269
|
+
.setTitle('Example')
|
|
270
|
+
.setVersion('1.0')
|
|
271
|
+
.addServer('https://api.example.com', 'Production', {}, '/v1')
|
|
272
|
+
.build();
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This produces:
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
servers:
|
|
279
|
+
- url: https://api.example.com
|
|
280
|
+
description: Production
|
|
281
|
+
pathPrefix: /v1
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### 10) `InfoObject.tags` (OAS 3.2)
|
|
285
|
+
|
|
286
|
+
OAS 3.2 added a `tags` field to `InfoObject` for categorizing the API itself:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
const config = new DocumentBuilder()
|
|
290
|
+
.setTitle('Example')
|
|
291
|
+
.setVersion('1.0')
|
|
292
|
+
.setInfoTags(['payments', 'commerce'])
|
|
293
|
+
.build();
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
This produces:
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
info:
|
|
300
|
+
title: Example
|
|
301
|
+
version: '1.0'
|
|
302
|
+
tags:
|
|
303
|
+
- payments
|
|
304
|
+
- commerce
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### 11) `ReferenceObject` with `summary` / `description` override (OAS 3.1)
|
|
308
|
+
|
|
309
|
+
OAS 3.1 allows `$ref` objects to include `summary` and `description` fields that override the referenced schema's values:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { ApiProperty } from 'nestjs-openapi-next';
|
|
313
|
+
|
|
314
|
+
class CreateUserDto {
|
|
315
|
+
@ApiProperty({
|
|
316
|
+
allOf: [
|
|
317
|
+
{
|
|
318
|
+
$ref: '#/components/schemas/BaseUser',
|
|
319
|
+
summary: 'User base fields',
|
|
320
|
+
description: 'Contains the common user properties'
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
})
|
|
324
|
+
user: BaseUser;
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 12) `type` as array (OAS 3.1)
|
|
329
|
+
|
|
330
|
+
OAS 3.1 supports `type` as an array for union types. This replaces the `nullable` keyword:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { ApiProperty } from 'nestjs-openapi-next';
|
|
334
|
+
|
|
335
|
+
class UserDto {
|
|
336
|
+
@ApiProperty({ type: ['string', 'null'] })
|
|
337
|
+
nickname: string | null;
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
This produces `type: ['string', 'null']` instead of `type: 'string', nullable: true`.
|
|
342
|
+
|
|
343
|
+
### 13) JSON Schema Draft 2020-12 keywords (OAS 3.1)
|
|
344
|
+
|
|
345
|
+
OAS 3.1 aligns with JSON Schema Draft 2020-12, adding support for:
|
|
346
|
+
|
|
347
|
+
- `$defs` - local schema definitions
|
|
348
|
+
- `prefixItems` - tuple validation (replaces `items` array form)
|
|
349
|
+
- `const` - exact value matching
|
|
350
|
+
- `contentEncoding` - encoding for string content (e.g., `base64`)
|
|
351
|
+
- `contentMediaType` - media type for string content
|
|
352
|
+
- `contentSchema` - schema for decoded content
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
import { ApiProperty } from 'nestjs-openapi-next';
|
|
356
|
+
|
|
357
|
+
class FileDto {
|
|
358
|
+
@ApiProperty({
|
|
359
|
+
contentEncoding: 'base64',
|
|
360
|
+
contentMediaType: 'image/png'
|
|
361
|
+
})
|
|
362
|
+
data: string;
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
233
366
|
## License
|
|
234
367
|
|
|
235
368
|
MIT (see `LICENSE`). This repository is a derivative work of the upstream `nestjs/swagger` project under the MIT license.
|
|
@@ -8,11 +8,13 @@ export declare class DocumentBuilder {
|
|
|
8
8
|
setDescription(description: string): this;
|
|
9
9
|
setVersion(version: string): this;
|
|
10
10
|
setTermsOfService(termsOfService: string): this;
|
|
11
|
+
setInfoTags(tags: string[]): this;
|
|
11
12
|
setContact(name: string, url: string, email: string): this;
|
|
12
13
|
setLicense(name: string, url: string): this;
|
|
14
|
+
setLicenseWithIdentifier(name: string, identifier: string): this;
|
|
13
15
|
setOpenAPIVersion(version: string): this;
|
|
14
|
-
addServer(url: string, description?: string, variables?: Record<string, ServerVariableObject>): this;
|
|
15
|
-
addServerWithName(name: string, url: string, description?: string, variables?: Record<string, ServerVariableObject>): this;
|
|
16
|
+
addServer(url: string, description?: string, pathPrefix?: string, variables?: Record<string, ServerVariableObject>): this;
|
|
17
|
+
addServerWithName(name: string, url: string, description?: string, pathPrefix?: string, variables?: Record<string, ServerVariableObject>): this;
|
|
16
18
|
setExternalDoc(description: string, url: string): this;
|
|
17
19
|
setBasePath(path: string): this;
|
|
18
20
|
addTag(name: string, description?: string, externalDocs?: ExternalDocumentationObject, summary?: string): this;
|
package/dist/document-builder.js
CHANGED
|
@@ -27,6 +27,10 @@ class DocumentBuilder {
|
|
|
27
27
|
this.document.info.termsOfService = termsOfService;
|
|
28
28
|
return this;
|
|
29
29
|
}
|
|
30
|
+
setInfoTags(tags) {
|
|
31
|
+
this.document.info.tags = tags;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
30
34
|
setContact(name, url, email) {
|
|
31
35
|
this.document.info.contact = { name, url, email };
|
|
32
36
|
return this;
|
|
@@ -35,6 +39,10 @@ class DocumentBuilder {
|
|
|
35
39
|
this.document.info.license = { name, url };
|
|
36
40
|
return this;
|
|
37
41
|
}
|
|
42
|
+
setLicenseWithIdentifier(name, identifier) {
|
|
43
|
+
this.document.info.license = { name, identifier };
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
38
46
|
setOpenAPIVersion(version) {
|
|
39
47
|
if (version.match(/^\d\.\d\.\d$/)) {
|
|
40
48
|
this.document.openapi = version;
|
|
@@ -44,12 +52,18 @@ class DocumentBuilder {
|
|
|
44
52
|
}
|
|
45
53
|
return this;
|
|
46
54
|
}
|
|
47
|
-
addServer(url, description, variables) {
|
|
48
|
-
this.document.servers.push({ url, description, variables });
|
|
55
|
+
addServer(url, description, pathPrefix, variables) {
|
|
56
|
+
this.document.servers.push({ url, description, variables, pathPrefix });
|
|
49
57
|
return this;
|
|
50
58
|
}
|
|
51
|
-
addServerWithName(name, url, description, variables) {
|
|
52
|
-
this.document.servers.push({
|
|
59
|
+
addServerWithName(name, url, description, pathPrefix, variables) {
|
|
60
|
+
this.document.servers.push({
|
|
61
|
+
name,
|
|
62
|
+
url,
|
|
63
|
+
description,
|
|
64
|
+
variables,
|
|
65
|
+
pathPrefix
|
|
66
|
+
});
|
|
53
67
|
return this;
|
|
54
68
|
}
|
|
55
69
|
setExternalDoc(description, url) {
|
|
@@ -6,6 +6,29 @@ export declare const exploreApiParametersMetadata: (schemas: Record<string, Sche
|
|
|
6
6
|
schema: {
|
|
7
7
|
type: string;
|
|
8
8
|
items: any;
|
|
9
|
+
$schema?: string;
|
|
10
|
+
$id?: string;
|
|
11
|
+
$defs?: Record<string, SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject>;
|
|
12
|
+
$dynamicAnchor?: string;
|
|
13
|
+
$dynamicRef?: string;
|
|
14
|
+
$anchor?: string;
|
|
15
|
+
const?: any;
|
|
16
|
+
contentMediaType?: string;
|
|
17
|
+
contentEncoding?: string;
|
|
18
|
+
contentSchema?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
19
|
+
if?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
20
|
+
then?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
21
|
+
else?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
22
|
+
dependentSchemas?: Record<string, SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject>;
|
|
23
|
+
dependentRequired?: Record<string, string[]>;
|
|
24
|
+
prefixItems?: (SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject)[];
|
|
25
|
+
unevaluatedProperties?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject | boolean;
|
|
26
|
+
unevaluatedItems?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject | boolean;
|
|
27
|
+
contains?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
28
|
+
minContains?: number;
|
|
29
|
+
maxContains?: number;
|
|
30
|
+
$comment?: string;
|
|
31
|
+
propertyNames?: SchemaObject | import("../interfaces/open-api-spec.interface").ReferenceObject;
|
|
9
32
|
nullable?: boolean;
|
|
10
33
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
11
34
|
readOnly?: boolean;
|
|
@@ -28,9 +51,9 @@ export declare const exploreApiParametersMetadata: (schemas: Record<string, Sche
|
|
|
28
51
|
title?: string;
|
|
29
52
|
multipleOf?: number;
|
|
30
53
|
maximum?: number;
|
|
31
|
-
exclusiveMaximum?: boolean;
|
|
54
|
+
exclusiveMaximum?: boolean | number;
|
|
32
55
|
minimum?: number;
|
|
33
|
-
exclusiveMinimum?: boolean;
|
|
56
|
+
exclusiveMinimum?: boolean | number;
|
|
34
57
|
maxLength?: number;
|
|
35
58
|
minLength?: number;
|
|
36
59
|
pattern?: string;
|
|
@@ -20,6 +20,7 @@ export interface InfoObject {
|
|
|
20
20
|
contact?: ContactObject;
|
|
21
21
|
license?: LicenseObject;
|
|
22
22
|
version: string;
|
|
23
|
+
tags?: string[];
|
|
23
24
|
}
|
|
24
25
|
export interface ContactObject {
|
|
25
26
|
name?: string;
|
|
@@ -29,10 +30,12 @@ export interface ContactObject {
|
|
|
29
30
|
export interface LicenseObject {
|
|
30
31
|
name: string;
|
|
31
32
|
url?: string;
|
|
33
|
+
identifier?: string;
|
|
32
34
|
}
|
|
33
35
|
export interface ServerObject {
|
|
34
36
|
name?: string;
|
|
35
37
|
url: string;
|
|
38
|
+
pathPrefix?: string;
|
|
36
39
|
description?: string;
|
|
37
40
|
variables?: Record<string, ServerVariableObject>;
|
|
38
41
|
}
|
|
@@ -170,8 +173,33 @@ export interface TagObject {
|
|
|
170
173
|
export type ExamplesObject = Record<string, ExampleObject | ReferenceObject>;
|
|
171
174
|
export interface ReferenceObject {
|
|
172
175
|
$ref: string;
|
|
176
|
+
summary?: string;
|
|
177
|
+
description?: string;
|
|
173
178
|
}
|
|
174
179
|
export interface SchemaObject {
|
|
180
|
+
$schema?: string;
|
|
181
|
+
$id?: string;
|
|
182
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
183
|
+
$dynamicAnchor?: string;
|
|
184
|
+
$dynamicRef?: string;
|
|
185
|
+
$anchor?: string;
|
|
186
|
+
const?: any;
|
|
187
|
+
contentMediaType?: string;
|
|
188
|
+
contentEncoding?: string;
|
|
189
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
190
|
+
if?: SchemaObject | ReferenceObject;
|
|
191
|
+
then?: SchemaObject | ReferenceObject;
|
|
192
|
+
else?: SchemaObject | ReferenceObject;
|
|
193
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
194
|
+
dependentRequired?: Record<string, string[]>;
|
|
195
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
196
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
197
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
198
|
+
contains?: SchemaObject | ReferenceObject;
|
|
199
|
+
minContains?: number;
|
|
200
|
+
maxContains?: number;
|
|
201
|
+
$comment?: string;
|
|
202
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
175
203
|
nullable?: boolean;
|
|
176
204
|
discriminator?: DiscriminatorObject;
|
|
177
205
|
readOnly?: boolean;
|
|
@@ -181,12 +209,12 @@ export interface SchemaObject {
|
|
|
181
209
|
example?: any;
|
|
182
210
|
examples?: any[] | Record<string, any>;
|
|
183
211
|
deprecated?: boolean;
|
|
184
|
-
type?: string;
|
|
212
|
+
type?: string | string[];
|
|
185
213
|
allOf?: (SchemaObject | ReferenceObject)[];
|
|
186
214
|
oneOf?: (SchemaObject | ReferenceObject)[];
|
|
187
215
|
anyOf?: (SchemaObject | ReferenceObject)[];
|
|
188
216
|
not?: SchemaObject | ReferenceObject;
|
|
189
|
-
items?: SchemaObject | ReferenceObject;
|
|
217
|
+
items?: SchemaObject | ReferenceObject | boolean;
|
|
190
218
|
properties?: Record<string, SchemaObject | ReferenceObject>;
|
|
191
219
|
additionalProperties?: SchemaObject | ReferenceObject | boolean;
|
|
192
220
|
patternProperties?: Record<string, SchemaObject | ReferenceObject> | undefined;
|
|
@@ -196,9 +224,9 @@ export interface SchemaObject {
|
|
|
196
224
|
title?: string;
|
|
197
225
|
multipleOf?: number;
|
|
198
226
|
maximum?: number;
|
|
199
|
-
exclusiveMaximum?: boolean;
|
|
227
|
+
exclusiveMaximum?: boolean | number;
|
|
200
228
|
minimum?: number;
|
|
201
|
-
exclusiveMinimum?: boolean;
|
|
229
|
+
exclusiveMinimum?: boolean | number;
|
|
202
230
|
maxLength?: number;
|
|
203
231
|
minLength?: number;
|
|
204
232
|
pattern?: string;
|
|
@@ -8,7 +8,7 @@ interface SchemaObjectCommonMetadata extends Omit<SchemaObject, 'type' | 'requir
|
|
|
8
8
|
enum?: EnumAllowedTypes;
|
|
9
9
|
}
|
|
10
10
|
export type SchemaObjectMetadata = (SchemaObjectCommonMetadata & {
|
|
11
|
-
type?: Type<unknown> | Function | [Function] | 'array' | 'string' | 'number' | 'boolean' | 'integer' | 'null';
|
|
11
|
+
type?: Type<unknown> | Function | [Function] | 'array' | 'string' | 'number' | 'boolean' | 'integer' | 'file' | 'null';
|
|
12
12
|
required?: boolean;
|
|
13
13
|
}) | ({
|
|
14
14
|
type?: Type<unknown> | Function | [Function] | Record<string, any>;
|
|
@@ -15,6 +15,29 @@ export declare class SchemaObjectFactory {
|
|
|
15
15
|
type: string;
|
|
16
16
|
items: {
|
|
17
17
|
$ref: string;
|
|
18
|
+
$schema?: string;
|
|
19
|
+
$id?: string;
|
|
20
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
21
|
+
$dynamicAnchor?: string;
|
|
22
|
+
$dynamicRef?: string;
|
|
23
|
+
$anchor?: string;
|
|
24
|
+
const?: any;
|
|
25
|
+
contentMediaType?: string;
|
|
26
|
+
contentEncoding?: string;
|
|
27
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
28
|
+
if?: SchemaObject | ReferenceObject;
|
|
29
|
+
then?: SchemaObject | ReferenceObject;
|
|
30
|
+
else?: SchemaObject | ReferenceObject;
|
|
31
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
32
|
+
dependentRequired?: Record<string, string[]>;
|
|
33
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
34
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
35
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
36
|
+
contains?: SchemaObject | ReferenceObject;
|
|
37
|
+
minContains?: number;
|
|
38
|
+
maxContains?: number;
|
|
39
|
+
$comment?: string;
|
|
40
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
18
41
|
nullable?: boolean;
|
|
19
42
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
20
43
|
readOnly?: boolean;
|
|
@@ -24,12 +47,12 @@ export declare class SchemaObjectFactory {
|
|
|
24
47
|
example?: any;
|
|
25
48
|
examples?: any[] | Record<string, any>;
|
|
26
49
|
deprecated?: boolean;
|
|
27
|
-
type?: string;
|
|
50
|
+
type?: string | string[];
|
|
28
51
|
allOf?: (SchemaObject | ReferenceObject)[];
|
|
29
52
|
oneOf?: (SchemaObject | ReferenceObject)[];
|
|
30
53
|
anyOf?: (SchemaObject | ReferenceObject)[];
|
|
31
54
|
not?: SchemaObject | ReferenceObject;
|
|
32
|
-
items?: SchemaObject | ReferenceObject;
|
|
55
|
+
items?: SchemaObject | ReferenceObject | boolean;
|
|
33
56
|
properties?: Record<string, SchemaObject | ReferenceObject>;
|
|
34
57
|
additionalProperties?: SchemaObject | ReferenceObject | boolean;
|
|
35
58
|
patternProperties?: Record<string, SchemaObject | ReferenceObject> | undefined;
|
|
@@ -39,9 +62,9 @@ export declare class SchemaObjectFactory {
|
|
|
39
62
|
title?: string;
|
|
40
63
|
multipleOf?: number;
|
|
41
64
|
maximum?: number;
|
|
42
|
-
exclusiveMaximum?: boolean;
|
|
65
|
+
exclusiveMaximum?: boolean | number;
|
|
43
66
|
minimum?: number;
|
|
44
|
-
exclusiveMinimum?: boolean;
|
|
67
|
+
exclusiveMinimum?: boolean | number;
|
|
45
68
|
maxLength?: number;
|
|
46
69
|
minLength?: number;
|
|
47
70
|
pattern?: string;
|
|
@@ -55,6 +78,8 @@ export declare class SchemaObjectFactory {
|
|
|
55
78
|
'x-enumNames'?: string[];
|
|
56
79
|
} | {
|
|
57
80
|
$ref: string;
|
|
81
|
+
summary?: string;
|
|
82
|
+
description?: string;
|
|
58
83
|
};
|
|
59
84
|
};
|
|
60
85
|
type?: Type<unknown>;
|
|
@@ -70,6 +95,29 @@ export declare class SchemaObjectFactory {
|
|
|
70
95
|
name: string | number | object;
|
|
71
96
|
schema: {
|
|
72
97
|
$ref: string;
|
|
98
|
+
$schema?: string;
|
|
99
|
+
$id?: string;
|
|
100
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
101
|
+
$dynamicAnchor?: string;
|
|
102
|
+
$dynamicRef?: string;
|
|
103
|
+
$anchor?: string;
|
|
104
|
+
const?: any;
|
|
105
|
+
contentMediaType?: string;
|
|
106
|
+
contentEncoding?: string;
|
|
107
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
108
|
+
if?: SchemaObject | ReferenceObject;
|
|
109
|
+
then?: SchemaObject | ReferenceObject;
|
|
110
|
+
else?: SchemaObject | ReferenceObject;
|
|
111
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
112
|
+
dependentRequired?: Record<string, string[]>;
|
|
113
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
114
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
115
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
116
|
+
contains?: SchemaObject | ReferenceObject;
|
|
117
|
+
minContains?: number;
|
|
118
|
+
maxContains?: number;
|
|
119
|
+
$comment?: string;
|
|
120
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
73
121
|
nullable?: boolean;
|
|
74
122
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
75
123
|
readOnly?: boolean;
|
|
@@ -79,12 +127,12 @@ export declare class SchemaObjectFactory {
|
|
|
79
127
|
example?: any;
|
|
80
128
|
examples?: any[] | Record<string, any>;
|
|
81
129
|
deprecated?: boolean;
|
|
82
|
-
type?: string;
|
|
130
|
+
type?: string | string[];
|
|
83
131
|
allOf?: (SchemaObject | ReferenceObject)[];
|
|
84
132
|
oneOf?: (SchemaObject | ReferenceObject)[];
|
|
85
133
|
anyOf?: (SchemaObject | ReferenceObject)[];
|
|
86
134
|
not?: SchemaObject | ReferenceObject;
|
|
87
|
-
items?: SchemaObject | ReferenceObject;
|
|
135
|
+
items?: SchemaObject | ReferenceObject | boolean;
|
|
88
136
|
properties?: Record<string, SchemaObject | ReferenceObject>;
|
|
89
137
|
additionalProperties?: SchemaObject | ReferenceObject | boolean;
|
|
90
138
|
patternProperties?: Record<string, SchemaObject | ReferenceObject> | undefined;
|
|
@@ -94,9 +142,9 @@ export declare class SchemaObjectFactory {
|
|
|
94
142
|
title?: string;
|
|
95
143
|
multipleOf?: number;
|
|
96
144
|
maximum?: number;
|
|
97
|
-
exclusiveMaximum?: boolean;
|
|
145
|
+
exclusiveMaximum?: boolean | number;
|
|
98
146
|
minimum?: number;
|
|
99
|
-
exclusiveMinimum?: boolean;
|
|
147
|
+
exclusiveMinimum?: boolean | number;
|
|
100
148
|
maxLength?: number;
|
|
101
149
|
minLength?: number;
|
|
102
150
|
pattern?: string;
|
|
@@ -110,6 +158,8 @@ export declare class SchemaObjectFactory {
|
|
|
110
158
|
'x-enumNames'?: string[];
|
|
111
159
|
} | {
|
|
112
160
|
$ref: string;
|
|
161
|
+
summary?: string;
|
|
162
|
+
description?: string;
|
|
113
163
|
};
|
|
114
164
|
type?: Type<unknown>;
|
|
115
165
|
in?: import("../interfaces/open-api-spec.interface").ParameterLocation | "body" | "placeholder";
|
|
@@ -305,7 +305,8 @@ class SchemaObjectFactory {
|
|
|
305
305
|
transformToArraySchemaProperty(metadata, key, type) {
|
|
306
306
|
const keysToRemove = ['type', 'enum'];
|
|
307
307
|
const [movedProperties, keysToMove] = this.extractPropertyModifiers(metadata);
|
|
308
|
-
const schemaHost = Object.assign(Object.assign({}, (0, lodash_1.omit)(metadata, [...keysToRemove, ...keysToMove])), { name: metadata.name || key, type: 'array', items:
|
|
308
|
+
const schemaHost = Object.assign(Object.assign({}, (0, lodash_1.omit)(metadata, [...keysToRemove, ...keysToMove])), { name: metadata.name || key, type: 'array', items: metadata.items
|
|
309
|
+
? Object.assign(Object.assign({}, metadata.items), movedProperties) : (0, lodash_1.isString)(type)
|
|
309
310
|
? Object.assign({ type }, movedProperties) : Object.assign(Object.assign({}, type), movedProperties) });
|
|
310
311
|
schemaHost.items = (0, lodash_1.omitBy)(schemaHost.items, shared_utils_1.isUndefined);
|
|
311
312
|
return schemaHost;
|
|
@@ -8,6 +8,29 @@ export declare class SwaggerTypesMapper {
|
|
|
8
8
|
schema: {
|
|
9
9
|
type: string;
|
|
10
10
|
items: any;
|
|
11
|
+
$schema?: string;
|
|
12
|
+
$id?: string;
|
|
13
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
14
|
+
$dynamicAnchor?: string;
|
|
15
|
+
$dynamicRef?: string;
|
|
16
|
+
$anchor?: string;
|
|
17
|
+
const?: any;
|
|
18
|
+
contentMediaType?: string;
|
|
19
|
+
contentEncoding?: string;
|
|
20
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
21
|
+
if?: SchemaObject | ReferenceObject;
|
|
22
|
+
then?: SchemaObject | ReferenceObject;
|
|
23
|
+
else?: SchemaObject | ReferenceObject;
|
|
24
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
25
|
+
dependentRequired?: Record<string, string[]>;
|
|
26
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
27
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
28
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
29
|
+
contains?: SchemaObject | ReferenceObject;
|
|
30
|
+
minContains?: number;
|
|
31
|
+
maxContains?: number;
|
|
32
|
+
$comment?: string;
|
|
33
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
11
34
|
nullable?: boolean;
|
|
12
35
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
13
36
|
readOnly?: boolean;
|
|
@@ -30,9 +53,9 @@ export declare class SwaggerTypesMapper {
|
|
|
30
53
|
title?: string;
|
|
31
54
|
multipleOf?: number;
|
|
32
55
|
maximum?: number;
|
|
33
|
-
exclusiveMaximum?: boolean;
|
|
56
|
+
exclusiveMaximum?: boolean | number;
|
|
34
57
|
minimum?: number;
|
|
35
|
-
exclusiveMinimum?: boolean;
|
|
58
|
+
exclusiveMinimum?: boolean | number;
|
|
36
59
|
maxLength?: number;
|
|
37
60
|
minLength?: number;
|
|
38
61
|
pattern?: string;
|
|
@@ -75,6 +98,29 @@ export declare class SwaggerTypesMapper {
|
|
|
75
98
|
schema: {
|
|
76
99
|
type: string;
|
|
77
100
|
items: any;
|
|
101
|
+
$schema?: string;
|
|
102
|
+
$id?: string;
|
|
103
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
104
|
+
$dynamicAnchor?: string;
|
|
105
|
+
$dynamicRef?: string;
|
|
106
|
+
$anchor?: string;
|
|
107
|
+
const?: any;
|
|
108
|
+
contentMediaType?: string;
|
|
109
|
+
contentEncoding?: string;
|
|
110
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
111
|
+
if?: SchemaObject | ReferenceObject;
|
|
112
|
+
then?: SchemaObject | ReferenceObject;
|
|
113
|
+
else?: SchemaObject | ReferenceObject;
|
|
114
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
115
|
+
dependentRequired?: Record<string, string[]>;
|
|
116
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
117
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
118
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
119
|
+
contains?: SchemaObject | ReferenceObject;
|
|
120
|
+
minContains?: number;
|
|
121
|
+
maxContains?: number;
|
|
122
|
+
$comment?: string;
|
|
123
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
78
124
|
nullable?: boolean;
|
|
79
125
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
80
126
|
readOnly?: boolean;
|
|
@@ -97,9 +143,9 @@ export declare class SwaggerTypesMapper {
|
|
|
97
143
|
title?: string;
|
|
98
144
|
multipleOf?: number;
|
|
99
145
|
maximum?: number;
|
|
100
|
-
exclusiveMaximum?: boolean;
|
|
146
|
+
exclusiveMaximum?: boolean | number;
|
|
101
147
|
minimum?: number;
|
|
102
|
-
exclusiveMinimum?: boolean;
|
|
148
|
+
exclusiveMinimum?: boolean | number;
|
|
103
149
|
maxLength?: number;
|
|
104
150
|
minLength?: number;
|
|
105
151
|
pattern?: string;
|
|
@@ -117,6 +163,29 @@ export declare class SwaggerTypesMapper {
|
|
|
117
163
|
schema: {
|
|
118
164
|
type: string;
|
|
119
165
|
items: any;
|
|
166
|
+
$schema?: string;
|
|
167
|
+
$id?: string;
|
|
168
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
169
|
+
$dynamicAnchor?: string;
|
|
170
|
+
$dynamicRef?: string;
|
|
171
|
+
$anchor?: string;
|
|
172
|
+
const?: any;
|
|
173
|
+
contentMediaType?: string;
|
|
174
|
+
contentEncoding?: string;
|
|
175
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
176
|
+
if?: SchemaObject | ReferenceObject;
|
|
177
|
+
then?: SchemaObject | ReferenceObject;
|
|
178
|
+
else?: SchemaObject | ReferenceObject;
|
|
179
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
180
|
+
dependentRequired?: Record<string, string[]>;
|
|
181
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
182
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
183
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
184
|
+
contains?: SchemaObject | ReferenceObject;
|
|
185
|
+
minContains?: number;
|
|
186
|
+
maxContains?: number;
|
|
187
|
+
$comment?: string;
|
|
188
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
120
189
|
nullable?: boolean;
|
|
121
190
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
122
191
|
readOnly?: boolean;
|
|
@@ -139,9 +208,9 @@ export declare class SwaggerTypesMapper {
|
|
|
139
208
|
title?: string;
|
|
140
209
|
multipleOf?: number;
|
|
141
210
|
maximum?: number;
|
|
142
|
-
exclusiveMaximum?: boolean;
|
|
211
|
+
exclusiveMaximum?: boolean | number;
|
|
143
212
|
minimum?: number;
|
|
144
|
-
exclusiveMinimum?: boolean;
|
|
213
|
+
exclusiveMinimum?: boolean | number;
|
|
145
214
|
maxLength?: number;
|
|
146
215
|
minLength?: number;
|
|
147
216
|
pattern?: string;
|
|
@@ -168,6 +237,29 @@ export declare class SwaggerTypesMapper {
|
|
|
168
237
|
schema: {
|
|
169
238
|
type: string;
|
|
170
239
|
items: any;
|
|
240
|
+
$schema?: string;
|
|
241
|
+
$id?: string;
|
|
242
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
243
|
+
$dynamicAnchor?: string;
|
|
244
|
+
$dynamicRef?: string;
|
|
245
|
+
$anchor?: string;
|
|
246
|
+
const?: any;
|
|
247
|
+
contentMediaType?: string;
|
|
248
|
+
contentEncoding?: string;
|
|
249
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
250
|
+
if?: SchemaObject | ReferenceObject;
|
|
251
|
+
then?: SchemaObject | ReferenceObject;
|
|
252
|
+
else?: SchemaObject | ReferenceObject;
|
|
253
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
254
|
+
dependentRequired?: Record<string, string[]>;
|
|
255
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
256
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
257
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
258
|
+
contains?: SchemaObject | ReferenceObject;
|
|
259
|
+
minContains?: number;
|
|
260
|
+
maxContains?: number;
|
|
261
|
+
$comment?: string;
|
|
262
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
171
263
|
nullable?: boolean;
|
|
172
264
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
173
265
|
readOnly?: boolean;
|
|
@@ -190,9 +282,9 @@ export declare class SwaggerTypesMapper {
|
|
|
190
282
|
title?: string;
|
|
191
283
|
multipleOf?: number;
|
|
192
284
|
maximum?: number;
|
|
193
|
-
exclusiveMaximum?: boolean;
|
|
285
|
+
exclusiveMaximum?: boolean | number;
|
|
194
286
|
minimum?: number;
|
|
195
|
-
exclusiveMinimum?: boolean;
|
|
287
|
+
exclusiveMinimum?: boolean | number;
|
|
196
288
|
maxLength?: number;
|
|
197
289
|
minLength?: number;
|
|
198
290
|
pattern?: string;
|
|
@@ -206,15 +298,38 @@ export declare class SwaggerTypesMapper {
|
|
|
206
298
|
'x-enumNames'?: string[];
|
|
207
299
|
};
|
|
208
300
|
name?: string | number | object;
|
|
209
|
-
type?: import("@nestjs/common").Type<unknown> & string;
|
|
301
|
+
type?: import("@nestjs/common").Type<unknown> & (string | string[]);
|
|
210
302
|
in?: import("../interfaces/open-api-spec.interface").ParameterLocation | "body" | "placeholder";
|
|
211
303
|
isArray?: boolean;
|
|
212
|
-
items?: SchemaObject |
|
|
304
|
+
items?: SchemaObject & (boolean | SchemaObject | ReferenceObject);
|
|
213
305
|
required?: boolean & string[];
|
|
214
306
|
enum?: unknown[] & any[];
|
|
215
307
|
enumName?: string;
|
|
216
308
|
enumSchema?: import("../interfaces/enum-schema-attributes.interface").EnumSchemaAttributes;
|
|
217
309
|
selfRequired?: boolean;
|
|
310
|
+
$schema?: string;
|
|
311
|
+
$id?: string;
|
|
312
|
+
$defs?: Record<string, SchemaObject | ReferenceObject>;
|
|
313
|
+
$dynamicAnchor?: string;
|
|
314
|
+
$dynamicRef?: string;
|
|
315
|
+
$anchor?: string;
|
|
316
|
+
const?: any;
|
|
317
|
+
contentMediaType?: string;
|
|
318
|
+
contentEncoding?: string;
|
|
319
|
+
contentSchema?: SchemaObject | ReferenceObject;
|
|
320
|
+
if?: SchemaObject | ReferenceObject;
|
|
321
|
+
then?: SchemaObject | ReferenceObject;
|
|
322
|
+
else?: SchemaObject | ReferenceObject;
|
|
323
|
+
dependentSchemas?: Record<string, SchemaObject | ReferenceObject>;
|
|
324
|
+
dependentRequired?: Record<string, string[]>;
|
|
325
|
+
prefixItems?: (SchemaObject | ReferenceObject)[];
|
|
326
|
+
unevaluatedProperties?: SchemaObject | ReferenceObject | boolean;
|
|
327
|
+
unevaluatedItems?: SchemaObject | ReferenceObject | boolean;
|
|
328
|
+
contains?: SchemaObject | ReferenceObject;
|
|
329
|
+
minContains?: number;
|
|
330
|
+
maxContains?: number;
|
|
331
|
+
$comment?: string;
|
|
332
|
+
propertyNames?: SchemaObject | ReferenceObject;
|
|
218
333
|
nullable?: boolean;
|
|
219
334
|
discriminator?: import("../interfaces/open-api-spec.interface").DiscriminatorObject;
|
|
220
335
|
readOnly?: boolean;
|
|
@@ -237,9 +352,9 @@ export declare class SwaggerTypesMapper {
|
|
|
237
352
|
title?: string;
|
|
238
353
|
multipleOf?: number;
|
|
239
354
|
maximum?: number;
|
|
240
|
-
exclusiveMaximum?: boolean;
|
|
355
|
+
exclusiveMaximum?: boolean | number;
|
|
241
356
|
minimum?: number;
|
|
242
|
-
exclusiveMinimum?: boolean;
|
|
357
|
+
exclusiveMinimum?: boolean | number;
|
|
243
358
|
maxLength?: number;
|
|
244
359
|
minLength?: number;
|
|
245
360
|
pattern?: string;
|
package/dist/swagger-explorer.js
CHANGED
|
@@ -196,7 +196,9 @@ class SwaggerExplorer {
|
|
|
196
196
|
return Object.assign(Object.assign(Object.assign({ method: httpMethodKey, path: fullPath === '' ? '/' : fullPath }, (isWebhook
|
|
197
197
|
? {
|
|
198
198
|
isWebhook: true,
|
|
199
|
-
webhookName: typeof webhookMetadata === 'string'
|
|
199
|
+
webhookName: typeof webhookMetadata === 'string'
|
|
200
|
+
? webhookMetadata
|
|
201
|
+
: method.name
|
|
200
202
|
}
|
|
201
203
|
: {})), { operationId: this.getOperationId(instance, methodKey, pathVersion) }), apiExtension);
|
|
202
204
|
}));
|
package/dist/swagger-module.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { HttpServer } from '@nestjs/common/interfaces/http/http-server.interface
|
|
|
3
3
|
import { OpenAPIObject, SwaggerCustomOptions, SwaggerDocumentOptions } from './interfaces';
|
|
4
4
|
export declare class SwaggerModule {
|
|
5
5
|
private static readonly metadataLoader;
|
|
6
|
+
private static readonly HTTP_METHODS;
|
|
7
|
+
private static collectOperationTagNames;
|
|
6
8
|
private static mergeWebhooks;
|
|
7
9
|
private static mergeTags;
|
|
8
10
|
private static buildXTagGroups;
|
package/dist/swagger-module.js
CHANGED
|
@@ -21,14 +21,349 @@ const normalize_rel_path_1 = require("./utils/normalize-rel-path");
|
|
|
21
21
|
const resolve_path_util_1 = require("./utils/resolve-path.util");
|
|
22
22
|
const validate_global_prefix_util_1 = require("./utils/validate-global-prefix.util");
|
|
23
23
|
const validate_path_util_1 = require("./utils/validate-path.util");
|
|
24
|
+
const NULL_TYPE_SCHEMA = { type: 'null' };
|
|
25
|
+
function isReferenceObject(value) {
|
|
26
|
+
return !!(value === null || value === void 0 ? void 0 : value.$ref);
|
|
27
|
+
}
|
|
28
|
+
function isOas31OrAbove(openapi) {
|
|
29
|
+
const [majorStr, minorStr] = (openapi || '').split('.');
|
|
30
|
+
const major = Number(majorStr);
|
|
31
|
+
const minor = Number(minorStr);
|
|
32
|
+
if (Number.isNaN(major) || Number.isNaN(minor)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return major > 3 || (major === 3 && minor >= 1);
|
|
36
|
+
}
|
|
37
|
+
function appendNullOption(schemas) {
|
|
38
|
+
const hasNull = schemas.some((item) => !isReferenceObject(item) &&
|
|
39
|
+
(item.type === 'null' ||
|
|
40
|
+
(Array.isArray(item.type) && item.type.includes('null'))));
|
|
41
|
+
return hasNull ? schemas : schemas.concat(NULL_TYPE_SCHEMA);
|
|
42
|
+
}
|
|
43
|
+
function normalizeNullableSchema(schema) {
|
|
44
|
+
if (!schema) {
|
|
45
|
+
return schema;
|
|
46
|
+
}
|
|
47
|
+
if (isReferenceObject(schema)) {
|
|
48
|
+
return schema;
|
|
49
|
+
}
|
|
50
|
+
const converted = Object.assign({}, schema);
|
|
51
|
+
if (converted.properties) {
|
|
52
|
+
converted.properties = Object.entries(converted.properties).reduce((acc, [key, value]) => {
|
|
53
|
+
acc[key] = normalizeNullableSchema(value);
|
|
54
|
+
return acc;
|
|
55
|
+
}, {});
|
|
56
|
+
}
|
|
57
|
+
if (converted.patternProperties) {
|
|
58
|
+
converted.patternProperties = Object.entries(converted.patternProperties).reduce((acc, [key, value]) => {
|
|
59
|
+
acc[key] = normalizeNullableSchema(value);
|
|
60
|
+
return acc;
|
|
61
|
+
}, {});
|
|
62
|
+
}
|
|
63
|
+
if (converted.additionalProperties &&
|
|
64
|
+
typeof converted.additionalProperties === 'object') {
|
|
65
|
+
converted.additionalProperties = normalizeNullableSchema(converted.additionalProperties);
|
|
66
|
+
}
|
|
67
|
+
if (converted.items && typeof converted.items !== 'boolean') {
|
|
68
|
+
converted.items = normalizeNullableSchema(converted.items);
|
|
69
|
+
}
|
|
70
|
+
if (converted.allOf) {
|
|
71
|
+
converted.allOf = converted.allOf.map((item) => normalizeNullableSchema(item));
|
|
72
|
+
}
|
|
73
|
+
if (converted.oneOf) {
|
|
74
|
+
converted.oneOf = converted.oneOf.map((item) => normalizeNullableSchema(item));
|
|
75
|
+
}
|
|
76
|
+
if (converted.anyOf) {
|
|
77
|
+
converted.anyOf = converted.anyOf.map((item) => normalizeNullableSchema(item));
|
|
78
|
+
}
|
|
79
|
+
if (converted.not) {
|
|
80
|
+
converted.not = normalizeNullableSchema(converted.not);
|
|
81
|
+
}
|
|
82
|
+
const exclusiveMinimum = converted.exclusiveMinimum;
|
|
83
|
+
if (typeof exclusiveMinimum === 'boolean') {
|
|
84
|
+
if (exclusiveMinimum === true && typeof converted.minimum === 'number') {
|
|
85
|
+
converted.exclusiveMinimum = converted.minimum;
|
|
86
|
+
delete converted.minimum;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
delete converted.exclusiveMinimum;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const exclusiveMaximum = converted.exclusiveMaximum;
|
|
93
|
+
if (typeof exclusiveMaximum === 'boolean') {
|
|
94
|
+
if (exclusiveMaximum === true && typeof converted.maximum === 'number') {
|
|
95
|
+
converted.exclusiveMaximum = converted.maximum;
|
|
96
|
+
delete converted.maximum;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
delete converted.exclusiveMaximum;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const isNullable = converted.nullable === true;
|
|
103
|
+
if (converted.nullable !== undefined) {
|
|
104
|
+
delete converted.nullable;
|
|
105
|
+
}
|
|
106
|
+
if (!isNullable) {
|
|
107
|
+
return converted;
|
|
108
|
+
}
|
|
109
|
+
if (converted.type !== undefined) {
|
|
110
|
+
const types = Array.isArray(converted.type)
|
|
111
|
+
? converted.type
|
|
112
|
+
: [converted.type];
|
|
113
|
+
converted.type = types.includes('null') ? types : [...types, 'null'];
|
|
114
|
+
return converted;
|
|
115
|
+
}
|
|
116
|
+
if (converted.oneOf) {
|
|
117
|
+
converted.oneOf = appendNullOption(converted.oneOf);
|
|
118
|
+
return converted;
|
|
119
|
+
}
|
|
120
|
+
if (converted.anyOf) {
|
|
121
|
+
converted.anyOf = appendNullOption(converted.anyOf);
|
|
122
|
+
return converted;
|
|
123
|
+
}
|
|
124
|
+
if (converted.allOf) {
|
|
125
|
+
return {
|
|
126
|
+
anyOf: appendNullOption([converted])
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
anyOf: appendNullOption([converted])
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function transformMediaType(mediaType) {
|
|
134
|
+
const transformed = Object.assign({}, mediaType);
|
|
135
|
+
if (mediaType.schema) {
|
|
136
|
+
transformed.schema = normalizeNullableSchema(mediaType.schema);
|
|
137
|
+
}
|
|
138
|
+
if (mediaType.itemSchema) {
|
|
139
|
+
transformed.itemSchema = normalizeNullableSchema(mediaType.itemSchema);
|
|
140
|
+
}
|
|
141
|
+
return transformed;
|
|
142
|
+
}
|
|
143
|
+
function transformContent(content) {
|
|
144
|
+
if (!content) {
|
|
145
|
+
return content;
|
|
146
|
+
}
|
|
147
|
+
return Object.entries(content).reduce((acc, [key, value]) => {
|
|
148
|
+
acc[key] = transformMediaType(value);
|
|
149
|
+
return acc;
|
|
150
|
+
}, {});
|
|
151
|
+
}
|
|
152
|
+
function transformParameter(parameter) {
|
|
153
|
+
if (isReferenceObject(parameter)) {
|
|
154
|
+
return parameter;
|
|
155
|
+
}
|
|
156
|
+
const transformed = Object.assign({}, parameter);
|
|
157
|
+
if (parameter.schema) {
|
|
158
|
+
transformed.schema = normalizeNullableSchema(parameter.schema);
|
|
159
|
+
}
|
|
160
|
+
if (parameter.content) {
|
|
161
|
+
transformed.content =
|
|
162
|
+
transformContent(parameter.content) || parameter.content;
|
|
163
|
+
}
|
|
164
|
+
return transformed;
|
|
165
|
+
}
|
|
166
|
+
function transformHeaders(headers) {
|
|
167
|
+
if (!headers) {
|
|
168
|
+
return headers;
|
|
169
|
+
}
|
|
170
|
+
return Object.entries(headers).reduce((acc, [key, value]) => {
|
|
171
|
+
if (isReferenceObject(value)) {
|
|
172
|
+
acc[key] = value;
|
|
173
|
+
return acc;
|
|
174
|
+
}
|
|
175
|
+
const header = value;
|
|
176
|
+
const transformed = Object.assign({}, header);
|
|
177
|
+
if (header.schema) {
|
|
178
|
+
transformed.schema = normalizeNullableSchema(header.schema);
|
|
179
|
+
}
|
|
180
|
+
if (header.content) {
|
|
181
|
+
transformed.content = transformContent(header.content) || header.content;
|
|
182
|
+
}
|
|
183
|
+
acc[key] = transformed;
|
|
184
|
+
return acc;
|
|
185
|
+
}, {});
|
|
186
|
+
}
|
|
187
|
+
function transformRequestBody(requestBody) {
|
|
188
|
+
if (isReferenceObject(requestBody)) {
|
|
189
|
+
return requestBody;
|
|
190
|
+
}
|
|
191
|
+
const transformed = Object.assign({}, requestBody);
|
|
192
|
+
if (requestBody.content) {
|
|
193
|
+
transformed.content =
|
|
194
|
+
transformContent(requestBody.content) || requestBody.content;
|
|
195
|
+
}
|
|
196
|
+
return transformed;
|
|
197
|
+
}
|
|
198
|
+
function transformResponse(response) {
|
|
199
|
+
if (isReferenceObject(response)) {
|
|
200
|
+
return response;
|
|
201
|
+
}
|
|
202
|
+
const transformed = Object.assign({}, response);
|
|
203
|
+
if (response.content) {
|
|
204
|
+
transformed.content =
|
|
205
|
+
transformContent(response.content) || response.content;
|
|
206
|
+
}
|
|
207
|
+
if (response.headers) {
|
|
208
|
+
transformed.headers =
|
|
209
|
+
transformHeaders(response.headers) || response.headers;
|
|
210
|
+
}
|
|
211
|
+
return transformed;
|
|
212
|
+
}
|
|
213
|
+
function transformResponses(responses) {
|
|
214
|
+
return Object.entries(responses).reduce((acc, [status, response]) => {
|
|
215
|
+
if (response === undefined) {
|
|
216
|
+
acc[status] = response;
|
|
217
|
+
return acc;
|
|
218
|
+
}
|
|
219
|
+
acc[status] = transformResponse(response);
|
|
220
|
+
return acc;
|
|
221
|
+
}, {});
|
|
222
|
+
}
|
|
223
|
+
function transformCallbacks(callbacks) {
|
|
224
|
+
return Object.entries(callbacks).reduce((acc, [name, callback]) => {
|
|
225
|
+
if (isReferenceObject(callback)) {
|
|
226
|
+
acc[name] = callback;
|
|
227
|
+
return acc;
|
|
228
|
+
}
|
|
229
|
+
const transformedCallback = {};
|
|
230
|
+
Object.entries(callback).forEach(([path, pathItem]) => {
|
|
231
|
+
transformedCallback[path] = transformPathItem(pathItem);
|
|
232
|
+
});
|
|
233
|
+
acc[name] = transformedCallback;
|
|
234
|
+
return acc;
|
|
235
|
+
}, {});
|
|
236
|
+
}
|
|
237
|
+
function transformOperation(operation) {
|
|
238
|
+
const transformed = Object.assign({}, operation);
|
|
239
|
+
if (operation.parameters) {
|
|
240
|
+
transformed.parameters = operation.parameters.map(transformParameter);
|
|
241
|
+
}
|
|
242
|
+
if (operation.requestBody) {
|
|
243
|
+
transformed.requestBody = transformRequestBody(operation.requestBody);
|
|
244
|
+
}
|
|
245
|
+
if (operation.responses) {
|
|
246
|
+
transformed.responses = transformResponses(operation.responses);
|
|
247
|
+
}
|
|
248
|
+
if (operation.callbacks) {
|
|
249
|
+
transformed.callbacks = transformCallbacks(operation.callbacks);
|
|
250
|
+
}
|
|
251
|
+
return transformed;
|
|
252
|
+
}
|
|
253
|
+
function transformPathItem(pathItem) {
|
|
254
|
+
const transformed = Object.assign({}, pathItem);
|
|
255
|
+
if (pathItem.parameters) {
|
|
256
|
+
transformed.parameters = pathItem.parameters.map(transformParameter);
|
|
257
|
+
}
|
|
258
|
+
const operationKeys = [
|
|
259
|
+
'get',
|
|
260
|
+
'put',
|
|
261
|
+
'post',
|
|
262
|
+
'delete',
|
|
263
|
+
'options',
|
|
264
|
+
'head',
|
|
265
|
+
'patch',
|
|
266
|
+
'trace',
|
|
267
|
+
'search',
|
|
268
|
+
'query'
|
|
269
|
+
];
|
|
270
|
+
operationKeys.forEach((operationKey) => {
|
|
271
|
+
const operation = pathItem[operationKey];
|
|
272
|
+
if (operation) {
|
|
273
|
+
transformed[operationKey] = transformOperation(operation);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return transformed;
|
|
277
|
+
}
|
|
278
|
+
function transformPathItems(paths) {
|
|
279
|
+
if (!paths) {
|
|
280
|
+
return paths;
|
|
281
|
+
}
|
|
282
|
+
return Object.entries(paths).reduce((acc, [path, pathItem]) => {
|
|
283
|
+
acc[path] = transformPathItem(pathItem);
|
|
284
|
+
return acc;
|
|
285
|
+
}, {});
|
|
286
|
+
}
|
|
287
|
+
function normalizeNullableForOas31(document) {
|
|
288
|
+
var _a, _b, _c, _d, _e, _f;
|
|
289
|
+
if ((_a = document.components) === null || _a === void 0 ? void 0 : _a.schemas) {
|
|
290
|
+
Object.entries(document.components.schemas).forEach(([name, schema]) => {
|
|
291
|
+
document.components.schemas[name] = normalizeNullableSchema(schema);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if ((_b = document.components) === null || _b === void 0 ? void 0 : _b.parameters) {
|
|
295
|
+
Object.entries(document.components.parameters).forEach(([name, parameter]) => {
|
|
296
|
+
document.components.parameters[name] = transformParameter(parameter);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
if ((_c = document.components) === null || _c === void 0 ? void 0 : _c.requestBodies) {
|
|
300
|
+
Object.entries(document.components.requestBodies).forEach(([name, body]) => {
|
|
301
|
+
document.components.requestBodies[name] = transformRequestBody(body);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if ((_d = document.components) === null || _d === void 0 ? void 0 : _d.responses) {
|
|
305
|
+
Object.entries(document.components.responses).forEach(([name, response]) => {
|
|
306
|
+
document.components.responses[name] = transformResponse(response);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
if ((_e = document.components) === null || _e === void 0 ? void 0 : _e.headers) {
|
|
310
|
+
document.components.headers =
|
|
311
|
+
transformHeaders(document.components.headers) ||
|
|
312
|
+
document.components.headers;
|
|
313
|
+
}
|
|
314
|
+
if ((_f = document.components) === null || _f === void 0 ? void 0 : _f.callbacks) {
|
|
315
|
+
document.components.callbacks = transformCallbacks(document.components.callbacks);
|
|
316
|
+
}
|
|
317
|
+
document.paths = transformPathItems(document.paths) || {};
|
|
318
|
+
if (document.webhooks) {
|
|
319
|
+
document.webhooks = transformPathItems(document.webhooks);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
24
322
|
class SwaggerModule {
|
|
323
|
+
static collectOperationTagNames(paths, webhooks) {
|
|
324
|
+
const names = [];
|
|
325
|
+
const seen = new Set();
|
|
326
|
+
const collectFromItems = (items) => {
|
|
327
|
+
if (!items) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
for (const pathItem of Object.values(items)) {
|
|
331
|
+
if (!pathItem || typeof pathItem !== 'object') {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
for (const [key, operation] of Object.entries(pathItem)) {
|
|
335
|
+
if (!SwaggerModule.HTTP_METHODS.has(String(key).toLowerCase())) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const tags = operation === null || operation === void 0 ? void 0 : operation.tags;
|
|
339
|
+
if (!Array.isArray(tags)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
for (const t of tags) {
|
|
343
|
+
if (typeof t !== 'string') {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const trimmed = t.trim();
|
|
347
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
seen.add(trimmed);
|
|
351
|
+
names.push(trimmed);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
collectFromItems(paths);
|
|
357
|
+
collectFromItems(webhooks);
|
|
358
|
+
return names;
|
|
359
|
+
}
|
|
25
360
|
static mergeWebhooks(configWebhooks, scannedWebhooks) {
|
|
26
361
|
if (!configWebhooks && !scannedWebhooks) {
|
|
27
362
|
return undefined;
|
|
28
363
|
}
|
|
29
364
|
return (0, assign_two_levels_deep_1.assignTwoLevelsDeep)({}, configWebhooks || {}, scannedWebhooks || {});
|
|
30
365
|
}
|
|
31
|
-
static mergeTags(configTags, scannedTags) {
|
|
366
|
+
static mergeTags(configTags, scannedTags, operationTagNames) {
|
|
32
367
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
33
368
|
const byName = new Map();
|
|
34
369
|
for (const tag of configTags || []) {
|
|
@@ -55,6 +390,14 @@ class SwaggerModule {
|
|
|
55
390
|
merged.kind = (_j = existing.kind) !== null && _j !== void 0 ? _j : tag.kind;
|
|
56
391
|
byName.set(tag.name, merged);
|
|
57
392
|
}
|
|
393
|
+
if (byName.size > 0) {
|
|
394
|
+
for (const name of operationTagNames || []) {
|
|
395
|
+
if (!name || byName.has(name)) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
byName.set(name, { name });
|
|
399
|
+
}
|
|
400
|
+
}
|
|
58
401
|
const merged = [...byName.values()];
|
|
59
402
|
return merged.length > 0 ? merged : undefined;
|
|
60
403
|
}
|
|
@@ -73,8 +416,16 @@ class SwaggerModule {
|
|
|
73
416
|
groupToTags.set(parent, []);
|
|
74
417
|
groupToSeen.set(parent, new Set());
|
|
75
418
|
}
|
|
76
|
-
|
|
77
|
-
|
|
419
|
+
let list = groupToTags.get(parent);
|
|
420
|
+
if (!list) {
|
|
421
|
+
list = [];
|
|
422
|
+
groupToTags.set(parent, list);
|
|
423
|
+
}
|
|
424
|
+
let seen = groupToSeen.get(parent);
|
|
425
|
+
if (!seen) {
|
|
426
|
+
seen = new Set();
|
|
427
|
+
groupToSeen.set(parent, seen);
|
|
428
|
+
}
|
|
78
429
|
if (!seen.has(tag.name)) {
|
|
79
430
|
seen.add(tag.name);
|
|
80
431
|
list.push(tag.name);
|
|
@@ -104,9 +455,13 @@ class SwaggerModule {
|
|
|
104
455
|
const swaggerScanner = new swagger_scanner_1.SwaggerScanner();
|
|
105
456
|
const document = swaggerScanner.scanApplication(app, options);
|
|
106
457
|
document.components = (0, assign_two_levels_deep_1.assignTwoLevelsDeep)({}, config.components, document.components);
|
|
107
|
-
const
|
|
458
|
+
const operationTagNames = SwaggerModule.collectOperationTagNames(document.paths, document.webhooks);
|
|
459
|
+
const mergedTags = SwaggerModule.mergeTags(config.tags, document.tags, operationTagNames);
|
|
108
460
|
const mergedWebhooks = SwaggerModule.mergeWebhooks(config.webhooks, document.webhooks);
|
|
109
461
|
const mergedDocument = Object.assign(Object.assign(Object.assign(Object.assign({ openapi: '3.0.0', paths: {} }, config), document), (mergedTags ? { tags: mergedTags } : {})), (mergedWebhooks ? { webhooks: mergedWebhooks } : {}));
|
|
462
|
+
if (isOas31OrAbove(mergedDocument.openapi)) {
|
|
463
|
+
normalizeNullableForOas31(mergedDocument);
|
|
464
|
+
}
|
|
110
465
|
if (mergedDocument['x-tagGroups'] === undefined) {
|
|
111
466
|
const xTagGroups = SwaggerModule.buildXTagGroups(mergedDocument.tags);
|
|
112
467
|
if (xTagGroups) {
|
|
@@ -281,3 +636,13 @@ class SwaggerModule {
|
|
|
281
636
|
}
|
|
282
637
|
exports.SwaggerModule = SwaggerModule;
|
|
283
638
|
SwaggerModule.metadataLoader = new metadata_loader_1.MetadataLoader();
|
|
639
|
+
SwaggerModule.HTTP_METHODS = new Set([
|
|
640
|
+
'get',
|
|
641
|
+
'put',
|
|
642
|
+
'post',
|
|
643
|
+
'delete',
|
|
644
|
+
'options',
|
|
645
|
+
'head',
|
|
646
|
+
'patch',
|
|
647
|
+
'trace'
|
|
648
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestjs-openapi-next",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A fork of @nestjs/swagger support OAS 3.2",
|
|
5
5
|
"author": "undownding",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"js-yaml": "4.1.1",
|
|
33
33
|
"lodash": "4.17.21",
|
|
34
34
|
"path-to-regexp": "8.3.0",
|
|
35
|
-
"swagger-ui-dist": "5.
|
|
35
|
+
"swagger-ui-dist": "5.31.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@commitlint/cli": "20.3.0",
|
|
39
39
|
"@commitlint/config-angular": "20.3.0",
|
|
40
40
|
"@eslint/eslintrc": "3.3.3",
|
|
41
41
|
"@eslint/js": "9.39.2",
|
|
42
|
-
"@fastify/static": "
|
|
42
|
+
"@fastify/static": "9.0.0",
|
|
43
43
|
"@nestjs/common": "11.1.11",
|
|
44
44
|
"@nestjs/core": "11.1.11",
|
|
45
45
|
"@nestjs/platform-express": "11.1.11",
|
|
@@ -64,15 +64,15 @@
|
|
|
64
64
|
"prettier": "3.7.4",
|
|
65
65
|
"prettier-v2": "npm:prettier@2.8.8",
|
|
66
66
|
"reflect-metadata": "0.2.2",
|
|
67
|
-
"release-it": "19.2.
|
|
68
|
-
"supertest": "7.
|
|
67
|
+
"release-it": "19.2.3",
|
|
68
|
+
"supertest": "7.2.2",
|
|
69
69
|
"swagger-parser": "10.0.3",
|
|
70
70
|
"ts-jest": "29.4.6",
|
|
71
71
|
"typescript": "5.9.3",
|
|
72
|
-
"typescript-eslint": "8.
|
|
72
|
+
"typescript-eslint": "8.52.0"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"@fastify/static": "^8.0.0",
|
|
75
|
+
"@fastify/static": "^8.0.0 || ^9.0.0",
|
|
76
76
|
"@nestjs/common": "^11.0.1",
|
|
77
77
|
"@nestjs/core": "^11.0.1",
|
|
78
78
|
"class-transformer": "*",
|