@zenofolio/hyper-decor 1.0.69 → 1.0.72
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 +136 -254
- package/dist/__internals/constants.d.ts +2 -2
- package/dist/__internals/constants.js +2 -2
- package/dist/__internals/creators/request.creator.d.ts +1 -10
- package/dist/__internals/creators/request.creator.js +141 -21
- package/dist/__internals/decorator-base.js +1 -1
- package/dist/__internals/helpers/prepare.helper.js +71 -51
- package/dist/decorators/File.d.ts +4 -32
- package/dist/decorators/File.js +62 -75
- package/dist/decorators/Http.d.ts +9 -42
- package/dist/decorators/Http.js +24 -72
- package/dist/decorators/Output.d.ts +9 -0
- package/dist/decorators/Output.js +18 -0
- package/dist/decorators/index.d.ts +1 -1
- package/dist/decorators/index.js +1 -1
- package/dist/decorators/types.d.ts +2 -0
- package/dist/exeptions/HyperException.d.ts +2 -1
- package/dist/exeptions/HyperException.js +2 -1
- package/dist/exeptions/HyperFileException.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/lib/openapi/collectors/method.collector.js +25 -12
- package/dist/lib/openapi/collectors/param.collector.d.ts +2 -3
- package/dist/lib/openapi/collectors/param.collector.js +49 -12
- package/dist/lib/openapi/decorators/api-parameter.decorator.d.ts +2 -2
- package/dist/lib/openapi/decorators/api-response.decorator.d.ts +2 -2
- package/dist/lib/openapi/decorators/api-tag.decorator.d.ts +1 -1
- package/dist/lib/openapi/decorators/api-tag.decorator.js +3 -0
- package/dist/lib/openapi/helpers/parameter.helper.d.ts +2 -2
- package/dist/lib/openapi/helpers/response.helper.d.ts +2 -2
- package/dist/lib/openapi/types.d.ts +8 -8
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,254 +1,136 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
###
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
@
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
res.send("hello");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
`@HyperModule` - define module with controllers
|
|
144
|
-
```typescript
|
|
145
|
-
@HyperModule({
|
|
146
|
-
path: "users",
|
|
147
|
-
controllers: [TestController]
|
|
148
|
-
})
|
|
149
|
-
class UserModule {}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
`@HyperApp` - define application
|
|
155
|
-
```typescript
|
|
156
|
-
@HyperApp({
|
|
157
|
-
name: "Hyper Express Decorators",
|
|
158
|
-
version: "1.0.0",
|
|
159
|
-
description: "Decorators to make development easier",
|
|
160
|
-
modules: [UserV1Module],
|
|
161
|
-
prefix: "/api",
|
|
162
|
-
})
|
|
163
|
-
export class Application implements IHyperApplication {
|
|
164
|
-
onPrepare() {
|
|
165
|
-
console.log("This method will be called after the app is prepared");
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Run Application
|
|
171
|
-
```typescript
|
|
172
|
-
const app = await createApplication(Application)
|
|
173
|
-
await app.listen(3000);
|
|
174
|
-
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
As a result, we get:
|
|
178
|
-
|
|
179
|
-
- `/api/users/v1/list`
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
# Examples
|
|
183
|
-
|
|
184
|
-
- [Add Roles and scopes](./examples/add-roles-and-scopes.ts)
|
|
185
|
-
- [Middleware](./examples/middleware.ts)
|
|
186
|
-
- [File](./examples//upload-file.ts)
|
|
187
|
-
|
|
188
|
-
## Services & Dependency Injection
|
|
189
|
-
You can use `@HyperService()` with `tsyringe` to inject dependencies into controllers or other services.
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
import { injectable } from "tsyringe";
|
|
193
|
-
|
|
194
|
-
@injectable()
|
|
195
|
-
@HyperService()
|
|
196
|
-
class UserService {
|
|
197
|
-
getUsers() { return ["User1", "User2"]; }
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
@HyperController("/users")
|
|
201
|
-
class UserController {
|
|
202
|
-
constructor(private userService: UserService) {}
|
|
203
|
-
|
|
204
|
-
@Get("/")
|
|
205
|
-
getUsers(@Res() res: Response) {
|
|
206
|
-
res.json(this.userService.getUsers());
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Agnostic Body Validation & Transformation (`@Transform`)
|
|
212
|
-
You can use `@Transform` to validate and transform incoming requests agnostic of the validation library (like Zod) while seamlessly syncing with OpenAPI definitions.
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
const ZodTransformer = {
|
|
216
|
-
transform: ({ data, schema }) => {
|
|
217
|
-
if (schema && schema._type === "zod") {
|
|
218
|
-
// Validate and return the parsed data
|
|
219
|
-
return { ...data, parsed: true };
|
|
220
|
-
}
|
|
221
|
-
return data;
|
|
222
|
-
},
|
|
223
|
-
getOpenApiSchema: (schema) => {
|
|
224
|
-
if (schema._type === "zod") {
|
|
225
|
-
return { type: "object", properties: { /* derived from schema */ } };
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const app = await createApplication(Application);
|
|
231
|
-
app.useTransform(ZodTransformer); // Register your custom transformer globally
|
|
232
|
-
|
|
233
|
-
@HyperController("/users")
|
|
234
|
-
class UserController {
|
|
235
|
-
@Post("/")
|
|
236
|
-
@Transform({ _type: "zod" /* pass your schema */ })
|
|
237
|
-
createUser(@Body() data: any, @Res() res: Response) {
|
|
238
|
-
res.json(data); // `data` is automatically intercepted and transformed!
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## OpenAPI Generation
|
|
244
|
-
Generate a complete OpenAPI specification out-of-the-box leveraging your application tree and decorators footprint.
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import { getOpenAPI } from "@zenofolio/hyper-decor/lib/openapi";
|
|
248
|
-
|
|
249
|
-
const openApiDoc = getOpenAPI(Application);
|
|
250
|
-
console.log(openApiDoc.info.title, openApiDoc.paths);
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
# All for now
|
|
254
|
-
More documentation will be added here late.
|
|
1
|
+
# @zenofolio/hyper-decor (v1.0.71)
|
|
2
|
+
|
|
3
|
+
A high-performance, ultra-secure, and indestructible decorators library for [HyperExpress](https://github.com/kartikk221/hyper-express).
|
|
4
|
+
|
|
5
|
+
Built for speed and security, this library leverages a **Zero-Overhead** architecture using functional composition to ensure your API remains as fast as raw `uWS` while providing a modern development experience.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🚀 Why This Library?
|
|
10
|
+
|
|
11
|
+
- **Zero-Overhead Architecture**: Parameter resolution, DTO validation, and transformations are pre-composed at startup. No runtime branching in the hotpath.
|
|
12
|
+
- **Ultra-Secure File Handling**: Streaming validation of file sizes and types. Cuts the connection immediately if limits are breached. No memory exhaustion.
|
|
13
|
+
- **Polymorphic Decorators**: Flexible parameter decorators that adapt to your needs without sacrificing performance.
|
|
14
|
+
- **Automated OpenAPI**: Full OpenAPI 3.0 support with automatic DTO expansion and response documentation.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📦 Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @zenofolio/hyper-decor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🛠️ Usage
|
|
27
|
+
|
|
28
|
+
### Define Your App
|
|
29
|
+
```typescript
|
|
30
|
+
import { HyperApp, createApplication } from "@zenofolio/hyper-decor";
|
|
31
|
+
|
|
32
|
+
@HyperApp({
|
|
33
|
+
modules: [UserModule],
|
|
34
|
+
prefix: "/api"
|
|
35
|
+
})
|
|
36
|
+
class Application {}
|
|
37
|
+
|
|
38
|
+
const app = await createApplication(Application);
|
|
39
|
+
await app.listen(3000);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Polymorphic Parameter Decorators
|
|
43
|
+
Decorators like `@Body`, `@Query`, `@Param`, and `@Headers` are now smarter and faster.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
@HyperController("/users")
|
|
47
|
+
class UserController {
|
|
48
|
+
|
|
49
|
+
// 1. Direct DTO Validation (Auto-transformation + OpenAPI)
|
|
50
|
+
@Post("/")
|
|
51
|
+
async create(@Body(CreateUserDto) user: CreateUserDto) {
|
|
52
|
+
return user;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 2. Key-based extraction with Functional Transformer
|
|
56
|
+
@Get("/:id")
|
|
57
|
+
async findOne(@Param("id", v => parseInt(v)) id: number) {
|
|
58
|
+
return { id };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Nested extraction + DTO
|
|
62
|
+
@Post("/settings")
|
|
63
|
+
async updateSettings(@Body("settings", SettingsDto) data: SettingsDto) {
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 4. Raw Extractors
|
|
68
|
+
@Get("/")
|
|
69
|
+
async list(@Query() allQuery: any, @Req req: any) {
|
|
70
|
+
return allQuery;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🔒 Indestructible File Uploader (`@File`)
|
|
78
|
+
|
|
79
|
+
The `@File` decorator is designed to be ultra-secure. It processes files as **streams**, validating size and type binarily (magic numbers) *before* the file is fully buffered.
|
|
80
|
+
|
|
81
|
+
- **Streaming Validation**: Connection is terminated immediately if `maxFileSize` is exceeded.
|
|
82
|
+
- **Binary Verification**: Uses binary signatures to verify MIME types, ensuring security even if the extension is falsified.
|
|
83
|
+
- **Automatic Sanitization**: File names are sanitized against path traversal and null bytes.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
@Post("/upload")
|
|
87
|
+
async upload(
|
|
88
|
+
@File("avatar", {
|
|
89
|
+
maxFileSize: 5 * 1024 * 1024, // 5MB
|
|
90
|
+
allowedExtensions: ["png", "jpg"],
|
|
91
|
+
allowedMimeTypes: ["image/png", "image/jpeg"]
|
|
92
|
+
}) file: UploadedFile
|
|
93
|
+
) {
|
|
94
|
+
// file.buffer contains the safely validated data
|
|
95
|
+
return { filename: file.filename, size: file.size };
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## ⚡ Performance
|
|
102
|
+
|
|
103
|
+
Benchmarks show that `@zenofolio/hyper-decor` introduces less than **1.8%** overhead compared to raw, manual HyperExpress handlers.
|
|
104
|
+
|
|
105
|
+
| Scenario | Raw HyperExpress | @zenofolio/hyper-decor | Overhead |
|
|
106
|
+
| :--- | :--- | :--- | :--- |
|
|
107
|
+
| **Simple GET** | 27,150 req/s | 26,660 req/s | ~1.8% |
|
|
108
|
+
| **Deep Transformation**| 18,120 req/s | 18,335 req/s | **+1.1% Gain** |
|
|
109
|
+
|
|
110
|
+
> [!TIP]
|
|
111
|
+
> The "Transformed" scenario actually performs better than manual implementations thanks to our optimized functional resolver composition that V8 can inline aggressively.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🔍 OpenAPI & DTOs
|
|
116
|
+
|
|
117
|
+
Pass classes to your decorators, and they will be expanded into the OpenAPI schema automatically.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
class CreateUserDto {
|
|
121
|
+
/** @minimum 18 */
|
|
122
|
+
age: number;
|
|
123
|
+
name: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Post("/")
|
|
127
|
+
async create(@Body(CreateUserDto) data: CreateUserDto) { ... }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
You can integrate any validation engine (Zod, Class-Validator) by registering a `Transformer` in the `transformRegistry`.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🛡️ License
|
|
135
|
+
|
|
136
|
+
MIT
|
|
@@ -14,8 +14,8 @@ export declare const KEY_PARAMS_MIDDLEWARES = "hyper:type:middlewares";
|
|
|
14
14
|
export declare const KEY_PARAMS_SCOPE = "hyper:type:scope";
|
|
15
15
|
export declare const KEY_PARAMS_ROLE = "hyper:type:role";
|
|
16
16
|
export declare const KEY_PARAMS_PASS = "hyper:type:pass";
|
|
17
|
-
export declare const
|
|
18
|
-
export type KeyParams = typeof KEY_PARAMS_APP | typeof KEY_PARAMS_CONTROLLER | typeof KEY_PARAMS_MODULE | typeof KEY_PARAMS_ROUTE | typeof KEY_PARAMS_PARAM | typeof KEY_PARAMS_MIDDLEWARES | typeof KEY_PARAMS_SCOPE | typeof KEY_PARAMS_ROLE | typeof KEY_PARAMS_PASS | typeof
|
|
17
|
+
export declare const KEY_OUTPUT_SCHEMA = "hyper:output:schema";
|
|
18
|
+
export type KeyParams = typeof KEY_PARAMS_APP | typeof KEY_PARAMS_CONTROLLER | typeof KEY_PARAMS_MODULE | typeof KEY_PARAMS_ROUTE | typeof KEY_PARAMS_PARAM | typeof KEY_PARAMS_MIDDLEWARES | typeof KEY_PARAMS_SCOPE | typeof KEY_PARAMS_ROLE | typeof KEY_PARAMS_PASS | typeof KEY_OUTPUT_SCHEMA;
|
|
19
19
|
export declare const KEY_STATE_UPDATED = "hyper:state:updated";
|
|
20
20
|
export declare const KEY_STATE_CREATED = "hyper:state:created";
|
|
21
21
|
export declare const KEY_STATE_PREPARED = "hyper:state:prepared";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FULL_ACCESS = exports.METADATA_STATE_KEYS = exports.METADATA_PARAMS_KEYS = exports.METADATA_METHOD_KEYS = exports.METADATA_STORE_KEYS = exports.METADATA_KEYS = exports.METADATA_HYPER_TYPE = exports.DESIGN_TYPE = exports.DESIGN_RETURNTYPE = exports.DESIGN_PARAMTYPES = exports.KEY_STATE_BY_PASS = exports.KEY_STATE_PREPARED = exports.KEY_STATE_CREATED = exports.KEY_STATE_UPDATED = exports.
|
|
3
|
+
exports.FULL_ACCESS = exports.METADATA_STATE_KEYS = exports.METADATA_PARAMS_KEYS = exports.METADATA_METHOD_KEYS = exports.METADATA_STORE_KEYS = exports.METADATA_KEYS = exports.METADATA_HYPER_TYPE = exports.DESIGN_TYPE = exports.DESIGN_RETURNTYPE = exports.DESIGN_PARAMTYPES = exports.KEY_STATE_BY_PASS = exports.KEY_STATE_PREPARED = exports.KEY_STATE_CREATED = exports.KEY_STATE_UPDATED = exports.KEY_OUTPUT_SCHEMA = exports.KEY_PARAMS_PASS = exports.KEY_PARAMS_ROLE = exports.KEY_PARAMS_SCOPE = exports.KEY_PARAMS_MIDDLEWARES = exports.KEY_PARAMS_PARAM = exports.KEY_PARAMS_ROUTE = exports.KEY_PARAMS_MODULE = exports.KEY_PARAMS_CONTROLLER = exports.KEY_PARAMS_APP = exports.KEY_TYPE_GUARD = exports.KEY_TYPE_SERVICE = exports.KEY_TYPE_ROUTE = exports.KEY_TYPE_MODULE = exports.KEY_TYPE_CONTROLLER = exports.KEY_TYPE_APP = void 0;
|
|
4
4
|
//////////////////////////////
|
|
5
5
|
/// Types constants
|
|
6
6
|
//////////////////////////////
|
|
@@ -22,7 +22,7 @@ exports.KEY_PARAMS_MIDDLEWARES = "hyper:type:middlewares";
|
|
|
22
22
|
exports.KEY_PARAMS_SCOPE = "hyper:type:scope";
|
|
23
23
|
exports.KEY_PARAMS_ROLE = "hyper:type:role";
|
|
24
24
|
exports.KEY_PARAMS_PASS = "hyper:type:pass";
|
|
25
|
-
exports.
|
|
25
|
+
exports.KEY_OUTPUT_SCHEMA = "hyper:output:schema";
|
|
26
26
|
//////////////////////////////
|
|
27
27
|
/// State constants
|
|
28
28
|
//////////////////////////////
|
|
@@ -1,11 +1,2 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
|
|
3
|
-
import { ParameterResolver } from "../../decorators";
|
|
4
|
-
/**
|
|
5
|
-
* Creates a parameter decorator for handling request data.
|
|
6
|
-
*
|
|
7
|
-
* @param {keyof Request | ByPassKeys} key - The key to extract from the request.
|
|
8
|
-
* @param {IParamsResolver} resolver - Resolver function to handle the parameter.
|
|
9
|
-
* @returns {ParameterDecorator} - The parameter decorator function.
|
|
10
|
-
*/
|
|
11
|
-
export default function createParamDecorator(key: keyof Request | "req" | "res", decoratorName: string, resolver: ParameterResolver): ParameterDecorator;
|
|
2
|
+
export default function createParamDecorator(sourceKey: any, decoratorName: string, keyOrSchema?: string | any, schemaOrTransform?: any, isWholeSource?: boolean): ParameterDecorator;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
@@ -7,47 +16,158 @@ exports.default = createParamDecorator;
|
|
|
7
16
|
require("reflect-metadata");
|
|
8
17
|
const constants_1 = require("../constants");
|
|
9
18
|
const decorator_base_1 = require("../decorator-base");
|
|
10
|
-
const who_helper_1 = __importDefault(require("../helpers/who.helper"));
|
|
11
19
|
const WrongPlaceException_1 = __importDefault(require("../../exeptions/WrongPlaceException"));
|
|
12
20
|
const function_util_1 = require("../utils/function.util");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
function createParamDecorator(key, decoratorName, resolver) {
|
|
21
|
-
const _key = key;
|
|
21
|
+
const object_util_1 = require("../utils/object.util");
|
|
22
|
+
const transform_registry_1 = require("../transform/transform.registry");
|
|
23
|
+
const identitySelector = (value) => value;
|
|
24
|
+
const identityTransformer = (value) => value;
|
|
25
|
+
function createParamDecorator(sourceKey, decoratorName, keyOrSchema, schemaOrTransform, isWholeSource = false) {
|
|
26
|
+
const _sourceKey = sourceKey;
|
|
22
27
|
return (0, decorator_base_1.DecoratorHelper)({
|
|
23
28
|
type: constants_1.KEY_TYPE_CONTROLLER,
|
|
24
29
|
key: constants_1.KEY_PARAMS_PARAM,
|
|
25
30
|
options: (options, Target, propertyKey, parameterIndex) => {
|
|
26
|
-
const
|
|
27
|
-
if (!isProperty)
|
|
28
|
-
throw new WrongPlaceException_1.default(decoratorName, "parameter", `${Target.constructor.name}.${propertyKey}`, Target);
|
|
31
|
+
const isProperty = typeof parameterIndex === "number";
|
|
32
|
+
if (!isProperty) {
|
|
33
|
+
throw new WrongPlaceException_1.default(decoratorName, "parameter", `${Target.constructor.name}.${String(propertyKey)}`, Target);
|
|
34
|
+
}
|
|
29
35
|
const saved = options !== null && options !== void 0 ? options : { params: {} };
|
|
30
|
-
const
|
|
36
|
+
const method = Target[propertyKey];
|
|
37
|
+
const names = (0, function_util_1.extractArgsNames)(method);
|
|
31
38
|
const types = Reflect.getMetadata(constants_1.DESIGN_PARAMTYPES, Target, propertyKey);
|
|
32
39
|
const name = names === null || names === void 0 ? void 0 : names[parameterIndex];
|
|
33
40
|
const type = types === null || types === void 0 ? void 0 : types[parameterIndex];
|
|
41
|
+
let key = undefined;
|
|
42
|
+
let inputSchemaOrFn = undefined;
|
|
43
|
+
let wholeSource = isWholeSource;
|
|
44
|
+
if (typeof keyOrSchema === "string") {
|
|
45
|
+
key = keyOrSchema;
|
|
46
|
+
inputSchemaOrFn = schemaOrTransform;
|
|
47
|
+
}
|
|
48
|
+
else if (keyOrSchema !== undefined) {
|
|
49
|
+
inputSchemaOrFn = keyOrSchema;
|
|
50
|
+
wholeSource = true;
|
|
51
|
+
}
|
|
52
|
+
const hasKey = key !== undefined && key !== "";
|
|
53
|
+
const hasTransform = inputSchemaOrFn !== undefined;
|
|
54
|
+
const extractor = createExtractor(_sourceKey);
|
|
55
|
+
const selector = createSelector(key);
|
|
56
|
+
const transformer = createTransformer(inputSchemaOrFn, _sourceKey, wholeSource);
|
|
57
|
+
const resolver = composeResolver(extractor, selector, transformer, hasKey, hasTransform);
|
|
34
58
|
if (name && saved) {
|
|
35
|
-
|
|
36
|
-
|
|
59
|
+
let methodParams = saved.params[propertyKey];
|
|
60
|
+
if (!methodParams) {
|
|
61
|
+
methodParams = saved.params[propertyKey] = [];
|
|
37
62
|
}
|
|
38
|
-
|
|
63
|
+
methodParams.push({
|
|
39
64
|
name,
|
|
40
65
|
type,
|
|
41
66
|
index: parameterIndex,
|
|
42
|
-
key:
|
|
67
|
+
key: _sourceKey,
|
|
43
68
|
method: propertyKey.toString(),
|
|
44
69
|
resolver,
|
|
70
|
+
schema: inputSchemaOrFn,
|
|
71
|
+
isWholeSource: wholeSource,
|
|
45
72
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
if (methodParams.length > 1) {
|
|
74
|
+
methodParams.sort((a, b) => a.index - b.index);
|
|
75
|
+
}
|
|
76
|
+
Reflect.defineMetadata(constants_1.KEY_PARAMS_PARAM, saved, method);
|
|
49
77
|
}
|
|
50
78
|
return saved;
|
|
51
79
|
},
|
|
52
80
|
});
|
|
53
81
|
}
|
|
82
|
+
//////////////////////////
|
|
83
|
+
/// Functions
|
|
84
|
+
/////////////////////////
|
|
85
|
+
function createBodyExtractor() {
|
|
86
|
+
return (req) => __awaiter(this, void 0, void 0, function* () {
|
|
87
|
+
const request = req;
|
|
88
|
+
const cachedBody = request.body;
|
|
89
|
+
if (cachedBody !== undefined) {
|
|
90
|
+
return cachedBody;
|
|
91
|
+
}
|
|
92
|
+
const body = yield req.json();
|
|
93
|
+
request.body = body;
|
|
94
|
+
return body;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function createReqExtractor() {
|
|
98
|
+
return (req) => __awaiter(this, void 0, void 0, function* () { return req; });
|
|
99
|
+
}
|
|
100
|
+
function createResExtractor() {
|
|
101
|
+
return (_req, res) => __awaiter(this, void 0, void 0, function* () { return res; });
|
|
102
|
+
}
|
|
103
|
+
function createGenericExtractor(sourceKey) {
|
|
104
|
+
return (req) => __awaiter(this, void 0, void 0, function* () { return req[sourceKey]; });
|
|
105
|
+
}
|
|
106
|
+
function createExtractor(sourceKey) {
|
|
107
|
+
switch (sourceKey) {
|
|
108
|
+
case "body":
|
|
109
|
+
return createBodyExtractor();
|
|
110
|
+
case "req":
|
|
111
|
+
return createReqExtractor();
|
|
112
|
+
case "res":
|
|
113
|
+
return createResExtractor();
|
|
114
|
+
default:
|
|
115
|
+
return createGenericExtractor(sourceKey);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function createSelector(key) {
|
|
119
|
+
if (key === undefined || key === "") {
|
|
120
|
+
return identitySelector;
|
|
121
|
+
}
|
|
122
|
+
return (value) => (0, object_util_1.$get)(value, key, value);
|
|
123
|
+
}
|
|
124
|
+
function createDirectTransformer(fn) {
|
|
125
|
+
return (value) => fn(value);
|
|
126
|
+
}
|
|
127
|
+
function createRegistryTransformer(schema, sourceKey, isWholeSource) {
|
|
128
|
+
const options = { isWholeSource };
|
|
129
|
+
const from = sourceKey;
|
|
130
|
+
return (value, req, res) => transform_registry_1.transformRegistry.resolve({
|
|
131
|
+
data: value,
|
|
132
|
+
schema,
|
|
133
|
+
options,
|
|
134
|
+
req,
|
|
135
|
+
res,
|
|
136
|
+
from,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function createTransformer(inputSchemaOrFn, sourceKey, isWholeSource) {
|
|
140
|
+
if (inputSchemaOrFn === undefined) {
|
|
141
|
+
return identityTransformer;
|
|
142
|
+
}
|
|
143
|
+
if (typeof inputSchemaOrFn === "function" &&
|
|
144
|
+
inputSchemaOrFn.prototype === undefined) {
|
|
145
|
+
return createDirectTransformer(inputSchemaOrFn);
|
|
146
|
+
}
|
|
147
|
+
if (typeof inputSchemaOrFn === "function" ||
|
|
148
|
+
typeof inputSchemaOrFn === "string") {
|
|
149
|
+
return createRegistryTransformer(inputSchemaOrFn, sourceKey, isWholeSource);
|
|
150
|
+
}
|
|
151
|
+
return identityTransformer;
|
|
152
|
+
}
|
|
153
|
+
function composeResolver(extractor, selector, transformer, hasKey, hasTransform) {
|
|
154
|
+
if (!hasKey && !hasTransform) {
|
|
155
|
+
return extractor;
|
|
156
|
+
}
|
|
157
|
+
if (hasKey && !hasTransform) {
|
|
158
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
const value = yield extractor(req, res);
|
|
160
|
+
return selector(value);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (!hasKey && hasTransform) {
|
|
164
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
const value = yield extractor(req, res);
|
|
166
|
+
return transformer(value, req, res);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
const value = yield extractor(req, res);
|
|
171
|
+
return transformer(selector(value), req, res);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
@@ -61,7 +61,7 @@ function DecoratorHelper({ key, type, options, targetResolver, onDefineData }, .
|
|
|
61
61
|
const METADATA_CACHE = new WeakMap();
|
|
62
62
|
const defineDecorData = (key, options, target, property, descriptor) => {
|
|
63
63
|
let value = options;
|
|
64
|
-
if (typeof options === "function") {
|
|
64
|
+
if (typeof options === "function" && !(options.prototype && options.prototype.constructor === options)) {
|
|
65
65
|
const old = (0, exports.getDecorData)(key, target, property);
|
|
66
66
|
value = Object.assign(Object.assign({}, old), options(old, target, property, descriptor));
|
|
67
67
|
}
|