nestjs-api-describe 1.0.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/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/index.d.mts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +237 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +229 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +81 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gabriele Partiti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# nestjs-api-describe
|
|
2
|
+
|
|
3
|
+
[](https://github.com/gabrielepartiti/nestjs-api-describe/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/nestjs-api-describe)
|
|
5
|
+
|
|
6
|
+
Composite decorators for standardizing OpenAPI documentation and input validation in NestJS APIs.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add nestjs-api-describe
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Peer dependencies
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @nestjs/common @nestjs/swagger class-validator class-transformer reflect-metadata
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### ControllerDescribe
|
|
23
|
+
|
|
24
|
+
A composite method decorator that combines HTTP method, Swagger documentation, auth metadata, and error responses in a single declaration.
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Controller } from '@nestjs/common';
|
|
28
|
+
import { ControllerDescribe } from 'nestjs-api-describe';
|
|
29
|
+
|
|
30
|
+
@Controller('users')
|
|
31
|
+
export class UsersController {
|
|
32
|
+
@ControllerDescribe({
|
|
33
|
+
method: 'GET',
|
|
34
|
+
path: ':id',
|
|
35
|
+
summary: 'Get user by ID',
|
|
36
|
+
description: 'Returns a single user by their unique identifier',
|
|
37
|
+
responseType: UserResponseDto,
|
|
38
|
+
errors: [404],
|
|
39
|
+
})
|
|
40
|
+
findOne(@Param('id') id: string) {
|
|
41
|
+
return this.usersService.findOne(id);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@ControllerDescribe({
|
|
45
|
+
method: 'POST',
|
|
46
|
+
summary: 'Register a new user',
|
|
47
|
+
responseType: UserResponseDto,
|
|
48
|
+
isPublic: true,
|
|
49
|
+
errors: [409],
|
|
50
|
+
})
|
|
51
|
+
register(@Body() dto: CreateUserDto) {
|
|
52
|
+
return this.usersService.create(dto);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Options
|
|
58
|
+
|
|
59
|
+
| Option | Type | Required | Default | Description |
|
|
60
|
+
| -------------- | ---------------- | -------- | ------- | ---------------------------------------- |
|
|
61
|
+
| `method` | `HttpMethod` | yes | | `GET`, `POST`, `PUT`, `PATCH`, `DELETE` |
|
|
62
|
+
| `path` | `string` | no | `''` | Route path |
|
|
63
|
+
| `summary` | `string` | yes | | Swagger operation summary |
|
|
64
|
+
| `description` | `string` | no | | Swagger operation description |
|
|
65
|
+
| `responseType` | `Type<unknown>` | yes | | Response DTO class for Swagger |
|
|
66
|
+
| `isPublic` | `boolean` | no | `false` | If true, skips auth decorators |
|
|
67
|
+
| `errors` | `number[]` | no | `[]` | Additional HTTP error status codes |
|
|
68
|
+
|
|
69
|
+
Non-public routes automatically include `401` and `403` error responses and `@ApiBearerAuth()`.
|
|
70
|
+
|
|
71
|
+
### DtoDescribe
|
|
72
|
+
|
|
73
|
+
A composite property decorator that combines Swagger `@ApiProperty`, class-validator validators, and class-transformer transforms.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { DtoDescribe } from 'nestjs-api-describe';
|
|
77
|
+
|
|
78
|
+
export class CreateUserDto {
|
|
79
|
+
@DtoDescribe({
|
|
80
|
+
description: 'User email address',
|
|
81
|
+
type: 'email',
|
|
82
|
+
example: 'user@example.com',
|
|
83
|
+
})
|
|
84
|
+
email!: string;
|
|
85
|
+
|
|
86
|
+
@DtoDescribe({
|
|
87
|
+
description: 'Display name',
|
|
88
|
+
type: 'string',
|
|
89
|
+
minLength: 2,
|
|
90
|
+
maxLength: 50,
|
|
91
|
+
})
|
|
92
|
+
name!: string;
|
|
93
|
+
|
|
94
|
+
@DtoDescribe({
|
|
95
|
+
description: 'User age',
|
|
96
|
+
type: 'int',
|
|
97
|
+
min: 0,
|
|
98
|
+
max: 150,
|
|
99
|
+
})
|
|
100
|
+
age!: number;
|
|
101
|
+
|
|
102
|
+
@DtoDescribe({
|
|
103
|
+
description: 'User preferences',
|
|
104
|
+
type: 'object',
|
|
105
|
+
required: false,
|
|
106
|
+
})
|
|
107
|
+
preferences?: Record<string, unknown>;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Options
|
|
112
|
+
|
|
113
|
+
| Option | Type | Required | Default | Description |
|
|
114
|
+
| ------------ | ---------------- | -------- | ------- | ---------------------------------- |
|
|
115
|
+
| `description`| `string` | yes | | Swagger property description |
|
|
116
|
+
| `type` | `DtoType` | yes | | Field type (see supported types) |
|
|
117
|
+
| `example` | `unknown` | no | | Swagger example value |
|
|
118
|
+
| `required` | `boolean` | no | `true` | Adds `IsNotEmpty` or `IsOptional` |
|
|
119
|
+
| `enum` | `object` | no | | Enum object (for `type: 'enum'`) |
|
|
120
|
+
| `isArray` | `boolean` | no | | Marks as array in Swagger |
|
|
121
|
+
| `nested` | `Type<unknown>` | no | | Nested DTO class |
|
|
122
|
+
| `minLength` | `number` | no | | Min string length |
|
|
123
|
+
| `maxLength` | `number` | no | | Max string length |
|
|
124
|
+
| `min` | `number` | no | | Min numeric value |
|
|
125
|
+
| `max` | `number` | no | | Max numeric value |
|
|
126
|
+
| `pattern` | `RegExp` | no | | Regex pattern validation |
|
|
127
|
+
|
|
128
|
+
#### Supported types
|
|
129
|
+
|
|
130
|
+
| Type | Validators applied |
|
|
131
|
+
| ------------ | ----------------------------------------- |
|
|
132
|
+
| `string` | `IsString`, `Length`, `Transform(trim)` |
|
|
133
|
+
| `number` | `IsNumber`, `Min`, `Max` |
|
|
134
|
+
| `int` | `IsInt`, `Min`, `Max` |
|
|
135
|
+
| `boolean` | `IsBoolean` |
|
|
136
|
+
| `uuid` | `IsUUID` |
|
|
137
|
+
| `email` | `IsEmail`, `Transform(lowercase + trim)` |
|
|
138
|
+
| `date` | `IsDate`, `Type(() => Date)` |
|
|
139
|
+
| `dateString` | `IsDateString` |
|
|
140
|
+
| `url` | `IsUrl` |
|
|
141
|
+
| `enum` | `IsEnum` |
|
|
142
|
+
| `nested` | `ValidateNested`, `Type(() => nested)` |
|
|
143
|
+
| `array` | `IsArray`, `ValidateNested` (if nested) |
|
|
144
|
+
| `object` | `IsObject` |
|
|
145
|
+
|
|
146
|
+
### ApiDescribeModule
|
|
147
|
+
|
|
148
|
+
Optional NestJS dynamic module for global configuration.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { Module } from '@nestjs/common';
|
|
152
|
+
import { ApiDescribeModule } from 'nestjs-api-describe';
|
|
153
|
+
|
|
154
|
+
@Module({
|
|
155
|
+
imports: [
|
|
156
|
+
ApiDescribeModule.forRoot({
|
|
157
|
+
defaultErrors: [400, 500],
|
|
158
|
+
customErrorDescriptions: {
|
|
159
|
+
400: 'Validation failed',
|
|
160
|
+
500: 'Something went wrong',
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
],
|
|
164
|
+
})
|
|
165
|
+
export class AppModule {}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Type, DynamicModule } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
|
+
type DtoType = 'string' | 'number' | 'int' | 'boolean' | 'uuid' | 'email' | 'date' | 'dateString' | 'url' | 'enum' | 'nested' | 'array' | 'object';
|
|
5
|
+
interface ControllerDescribeOptions {
|
|
6
|
+
method: HttpMethod;
|
|
7
|
+
path?: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
responseType: Type<unknown>;
|
|
11
|
+
isPublic?: boolean;
|
|
12
|
+
errors?: number[];
|
|
13
|
+
}
|
|
14
|
+
interface DtoDescribeOptions {
|
|
15
|
+
description: string;
|
|
16
|
+
example?: unknown;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
type: DtoType;
|
|
19
|
+
enum?: object;
|
|
20
|
+
isArray?: boolean;
|
|
21
|
+
nested?: Type<unknown>;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
min?: number;
|
|
25
|
+
max?: number;
|
|
26
|
+
pattern?: RegExp;
|
|
27
|
+
}
|
|
28
|
+
interface ApiDescribeModuleOptions {
|
|
29
|
+
defaultErrors?: number[];
|
|
30
|
+
customErrorDescriptions?: Record<number, string>;
|
|
31
|
+
}
|
|
32
|
+
declare const IS_PUBLIC_KEY = "isPublic";
|
|
33
|
+
declare const API_DESCRIBE_OPTIONS = "API_DESCRIBE_OPTIONS";
|
|
34
|
+
|
|
35
|
+
declare function ControllerDescribe(options: ControllerDescribeOptions): MethodDecorator;
|
|
36
|
+
|
|
37
|
+
declare function DtoDescribe(options: DtoDescribeOptions): PropertyDecorator;
|
|
38
|
+
|
|
39
|
+
declare class ApiDescribeModule {
|
|
40
|
+
static forRoot(options?: ApiDescribeModuleOptions): DynamicModule;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { API_DESCRIBE_OPTIONS, ApiDescribeModule, type ApiDescribeModuleOptions, ControllerDescribe, type ControllerDescribeOptions, DtoDescribe, type DtoDescribeOptions, type DtoType, type HttpMethod, IS_PUBLIC_KEY };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Type, DynamicModule } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
|
+
type DtoType = 'string' | 'number' | 'int' | 'boolean' | 'uuid' | 'email' | 'date' | 'dateString' | 'url' | 'enum' | 'nested' | 'array' | 'object';
|
|
5
|
+
interface ControllerDescribeOptions {
|
|
6
|
+
method: HttpMethod;
|
|
7
|
+
path?: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
responseType: Type<unknown>;
|
|
11
|
+
isPublic?: boolean;
|
|
12
|
+
errors?: number[];
|
|
13
|
+
}
|
|
14
|
+
interface DtoDescribeOptions {
|
|
15
|
+
description: string;
|
|
16
|
+
example?: unknown;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
type: DtoType;
|
|
19
|
+
enum?: object;
|
|
20
|
+
isArray?: boolean;
|
|
21
|
+
nested?: Type<unknown>;
|
|
22
|
+
minLength?: number;
|
|
23
|
+
maxLength?: number;
|
|
24
|
+
min?: number;
|
|
25
|
+
max?: number;
|
|
26
|
+
pattern?: RegExp;
|
|
27
|
+
}
|
|
28
|
+
interface ApiDescribeModuleOptions {
|
|
29
|
+
defaultErrors?: number[];
|
|
30
|
+
customErrorDescriptions?: Record<number, string>;
|
|
31
|
+
}
|
|
32
|
+
declare const IS_PUBLIC_KEY = "isPublic";
|
|
33
|
+
declare const API_DESCRIBE_OPTIONS = "API_DESCRIBE_OPTIONS";
|
|
34
|
+
|
|
35
|
+
declare function ControllerDescribe(options: ControllerDescribeOptions): MethodDecorator;
|
|
36
|
+
|
|
37
|
+
declare function DtoDescribe(options: DtoDescribeOptions): PropertyDecorator;
|
|
38
|
+
|
|
39
|
+
declare class ApiDescribeModule {
|
|
40
|
+
static forRoot(options?: ApiDescribeModuleOptions): DynamicModule;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export { API_DESCRIBE_OPTIONS, ApiDescribeModule, type ApiDescribeModuleOptions, ControllerDescribe, type ControllerDescribeOptions, DtoDescribe, type DtoDescribeOptions, type DtoType, type HttpMethod, IS_PUBLIC_KEY };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/index.ts
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
API_DESCRIBE_OPTIONS: () => API_DESCRIBE_OPTIONS,
|
|
32
|
+
ApiDescribeModule: () => ApiDescribeModule,
|
|
33
|
+
ControllerDescribe: () => ControllerDescribe,
|
|
34
|
+
DtoDescribe: () => DtoDescribe,
|
|
35
|
+
IS_PUBLIC_KEY: () => IS_PUBLIC_KEY
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/types.ts
|
|
40
|
+
var IS_PUBLIC_KEY = "isPublic";
|
|
41
|
+
var API_DESCRIBE_OPTIONS = "API_DESCRIBE_OPTIONS";
|
|
42
|
+
|
|
43
|
+
// src/controller-describe.decorator.ts
|
|
44
|
+
var import_common = require("@nestjs/common");
|
|
45
|
+
var import_swagger = require("@nestjs/swagger");
|
|
46
|
+
var METHOD_DECORATORS = {
|
|
47
|
+
GET: import_common.Get,
|
|
48
|
+
POST: import_common.Post,
|
|
49
|
+
PUT: import_common.Put,
|
|
50
|
+
PATCH: import_common.Patch,
|
|
51
|
+
DELETE: import_common.Delete
|
|
52
|
+
};
|
|
53
|
+
var ERROR_DESCRIPTIONS = {
|
|
54
|
+
400: "Bad Request",
|
|
55
|
+
401: "Unauthorized",
|
|
56
|
+
403: "Forbidden",
|
|
57
|
+
404: "Not Found",
|
|
58
|
+
409: "Conflict",
|
|
59
|
+
422: "Unprocessable Entity",
|
|
60
|
+
500: "Internal Server Error"
|
|
61
|
+
};
|
|
62
|
+
function getErrorDescription(code) {
|
|
63
|
+
return ERROR_DESCRIPTIONS[code] ?? `Error ${String(code)}`;
|
|
64
|
+
}
|
|
65
|
+
function getErrorDecorators(errors) {
|
|
66
|
+
return errors.map(
|
|
67
|
+
(code) => (0, import_swagger.ApiResponse)({
|
|
68
|
+
status: code,
|
|
69
|
+
description: getErrorDescription(code)
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function getSuccessStatus(method) {
|
|
74
|
+
return method === "POST" ? 201 : 200;
|
|
75
|
+
}
|
|
76
|
+
function addPublicDecorators(decorators, isPublic, allErrors) {
|
|
77
|
+
if (isPublic) {
|
|
78
|
+
allErrors.delete(401);
|
|
79
|
+
allErrors.delete(403);
|
|
80
|
+
decorators.push((0, import_common.SetMetadata)(IS_PUBLIC_KEY, true));
|
|
81
|
+
} else {
|
|
82
|
+
decorators.push((0, import_swagger.ApiBearerAuth)());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function buildDecorators(options) {
|
|
86
|
+
const {
|
|
87
|
+
method,
|
|
88
|
+
path = "",
|
|
89
|
+
summary,
|
|
90
|
+
description,
|
|
91
|
+
responseType,
|
|
92
|
+
isPublic = false,
|
|
93
|
+
errors = []
|
|
94
|
+
} = options;
|
|
95
|
+
const decorators = [];
|
|
96
|
+
decorators.push(METHOD_DECORATORS[method](path));
|
|
97
|
+
const operationOptions = description !== void 0 ? { summary, description } : { summary };
|
|
98
|
+
decorators.push((0, import_swagger.ApiOperation)(operationOptions));
|
|
99
|
+
decorators.push((0, import_swagger.ApiResponse)({ status: getSuccessStatus(method), type: responseType }));
|
|
100
|
+
const allErrors = /* @__PURE__ */ new Set([401, 403, ...errors]);
|
|
101
|
+
addPublicDecorators(decorators, isPublic, allErrors);
|
|
102
|
+
decorators.push(...getErrorDecorators([...allErrors]));
|
|
103
|
+
return decorators;
|
|
104
|
+
}
|
|
105
|
+
function ControllerDescribe(options) {
|
|
106
|
+
return (0, import_common.applyDecorators)(...buildDecorators(options));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/dto-describe.decorator.ts
|
|
110
|
+
var import_common2 = require("@nestjs/common");
|
|
111
|
+
var import_swagger2 = require("@nestjs/swagger");
|
|
112
|
+
var import_class_transformer = require("class-transformer");
|
|
113
|
+
var import_class_validator = require("class-validator");
|
|
114
|
+
function buildApiPropertyOptions(options) {
|
|
115
|
+
const apiOptions = {
|
|
116
|
+
description: options.description,
|
|
117
|
+
required: options.required ?? true
|
|
118
|
+
};
|
|
119
|
+
if (options.example !== void 0) {
|
|
120
|
+
apiOptions.example = options.example;
|
|
121
|
+
}
|
|
122
|
+
if (options.isArray === true) {
|
|
123
|
+
apiOptions.isArray = true;
|
|
124
|
+
}
|
|
125
|
+
if (options.type === "enum" && options.enum !== void 0) {
|
|
126
|
+
apiOptions.enum = options.enum;
|
|
127
|
+
}
|
|
128
|
+
if (options.nested !== void 0) {
|
|
129
|
+
apiOptions.type = options.nested;
|
|
130
|
+
}
|
|
131
|
+
return apiOptions;
|
|
132
|
+
}
|
|
133
|
+
function getStringDecorators(options) {
|
|
134
|
+
const decorators = [(0, import_class_validator.IsString)()];
|
|
135
|
+
if (options.minLength !== void 0 || options.maxLength !== void 0) {
|
|
136
|
+
decorators.push((0, import_class_validator.Length)(options.minLength ?? 0, options.maxLength));
|
|
137
|
+
}
|
|
138
|
+
decorators.push(
|
|
139
|
+
(0, import_class_transformer.Transform)(
|
|
140
|
+
({ value }) => typeof value === "string" ? value.trim() : value
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
return decorators;
|
|
144
|
+
}
|
|
145
|
+
function getNumberDecorators(options) {
|
|
146
|
+
const decorators = [(0, import_class_validator.IsNumber)()];
|
|
147
|
+
if (options.min !== void 0) {
|
|
148
|
+
decorators.push((0, import_class_validator.Min)(options.min));
|
|
149
|
+
}
|
|
150
|
+
if (options.max !== void 0) {
|
|
151
|
+
decorators.push((0, import_class_validator.Max)(options.max));
|
|
152
|
+
}
|
|
153
|
+
return decorators;
|
|
154
|
+
}
|
|
155
|
+
function getIntDecorators(options) {
|
|
156
|
+
const decorators = [(0, import_class_validator.IsInt)()];
|
|
157
|
+
if (options.min !== void 0) {
|
|
158
|
+
decorators.push((0, import_class_validator.Min)(options.min));
|
|
159
|
+
}
|
|
160
|
+
if (options.max !== void 0) {
|
|
161
|
+
decorators.push((0, import_class_validator.Max)(options.max));
|
|
162
|
+
}
|
|
163
|
+
return decorators;
|
|
164
|
+
}
|
|
165
|
+
function getEmailDecorators() {
|
|
166
|
+
return [
|
|
167
|
+
(0, import_class_validator.IsEmail)(),
|
|
168
|
+
(0, import_class_transformer.Transform)(
|
|
169
|
+
({ value }) => typeof value === "string" ? value.toLowerCase().trim() : value
|
|
170
|
+
)
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
function getNestedDecorators(nested) {
|
|
174
|
+
return [(0, import_class_validator.ValidateNested)(), (0, import_class_transformer.Type)(() => nested)];
|
|
175
|
+
}
|
|
176
|
+
function getArrayDecorators(nested) {
|
|
177
|
+
const decorators = [(0, import_class_validator.IsArray)()];
|
|
178
|
+
if (nested !== void 0) {
|
|
179
|
+
decorators.push((0, import_class_validator.ValidateNested)({ each: true }), (0, import_class_transformer.Type)(() => nested));
|
|
180
|
+
}
|
|
181
|
+
return decorators;
|
|
182
|
+
}
|
|
183
|
+
var TYPE_DECORATOR_MAP = {
|
|
184
|
+
string: getStringDecorators,
|
|
185
|
+
number: getNumberDecorators,
|
|
186
|
+
int: getIntDecorators,
|
|
187
|
+
boolean: () => [(0, import_class_validator.IsBoolean)()],
|
|
188
|
+
uuid: () => [(0, import_class_validator.IsUUID)()],
|
|
189
|
+
email: getEmailDecorators,
|
|
190
|
+
date: () => [(0, import_class_validator.IsDate)(), (0, import_class_transformer.Type)(() => Date)],
|
|
191
|
+
dateString: () => [(0, import_class_validator.IsDateString)()],
|
|
192
|
+
url: () => [(0, import_class_validator.IsUrl)()],
|
|
193
|
+
enum: (opts) => opts.enum !== void 0 ? [(0, import_class_validator.IsEnum)(opts.enum)] : [],
|
|
194
|
+
nested: (opts) => opts.nested !== void 0 ? getNestedDecorators(opts.nested) : [],
|
|
195
|
+
array: (opts) => getArrayDecorators(opts.nested),
|
|
196
|
+
object: () => [(0, import_class_validator.IsObject)()]
|
|
197
|
+
};
|
|
198
|
+
function getTypeDecorators(options) {
|
|
199
|
+
const decoratorFn = TYPE_DECORATOR_MAP[options.type];
|
|
200
|
+
return decoratorFn(options);
|
|
201
|
+
}
|
|
202
|
+
function DtoDescribe(options) {
|
|
203
|
+
const decorators = [];
|
|
204
|
+
decorators.push((0, import_swagger2.ApiProperty)(buildApiPropertyOptions(options)));
|
|
205
|
+
const isRequired = options.required ?? true;
|
|
206
|
+
decorators.push(isRequired ? (0, import_class_validator.IsNotEmpty)() : (0, import_class_validator.IsOptional)());
|
|
207
|
+
decorators.push(...getTypeDecorators(options));
|
|
208
|
+
if (options.pattern !== void 0) {
|
|
209
|
+
decorators.push((0, import_class_validator.Matches)(options.pattern));
|
|
210
|
+
}
|
|
211
|
+
return (0, import_common2.applyDecorators)(...decorators);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/api-describe.module.ts
|
|
215
|
+
var import_common3 = require("@nestjs/common");
|
|
216
|
+
var ApiDescribeModule = class {
|
|
217
|
+
static forRoot(options) {
|
|
218
|
+
return {
|
|
219
|
+
module: ApiDescribeModule,
|
|
220
|
+
global: true,
|
|
221
|
+
providers: [{ provide: API_DESCRIBE_OPTIONS, useValue: options ?? {} }],
|
|
222
|
+
exports: [API_DESCRIBE_OPTIONS]
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
ApiDescribeModule = __decorateClass([
|
|
227
|
+
(0, import_common3.Module)({})
|
|
228
|
+
], ApiDescribeModule);
|
|
229
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
230
|
+
0 && (module.exports = {
|
|
231
|
+
API_DESCRIBE_OPTIONS,
|
|
232
|
+
ApiDescribeModule,
|
|
233
|
+
ControllerDescribe,
|
|
234
|
+
DtoDescribe,
|
|
235
|
+
IS_PUBLIC_KEY
|
|
236
|
+
});
|
|
237
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/controller-describe.decorator.ts","../src/dto-describe.decorator.ts","../src/api-describe.module.ts"],"sourcesContent":["export type {\n HttpMethod,\n DtoType,\n ControllerDescribeOptions,\n DtoDescribeOptions,\n ApiDescribeModuleOptions,\n} from './types';\nexport { IS_PUBLIC_KEY, API_DESCRIBE_OPTIONS } from './types';\nexport { ControllerDescribe } from './controller-describe.decorator';\nexport { DtoDescribe } from './dto-describe.decorator';\nexport { ApiDescribeModule } from './api-describe.module';\n","import type { Type } from '@nestjs/common';\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type DtoType =\n | 'string'\n | 'number'\n | 'int'\n | 'boolean'\n | 'uuid'\n | 'email'\n | 'date'\n | 'dateString'\n | 'url'\n | 'enum'\n | 'nested'\n | 'array'\n | 'object';\n\nexport interface ControllerDescribeOptions {\n method: HttpMethod;\n path?: string;\n summary: string;\n description?: string;\n responseType: Type<unknown>;\n isPublic?: boolean;\n errors?: number[];\n}\n\nexport interface DtoDescribeOptions {\n description: string;\n example?: unknown;\n required?: boolean;\n type: DtoType;\n enum?: object;\n isArray?: boolean;\n nested?: Type<unknown>;\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n pattern?: RegExp;\n}\n\nexport interface ApiDescribeModuleOptions {\n defaultErrors?: number[];\n customErrorDescriptions?: Record<number, string>;\n}\n\nexport const IS_PUBLIC_KEY = 'isPublic';\nexport const API_DESCRIBE_OPTIONS = 'API_DESCRIBE_OPTIONS';\n","import { applyDecorators, Delete, Get, Patch, Post, Put, SetMetadata } from '@nestjs/common';\nimport { ApiBearerAuth, ApiOperation, ApiResponse } from '@nestjs/swagger';\nimport type { ControllerDescribeOptions, HttpMethod } from './types';\nimport { IS_PUBLIC_KEY } from './types';\n\nconst METHOD_DECORATORS: Record<HttpMethod, (path?: string) => MethodDecorator> = {\n GET: Get,\n POST: Post,\n PUT: Put,\n PATCH: Patch,\n DELETE: Delete,\n};\n\nconst ERROR_DESCRIPTIONS: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error',\n};\n\nfunction getErrorDescription(code: number): string {\n return ERROR_DESCRIPTIONS[code] ?? `Error ${String(code)}`;\n}\n\nfunction getErrorDecorators(errors: number[]): MethodDecorator[] {\n return errors.map((code) =>\n ApiResponse({\n status: code,\n description: getErrorDescription(code),\n }),\n );\n}\n\nfunction getSuccessStatus(method: HttpMethod): number {\n return method === 'POST' ? 201 : 200;\n}\n\nfunction addPublicDecorators(\n decorators: (MethodDecorator | ClassDecorator)[],\n isPublic: boolean,\n allErrors: Set<number>,\n): void {\n if (isPublic) {\n allErrors.delete(401);\n allErrors.delete(403);\n decorators.push(SetMetadata(IS_PUBLIC_KEY, true));\n } else {\n decorators.push(ApiBearerAuth());\n }\n}\n\nfunction buildDecorators(options: ControllerDescribeOptions): (MethodDecorator | ClassDecorator)[] {\n const {\n method,\n path = '',\n summary,\n description,\n responseType,\n isPublic = false,\n errors = [],\n } = options;\n\n const decorators: (MethodDecorator | ClassDecorator)[] = [];\n decorators.push(METHOD_DECORATORS[method](path));\n\n const operationOptions = description !== undefined ? { summary, description } : { summary };\n decorators.push(ApiOperation(operationOptions));\n decorators.push(ApiResponse({ status: getSuccessStatus(method), type: responseType }));\n\n const allErrors = new Set([401, 403, ...errors]);\n addPublicDecorators(decorators, isPublic, allErrors);\n decorators.push(...getErrorDecorators([...allErrors]));\n\n return decorators;\n}\n\nexport function ControllerDescribe(options: ControllerDescribeOptions): MethodDecorator {\n return applyDecorators(...buildDecorators(options));\n}\n","import type { Type } from '@nestjs/common';\nimport { applyDecorators } from '@nestjs/common';\nimport type { ApiPropertyOptions } from '@nestjs/swagger';\nimport { ApiProperty } from '@nestjs/swagger';\nimport { Transform, Type as ClassType } from 'class-transformer';\nimport {\n IsArray,\n IsBoolean,\n IsDate,\n IsDateString,\n IsEmail,\n IsEnum,\n IsInt,\n IsNotEmpty,\n IsNumber,\n IsObject,\n IsOptional,\n IsString,\n IsUrl,\n IsUUID,\n Length,\n Matches,\n Max,\n Min,\n ValidateNested,\n} from 'class-validator';\nimport type { DtoDescribeOptions, DtoType } from './types';\n\nfunction buildApiPropertyOptions(options: DtoDescribeOptions): ApiPropertyOptions {\n const apiOptions: ApiPropertyOptions = {\n description: options.description,\n required: options.required ?? true,\n };\n\n if (options.example !== undefined) {\n apiOptions.example = options.example;\n }\n\n if (options.isArray === true) {\n apiOptions.isArray = true;\n }\n\n if (options.type === 'enum' && options.enum !== undefined) {\n apiOptions.enum = options.enum;\n }\n\n if (options.nested !== undefined) {\n apiOptions.type = options.nested;\n }\n\n return apiOptions;\n}\n\nfunction getStringDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsString()];\n\n if (options.minLength !== undefined || options.maxLength !== undefined) {\n decorators.push(Length(options.minLength ?? 0, options.maxLength));\n }\n\n decorators.push(\n Transform(({ value }: { value: unknown }) =>\n typeof value === 'string' ? value.trim() : value,\n ),\n );\n\n return decorators;\n}\n\nfunction getNumberDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsNumber()];\n\n if (options.min !== undefined) {\n decorators.push(Min(options.min));\n }\n\n if (options.max !== undefined) {\n decorators.push(Max(options.max));\n }\n\n return decorators;\n}\n\nfunction getIntDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsInt()];\n\n if (options.min !== undefined) {\n decorators.push(Min(options.min));\n }\n\n if (options.max !== undefined) {\n decorators.push(Max(options.max));\n }\n\n return decorators;\n}\n\nfunction getEmailDecorators(): PropertyDecorator[] {\n return [\n IsEmail(),\n Transform(({ value }: { value: unknown }) =>\n typeof value === 'string' ? value.toLowerCase().trim() : value,\n ),\n ];\n}\n\nfunction getNestedDecorators(nested: Type<unknown>): PropertyDecorator[] {\n return [ValidateNested(), ClassType(() => nested)];\n}\n\nfunction getArrayDecorators(nested?: Type<unknown>): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsArray()];\n\n if (nested !== undefined) {\n decorators.push(ValidateNested({ each: true }), ClassType(() => nested));\n }\n\n return decorators;\n}\n\nconst TYPE_DECORATOR_MAP: Record<DtoType, (options: DtoDescribeOptions) => PropertyDecorator[]> = {\n string: getStringDecorators,\n number: getNumberDecorators,\n int: getIntDecorators,\n boolean: () => [IsBoolean()],\n uuid: () => [IsUUID()],\n email: getEmailDecorators,\n date: () => [IsDate(), ClassType(() => Date)],\n dateString: () => [IsDateString()],\n url: () => [IsUrl()],\n enum: (opts) => (opts.enum !== undefined ? [IsEnum(opts.enum)] : []),\n nested: (opts) => (opts.nested !== undefined ? getNestedDecorators(opts.nested) : []),\n array: (opts) => getArrayDecorators(opts.nested),\n object: () => [IsObject()],\n};\n\nfunction getTypeDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decoratorFn = TYPE_DECORATOR_MAP[options.type];\n return decoratorFn(options);\n}\n\nexport function DtoDescribe(options: DtoDescribeOptions): PropertyDecorator {\n const decorators: PropertyDecorator[] = [];\n\n decorators.push(ApiProperty(buildApiPropertyOptions(options)));\n\n const isRequired = options.required ?? true;\n decorators.push(isRequired ? IsNotEmpty() : IsOptional());\n decorators.push(...getTypeDecorators(options));\n\n if (options.pattern !== undefined) {\n decorators.push(Matches(options.pattern));\n }\n\n return applyDecorators(...decorators);\n}\n","import { DynamicModule, Module } from '@nestjs/common';\nimport type { ApiDescribeModuleOptions } from './types';\nimport { API_DESCRIBE_OPTIONS } from './types';\n\n@Module({})\nexport class ApiDescribeModule {\n static forRoot(options?: ApiDescribeModuleOptions): DynamicModule {\n return {\n module: ApiDescribeModule,\n global: true,\n providers: [{ provide: API_DESCRIBE_OPTIONS, useValue: options ?? {} }],\n exports: [API_DESCRIBE_OPTIONS],\n };\n }\n}\n"],"mappings":"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","names":["import_common","import_swagger","ClassType","import_common"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/types.ts
|
|
13
|
+
var IS_PUBLIC_KEY = "isPublic";
|
|
14
|
+
var API_DESCRIBE_OPTIONS = "API_DESCRIBE_OPTIONS";
|
|
15
|
+
|
|
16
|
+
// src/controller-describe.decorator.ts
|
|
17
|
+
import { applyDecorators, Delete, Get, Patch, Post, Put, SetMetadata } from "@nestjs/common";
|
|
18
|
+
import { ApiBearerAuth, ApiOperation, ApiResponse } from "@nestjs/swagger";
|
|
19
|
+
var METHOD_DECORATORS = {
|
|
20
|
+
GET: Get,
|
|
21
|
+
POST: Post,
|
|
22
|
+
PUT: Put,
|
|
23
|
+
PATCH: Patch,
|
|
24
|
+
DELETE: Delete
|
|
25
|
+
};
|
|
26
|
+
var ERROR_DESCRIPTIONS = {
|
|
27
|
+
400: "Bad Request",
|
|
28
|
+
401: "Unauthorized",
|
|
29
|
+
403: "Forbidden",
|
|
30
|
+
404: "Not Found",
|
|
31
|
+
409: "Conflict",
|
|
32
|
+
422: "Unprocessable Entity",
|
|
33
|
+
500: "Internal Server Error"
|
|
34
|
+
};
|
|
35
|
+
function getErrorDescription(code) {
|
|
36
|
+
return ERROR_DESCRIPTIONS[code] ?? `Error ${String(code)}`;
|
|
37
|
+
}
|
|
38
|
+
function getErrorDecorators(errors) {
|
|
39
|
+
return errors.map(
|
|
40
|
+
(code) => ApiResponse({
|
|
41
|
+
status: code,
|
|
42
|
+
description: getErrorDescription(code)
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
function getSuccessStatus(method) {
|
|
47
|
+
return method === "POST" ? 201 : 200;
|
|
48
|
+
}
|
|
49
|
+
function addPublicDecorators(decorators, isPublic, allErrors) {
|
|
50
|
+
if (isPublic) {
|
|
51
|
+
allErrors.delete(401);
|
|
52
|
+
allErrors.delete(403);
|
|
53
|
+
decorators.push(SetMetadata(IS_PUBLIC_KEY, true));
|
|
54
|
+
} else {
|
|
55
|
+
decorators.push(ApiBearerAuth());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function buildDecorators(options) {
|
|
59
|
+
const {
|
|
60
|
+
method,
|
|
61
|
+
path = "",
|
|
62
|
+
summary,
|
|
63
|
+
description,
|
|
64
|
+
responseType,
|
|
65
|
+
isPublic = false,
|
|
66
|
+
errors = []
|
|
67
|
+
} = options;
|
|
68
|
+
const decorators = [];
|
|
69
|
+
decorators.push(METHOD_DECORATORS[method](path));
|
|
70
|
+
const operationOptions = description !== void 0 ? { summary, description } : { summary };
|
|
71
|
+
decorators.push(ApiOperation(operationOptions));
|
|
72
|
+
decorators.push(ApiResponse({ status: getSuccessStatus(method), type: responseType }));
|
|
73
|
+
const allErrors = /* @__PURE__ */ new Set([401, 403, ...errors]);
|
|
74
|
+
addPublicDecorators(decorators, isPublic, allErrors);
|
|
75
|
+
decorators.push(...getErrorDecorators([...allErrors]));
|
|
76
|
+
return decorators;
|
|
77
|
+
}
|
|
78
|
+
function ControllerDescribe(options) {
|
|
79
|
+
return applyDecorators(...buildDecorators(options));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/dto-describe.decorator.ts
|
|
83
|
+
import { applyDecorators as applyDecorators2 } from "@nestjs/common";
|
|
84
|
+
import { ApiProperty } from "@nestjs/swagger";
|
|
85
|
+
import { Transform, Type as ClassType } from "class-transformer";
|
|
86
|
+
import {
|
|
87
|
+
IsArray,
|
|
88
|
+
IsBoolean,
|
|
89
|
+
IsDate,
|
|
90
|
+
IsDateString,
|
|
91
|
+
IsEmail,
|
|
92
|
+
IsEnum,
|
|
93
|
+
IsInt,
|
|
94
|
+
IsNotEmpty,
|
|
95
|
+
IsNumber,
|
|
96
|
+
IsObject,
|
|
97
|
+
IsOptional,
|
|
98
|
+
IsString,
|
|
99
|
+
IsUrl,
|
|
100
|
+
IsUUID,
|
|
101
|
+
Length,
|
|
102
|
+
Matches,
|
|
103
|
+
Max,
|
|
104
|
+
Min,
|
|
105
|
+
ValidateNested
|
|
106
|
+
} from "class-validator";
|
|
107
|
+
function buildApiPropertyOptions(options) {
|
|
108
|
+
const apiOptions = {
|
|
109
|
+
description: options.description,
|
|
110
|
+
required: options.required ?? true
|
|
111
|
+
};
|
|
112
|
+
if (options.example !== void 0) {
|
|
113
|
+
apiOptions.example = options.example;
|
|
114
|
+
}
|
|
115
|
+
if (options.isArray === true) {
|
|
116
|
+
apiOptions.isArray = true;
|
|
117
|
+
}
|
|
118
|
+
if (options.type === "enum" && options.enum !== void 0) {
|
|
119
|
+
apiOptions.enum = options.enum;
|
|
120
|
+
}
|
|
121
|
+
if (options.nested !== void 0) {
|
|
122
|
+
apiOptions.type = options.nested;
|
|
123
|
+
}
|
|
124
|
+
return apiOptions;
|
|
125
|
+
}
|
|
126
|
+
function getStringDecorators(options) {
|
|
127
|
+
const decorators = [IsString()];
|
|
128
|
+
if (options.minLength !== void 0 || options.maxLength !== void 0) {
|
|
129
|
+
decorators.push(Length(options.minLength ?? 0, options.maxLength));
|
|
130
|
+
}
|
|
131
|
+
decorators.push(
|
|
132
|
+
Transform(
|
|
133
|
+
({ value }) => typeof value === "string" ? value.trim() : value
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
return decorators;
|
|
137
|
+
}
|
|
138
|
+
function getNumberDecorators(options) {
|
|
139
|
+
const decorators = [IsNumber()];
|
|
140
|
+
if (options.min !== void 0) {
|
|
141
|
+
decorators.push(Min(options.min));
|
|
142
|
+
}
|
|
143
|
+
if (options.max !== void 0) {
|
|
144
|
+
decorators.push(Max(options.max));
|
|
145
|
+
}
|
|
146
|
+
return decorators;
|
|
147
|
+
}
|
|
148
|
+
function getIntDecorators(options) {
|
|
149
|
+
const decorators = [IsInt()];
|
|
150
|
+
if (options.min !== void 0) {
|
|
151
|
+
decorators.push(Min(options.min));
|
|
152
|
+
}
|
|
153
|
+
if (options.max !== void 0) {
|
|
154
|
+
decorators.push(Max(options.max));
|
|
155
|
+
}
|
|
156
|
+
return decorators;
|
|
157
|
+
}
|
|
158
|
+
function getEmailDecorators() {
|
|
159
|
+
return [
|
|
160
|
+
IsEmail(),
|
|
161
|
+
Transform(
|
|
162
|
+
({ value }) => typeof value === "string" ? value.toLowerCase().trim() : value
|
|
163
|
+
)
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
function getNestedDecorators(nested) {
|
|
167
|
+
return [ValidateNested(), ClassType(() => nested)];
|
|
168
|
+
}
|
|
169
|
+
function getArrayDecorators(nested) {
|
|
170
|
+
const decorators = [IsArray()];
|
|
171
|
+
if (nested !== void 0) {
|
|
172
|
+
decorators.push(ValidateNested({ each: true }), ClassType(() => nested));
|
|
173
|
+
}
|
|
174
|
+
return decorators;
|
|
175
|
+
}
|
|
176
|
+
var TYPE_DECORATOR_MAP = {
|
|
177
|
+
string: getStringDecorators,
|
|
178
|
+
number: getNumberDecorators,
|
|
179
|
+
int: getIntDecorators,
|
|
180
|
+
boolean: () => [IsBoolean()],
|
|
181
|
+
uuid: () => [IsUUID()],
|
|
182
|
+
email: getEmailDecorators,
|
|
183
|
+
date: () => [IsDate(), ClassType(() => Date)],
|
|
184
|
+
dateString: () => [IsDateString()],
|
|
185
|
+
url: () => [IsUrl()],
|
|
186
|
+
enum: (opts) => opts.enum !== void 0 ? [IsEnum(opts.enum)] : [],
|
|
187
|
+
nested: (opts) => opts.nested !== void 0 ? getNestedDecorators(opts.nested) : [],
|
|
188
|
+
array: (opts) => getArrayDecorators(opts.nested),
|
|
189
|
+
object: () => [IsObject()]
|
|
190
|
+
};
|
|
191
|
+
function getTypeDecorators(options) {
|
|
192
|
+
const decoratorFn = TYPE_DECORATOR_MAP[options.type];
|
|
193
|
+
return decoratorFn(options);
|
|
194
|
+
}
|
|
195
|
+
function DtoDescribe(options) {
|
|
196
|
+
const decorators = [];
|
|
197
|
+
decorators.push(ApiProperty(buildApiPropertyOptions(options)));
|
|
198
|
+
const isRequired = options.required ?? true;
|
|
199
|
+
decorators.push(isRequired ? IsNotEmpty() : IsOptional());
|
|
200
|
+
decorators.push(...getTypeDecorators(options));
|
|
201
|
+
if (options.pattern !== void 0) {
|
|
202
|
+
decorators.push(Matches(options.pattern));
|
|
203
|
+
}
|
|
204
|
+
return applyDecorators2(...decorators);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/api-describe.module.ts
|
|
208
|
+
import { Module } from "@nestjs/common";
|
|
209
|
+
var ApiDescribeModule = class {
|
|
210
|
+
static forRoot(options) {
|
|
211
|
+
return {
|
|
212
|
+
module: ApiDescribeModule,
|
|
213
|
+
global: true,
|
|
214
|
+
providers: [{ provide: API_DESCRIBE_OPTIONS, useValue: options ?? {} }],
|
|
215
|
+
exports: [API_DESCRIBE_OPTIONS]
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
ApiDescribeModule = __decorateClass([
|
|
220
|
+
Module({})
|
|
221
|
+
], ApiDescribeModule);
|
|
222
|
+
export {
|
|
223
|
+
API_DESCRIBE_OPTIONS,
|
|
224
|
+
ApiDescribeModule,
|
|
225
|
+
ControllerDescribe,
|
|
226
|
+
DtoDescribe,
|
|
227
|
+
IS_PUBLIC_KEY
|
|
228
|
+
};
|
|
229
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/controller-describe.decorator.ts","../src/dto-describe.decorator.ts","../src/api-describe.module.ts"],"sourcesContent":["import type { Type } from '@nestjs/common';\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type DtoType =\n | 'string'\n | 'number'\n | 'int'\n | 'boolean'\n | 'uuid'\n | 'email'\n | 'date'\n | 'dateString'\n | 'url'\n | 'enum'\n | 'nested'\n | 'array'\n | 'object';\n\nexport interface ControllerDescribeOptions {\n method: HttpMethod;\n path?: string;\n summary: string;\n description?: string;\n responseType: Type<unknown>;\n isPublic?: boolean;\n errors?: number[];\n}\n\nexport interface DtoDescribeOptions {\n description: string;\n example?: unknown;\n required?: boolean;\n type: DtoType;\n enum?: object;\n isArray?: boolean;\n nested?: Type<unknown>;\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n pattern?: RegExp;\n}\n\nexport interface ApiDescribeModuleOptions {\n defaultErrors?: number[];\n customErrorDescriptions?: Record<number, string>;\n}\n\nexport const IS_PUBLIC_KEY = 'isPublic';\nexport const API_DESCRIBE_OPTIONS = 'API_DESCRIBE_OPTIONS';\n","import { applyDecorators, Delete, Get, Patch, Post, Put, SetMetadata } from '@nestjs/common';\nimport { ApiBearerAuth, ApiOperation, ApiResponse } from '@nestjs/swagger';\nimport type { ControllerDescribeOptions, HttpMethod } from './types';\nimport { IS_PUBLIC_KEY } from './types';\n\nconst METHOD_DECORATORS: Record<HttpMethod, (path?: string) => MethodDecorator> = {\n GET: Get,\n POST: Post,\n PUT: Put,\n PATCH: Patch,\n DELETE: Delete,\n};\n\nconst ERROR_DESCRIPTIONS: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error',\n};\n\nfunction getErrorDescription(code: number): string {\n return ERROR_DESCRIPTIONS[code] ?? `Error ${String(code)}`;\n}\n\nfunction getErrorDecorators(errors: number[]): MethodDecorator[] {\n return errors.map((code) =>\n ApiResponse({\n status: code,\n description: getErrorDescription(code),\n }),\n );\n}\n\nfunction getSuccessStatus(method: HttpMethod): number {\n return method === 'POST' ? 201 : 200;\n}\n\nfunction addPublicDecorators(\n decorators: (MethodDecorator | ClassDecorator)[],\n isPublic: boolean,\n allErrors: Set<number>,\n): void {\n if (isPublic) {\n allErrors.delete(401);\n allErrors.delete(403);\n decorators.push(SetMetadata(IS_PUBLIC_KEY, true));\n } else {\n decorators.push(ApiBearerAuth());\n }\n}\n\nfunction buildDecorators(options: ControllerDescribeOptions): (MethodDecorator | ClassDecorator)[] {\n const {\n method,\n path = '',\n summary,\n description,\n responseType,\n isPublic = false,\n errors = [],\n } = options;\n\n const decorators: (MethodDecorator | ClassDecorator)[] = [];\n decorators.push(METHOD_DECORATORS[method](path));\n\n const operationOptions = description !== undefined ? { summary, description } : { summary };\n decorators.push(ApiOperation(operationOptions));\n decorators.push(ApiResponse({ status: getSuccessStatus(method), type: responseType }));\n\n const allErrors = new Set([401, 403, ...errors]);\n addPublicDecorators(decorators, isPublic, allErrors);\n decorators.push(...getErrorDecorators([...allErrors]));\n\n return decorators;\n}\n\nexport function ControllerDescribe(options: ControllerDescribeOptions): MethodDecorator {\n return applyDecorators(...buildDecorators(options));\n}\n","import type { Type } from '@nestjs/common';\nimport { applyDecorators } from '@nestjs/common';\nimport type { ApiPropertyOptions } from '@nestjs/swagger';\nimport { ApiProperty } from '@nestjs/swagger';\nimport { Transform, Type as ClassType } from 'class-transformer';\nimport {\n IsArray,\n IsBoolean,\n IsDate,\n IsDateString,\n IsEmail,\n IsEnum,\n IsInt,\n IsNotEmpty,\n IsNumber,\n IsObject,\n IsOptional,\n IsString,\n IsUrl,\n IsUUID,\n Length,\n Matches,\n Max,\n Min,\n ValidateNested,\n} from 'class-validator';\nimport type { DtoDescribeOptions, DtoType } from './types';\n\nfunction buildApiPropertyOptions(options: DtoDescribeOptions): ApiPropertyOptions {\n const apiOptions: ApiPropertyOptions = {\n description: options.description,\n required: options.required ?? true,\n };\n\n if (options.example !== undefined) {\n apiOptions.example = options.example;\n }\n\n if (options.isArray === true) {\n apiOptions.isArray = true;\n }\n\n if (options.type === 'enum' && options.enum !== undefined) {\n apiOptions.enum = options.enum;\n }\n\n if (options.nested !== undefined) {\n apiOptions.type = options.nested;\n }\n\n return apiOptions;\n}\n\nfunction getStringDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsString()];\n\n if (options.minLength !== undefined || options.maxLength !== undefined) {\n decorators.push(Length(options.minLength ?? 0, options.maxLength));\n }\n\n decorators.push(\n Transform(({ value }: { value: unknown }) =>\n typeof value === 'string' ? value.trim() : value,\n ),\n );\n\n return decorators;\n}\n\nfunction getNumberDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsNumber()];\n\n if (options.min !== undefined) {\n decorators.push(Min(options.min));\n }\n\n if (options.max !== undefined) {\n decorators.push(Max(options.max));\n }\n\n return decorators;\n}\n\nfunction getIntDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsInt()];\n\n if (options.min !== undefined) {\n decorators.push(Min(options.min));\n }\n\n if (options.max !== undefined) {\n decorators.push(Max(options.max));\n }\n\n return decorators;\n}\n\nfunction getEmailDecorators(): PropertyDecorator[] {\n return [\n IsEmail(),\n Transform(({ value }: { value: unknown }) =>\n typeof value === 'string' ? value.toLowerCase().trim() : value,\n ),\n ];\n}\n\nfunction getNestedDecorators(nested: Type<unknown>): PropertyDecorator[] {\n return [ValidateNested(), ClassType(() => nested)];\n}\n\nfunction getArrayDecorators(nested?: Type<unknown>): PropertyDecorator[] {\n const decorators: PropertyDecorator[] = [IsArray()];\n\n if (nested !== undefined) {\n decorators.push(ValidateNested({ each: true }), ClassType(() => nested));\n }\n\n return decorators;\n}\n\nconst TYPE_DECORATOR_MAP: Record<DtoType, (options: DtoDescribeOptions) => PropertyDecorator[]> = {\n string: getStringDecorators,\n number: getNumberDecorators,\n int: getIntDecorators,\n boolean: () => [IsBoolean()],\n uuid: () => [IsUUID()],\n email: getEmailDecorators,\n date: () => [IsDate(), ClassType(() => Date)],\n dateString: () => [IsDateString()],\n url: () => [IsUrl()],\n enum: (opts) => (opts.enum !== undefined ? [IsEnum(opts.enum)] : []),\n nested: (opts) => (opts.nested !== undefined ? getNestedDecorators(opts.nested) : []),\n array: (opts) => getArrayDecorators(opts.nested),\n object: () => [IsObject()],\n};\n\nfunction getTypeDecorators(options: DtoDescribeOptions): PropertyDecorator[] {\n const decoratorFn = TYPE_DECORATOR_MAP[options.type];\n return decoratorFn(options);\n}\n\nexport function DtoDescribe(options: DtoDescribeOptions): PropertyDecorator {\n const decorators: PropertyDecorator[] = [];\n\n decorators.push(ApiProperty(buildApiPropertyOptions(options)));\n\n const isRequired = options.required ?? true;\n decorators.push(isRequired ? IsNotEmpty() : IsOptional());\n decorators.push(...getTypeDecorators(options));\n\n if (options.pattern !== undefined) {\n decorators.push(Matches(options.pattern));\n }\n\n return applyDecorators(...decorators);\n}\n","import { DynamicModule, Module } from '@nestjs/common';\nimport type { ApiDescribeModuleOptions } from './types';\nimport { API_DESCRIBE_OPTIONS } from './types';\n\n@Module({})\nexport class ApiDescribeModule {\n static forRoot(options?: ApiDescribeModuleOptions): DynamicModule {\n return {\n module: ApiDescribeModule,\n global: true,\n providers: [{ provide: API_DESCRIBE_OPTIONS, useValue: options ?? {} }],\n exports: [API_DESCRIBE_OPTIONS],\n };\n }\n}\n"],"mappings":"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","names":["applyDecorators"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nestjs-api-describe",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Composite decorators for standardizing OpenAPI documentation and input validation in NestJS APIs",
|
|
5
|
+
"author": "Gabriele Partiti",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"keywords": [
|
|
26
|
+
"nestjs",
|
|
27
|
+
"decorator",
|
|
28
|
+
"openapi",
|
|
29
|
+
"swagger",
|
|
30
|
+
"validation",
|
|
31
|
+
"class-validator",
|
|
32
|
+
"dto"
|
|
33
|
+
],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/gabry-ts/nestjs-api-describe"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"lint": "eslint 'src/**/*.ts'",
|
|
41
|
+
"format": "prettier --write 'src/**/*.ts'",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"test": "jest",
|
|
44
|
+
"test:cov": "jest --coverage",
|
|
45
|
+
"prepare": "husky"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"@nestjs/common": ">=10",
|
|
49
|
+
"@nestjs/swagger": ">=7",
|
|
50
|
+
"class-transformer": ">=0.5",
|
|
51
|
+
"class-validator": ">=0.14",
|
|
52
|
+
"reflect-metadata": ">=0.1"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@nestjs/common": "^10.0.0",
|
|
56
|
+
"@nestjs/core": "^10.0.0",
|
|
57
|
+
"@nestjs/swagger": "^7.0.0",
|
|
58
|
+
"@nestjs/testing": "^10.0.0",
|
|
59
|
+
"@types/jest": "^29.5.0",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
61
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
62
|
+
"class-transformer": "^0.5.1",
|
|
63
|
+
"class-validator": "^0.14.0",
|
|
64
|
+
"eslint": "^8.57.0",
|
|
65
|
+
"husky": "^9.0.0",
|
|
66
|
+
"jest": "^29.7.0",
|
|
67
|
+
"lint-staged": "^15.0.0",
|
|
68
|
+
"prettier": "^3.0.0",
|
|
69
|
+
"reflect-metadata": "^0.2.0",
|
|
70
|
+
"rxjs": "^7.8.0",
|
|
71
|
+
"ts-jest": "^29.1.0",
|
|
72
|
+
"tsup": "^8.0.0",
|
|
73
|
+
"typescript": "^5.4.0"
|
|
74
|
+
},
|
|
75
|
+
"lint-staged": {
|
|
76
|
+
"*.ts": [
|
|
77
|
+
"eslint --fix",
|
|
78
|
+
"prettier --write"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|