@nestia/core 14.0.0 → 14.0.2
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/MIGRATION.md +20 -0
- package/README.md +1 -1
- package/lib/adaptors/McpAdaptor.d.ts +29 -3
- package/lib/adaptors/McpAdaptor.js +150 -24
- package/lib/adaptors/McpAdaptor.js.map +1 -1
- package/lib/adaptors/WebSocketAdaptor.d.ts +37 -0
- package/lib/adaptors/WebSocketAdaptor.js +184 -48
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/adaptors/internal/RoutePathMatcher.d.ts +1 -0
- package/lib/adaptors/internal/RoutePathMatcher.js +88 -0
- package/lib/adaptors/internal/RoutePathMatcher.js.map +1 -0
- package/lib/adaptors/internal/external_context.d.ts +1 -0
- package/lib/adaptors/internal/external_context.js +56 -0
- package/lib/adaptors/internal/external_context.js.map +1 -0
- package/lib/decorators/DynamicModule.d.ts +12 -0
- package/lib/decorators/DynamicModule.js +12 -0
- package/lib/decorators/DynamicModule.js.map +1 -1
- package/lib/decorators/EncryptedBody.d.ts +14 -6
- package/lib/decorators/EncryptedBody.js +39 -16
- package/lib/decorators/EncryptedBody.js.map +1 -1
- package/lib/decorators/EncryptedController.d.ts +6 -1
- package/lib/decorators/EncryptedController.js +7 -2
- package/lib/decorators/EncryptedController.js.map +1 -1
- package/lib/decorators/EncryptedModule.d.ts +25 -6
- package/lib/decorators/EncryptedModule.js +87 -18
- package/lib/decorators/EncryptedModule.js.map +1 -1
- package/lib/decorators/EncryptedRoute.d.ts +20 -11
- package/lib/decorators/EncryptedRoute.js +27 -13
- package/lib/decorators/EncryptedRoute.js.map +1 -1
- package/lib/decorators/HumanRoute.d.ts +5 -0
- package/lib/decorators/HumanRoute.js +5 -0
- package/lib/decorators/HumanRoute.js.map +1 -1
- package/lib/decorators/McpRoute.d.ts +31 -19
- package/lib/decorators/McpRoute.js +12 -1
- package/lib/decorators/McpRoute.js.map +1 -1
- package/lib/decorators/PlainBody.d.ts +7 -1
- package/lib/decorators/PlainBody.js +16 -8
- package/lib/decorators/PlainBody.js.map +1 -1
- package/lib/decorators/SwaggerCustomizer.d.ts +15 -0
- package/lib/decorators/SwaggerCustomizer.js +5 -0
- package/lib/decorators/SwaggerCustomizer.js.map +1 -1
- package/lib/decorators/SwaggerExample.d.ts +51 -22
- package/lib/decorators/SwaggerExample.js +35 -22
- package/lib/decorators/SwaggerExample.js.map +1 -1
- package/lib/decorators/TypedBody.d.ts +10 -3
- package/lib/decorators/TypedBody.js +20 -10
- package/lib/decorators/TypedBody.js.map +1 -1
- package/lib/decorators/TypedException.d.ts +10 -0
- package/lib/decorators/TypedFormData.d.ts +81 -9
- package/lib/decorators/TypedFormData.js +121 -31
- package/lib/decorators/TypedFormData.js.map +1 -1
- package/lib/decorators/TypedHeaders.d.ts +5 -0
- package/lib/decorators/TypedHeaders.js +5 -0
- package/lib/decorators/TypedHeaders.js.map +1 -1
- package/lib/decorators/TypedParam.d.ts +12 -3
- package/lib/decorators/TypedParam.js +12 -3
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedQuery.d.ts +17 -1
- package/lib/decorators/TypedQuery.js +25 -4
- package/lib/decorators/TypedQuery.js.map +1 -1
- package/lib/decorators/TypedRoute.d.ts +25 -10
- package/lib/decorators/TypedRoute.js +19 -10
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/WebSocketRoute.d.ts +18 -1
- package/lib/decorators/WebSocketRoute.js +18 -1
- package/lib/decorators/WebSocketRoute.js.map +1 -1
- package/lib/decorators/doNotThrowTransformError.d.ts +14 -0
- package/lib/decorators/doNotThrowTransformError.js +14 -0
- package/lib/decorators/doNotThrowTransformError.js.map +1 -1
- package/lib/decorators/internal/EncryptedConstant.js +13 -2
- package/lib/decorators/internal/EncryptedConstant.js.map +1 -1
- package/lib/decorators/internal/IRequestBodyValidation.d.ts +66 -0
- package/lib/decorators/internal/IRequestBodyValidation.js +3 -0
- package/lib/decorators/internal/IRequestBodyValidation.js.map +1 -0
- package/lib/decorators/internal/IWebSocketRouteReflect.d.ts +93 -1
- package/lib/decorators/internal/get_encryption_password.d.ts +1 -0
- package/lib/decorators/internal/get_encryption_password.js +14 -0
- package/lib/decorators/internal/get_encryption_password.js.map +1 -0
- package/lib/decorators/internal/get_path_and_querify.js +23 -9
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/get_path_and_stringify.js +3 -7
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/get_text_body.js +9 -3
- package/lib/decorators/internal/get_text_body.js.map +1 -1
- package/lib/decorators/internal/group_entries.d.ts +1 -0
- package/lib/decorators/internal/group_entries.js +19 -0
- package/lib/decorators/internal/group_entries.js.map +1 -0
- package/lib/decorators/internal/load_controller.d.ts +14 -0
- package/lib/decorators/internal/load_controller.js +14 -0
- package/lib/decorators/internal/load_controller.js.map +1 -1
- package/lib/decorators/internal/split_route_arguments.d.ts +1 -0
- package/lib/decorators/internal/split_route_arguments.js +22 -0
- package/lib/decorators/internal/split_route_arguments.js.map +1 -0
- package/lib/decorators/internal/validate_request_body.js +68 -25
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/decorators/internal/validate_request_form_data.js +3 -1
- package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
- package/lib/decorators/internal/validate_request_headers.js +2 -1
- package/lib/decorators/internal/validate_request_headers.js.map +1 -1
- package/lib/decorators/internal/validate_request_query.js +3 -1
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/lib/options/IRequestBodyValidator.d.ts +71 -0
- package/lib/options/IRequestFormDataProps.d.ts +80 -0
- package/lib/options/IRequestHeadersValidator.d.ts +67 -0
- package/lib/options/IRequestQueryValidator.d.ts +68 -0
- package/lib/options/IResponseBodyQuerifier.d.ts +125 -1
- package/lib/options/IResponseBodyStringifier.d.ts +107 -0
- package/lib/typings/Creator.d.ts +10 -0
- package/lib/utils/ArrayUtil.d.ts +21 -0
- package/lib/utils/ArrayUtil.js +22 -1
- package/lib/utils/ArrayUtil.js.map +1 -1
- package/lib/utils/ExceptionManager.d.ts +43 -4
- package/lib/utils/ExceptionManager.js +44 -6
- package/lib/utils/ExceptionManager.js.map +1 -1
- package/lib/utils/SourceFinder.d.ts +22 -0
- package/lib/utils/SourceFinder.js +60 -10
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +45 -2
- package/lib/utils/VersioningStrategy.js +40 -7
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/native/go.mod +1 -1
- package/native/go.sum +2 -2
- package/native/plugin/plan.go +68 -2
- package/native/transform/ast.go +23 -3
- package/native/transform/build.go +51 -57
- package/native/transform/buildoutput/output.go +147 -0
- package/native/transform/contributor.go +9 -0
- package/native/transform/core_querify.go +63 -9
- package/native/transform/core_transform.go +180 -98
- package/native/transform/core_websocket.go +111 -14
- package/native/transform/node_transform.go +17 -8
- package/native/transform/package.go +55 -0
- package/native/transform/printer.go +1 -3
- package/native/transform/run.go +18 -0
- package/native/transform/schemaprojection/project.go +213 -0
- package/native/transform/transform.go +29 -18
- package/native/transform/typia_fast.go +6 -0
- package/native/transform.cjs +49 -0
- package/package.json +13 -15
- package/src/adaptors/McpAdaptor.ts +215 -33
- package/src/adaptors/WebSocketAdaptor.ts +202 -52
- package/src/adaptors/internal/RoutePathMatcher.ts +91 -0
- package/src/adaptors/internal/external_context.ts +81 -0
- package/src/decorators/DynamicModule.ts +12 -0
- package/src/decorators/EncryptedBody.ts +39 -15
- package/src/decorators/EncryptedController.ts +12 -3
- package/src/decorators/EncryptedModule.ts +89 -18
- package/src/decorators/EncryptedRoute.ts +27 -16
- package/src/decorators/HumanRoute.ts +5 -0
- package/src/decorators/McpRoute.ts +31 -19
- package/src/decorators/PlainBody.ts +20 -10
- package/src/decorators/SwaggerCustomizer.ts +15 -0
- package/src/decorators/SwaggerExample.ts +51 -22
- package/src/decorators/TypedBody.ts +18 -9
- package/src/decorators/TypedException.ts +10 -0
- package/src/decorators/TypedFormData.ts +207 -45
- package/src/decorators/TypedHeaders.ts +5 -0
- package/src/decorators/TypedParam.ts +12 -3
- package/src/decorators/TypedQuery.ts +35 -7
- package/src/decorators/TypedRoute.ts +25 -10
- package/src/decorators/WebSocketRoute.ts +18 -1
- package/src/decorators/doNotThrowTransformError.ts +14 -0
- package/src/decorators/internal/EncryptedConstant.ts +14 -1
- package/src/decorators/internal/IMcpRouteReflect.ts +7 -1
- package/src/decorators/internal/IRequestBodyValidation.ts +65 -0
- package/src/decorators/internal/IWebSocketRouteReflect.ts +95 -1
- package/src/decorators/internal/get_encryption_password.ts +19 -0
- package/src/decorators/internal/get_path_and_querify.ts +31 -10
- package/src/decorators/internal/get_path_and_stringify.ts +3 -9
- package/src/decorators/internal/get_text_body.ts +8 -4
- package/src/decorators/internal/group_entries.ts +17 -0
- package/src/decorators/internal/load_controller.ts +14 -0
- package/src/decorators/internal/split_route_arguments.ts +20 -0
- package/src/decorators/internal/validate_request_body.ts +87 -34
- package/src/decorators/internal/validate_request_form_data.ts +3 -1
- package/src/decorators/internal/validate_request_headers.ts +2 -2
- package/src/decorators/internal/validate_request_query.ts +3 -2
- package/src/options/IRequestBodyValidator.ts +71 -0
- package/src/options/IRequestFormDataProps.ts +80 -0
- package/src/options/IRequestHeadersValidator.ts +67 -0
- package/src/options/IRequestQueryValidator.ts +68 -0
- package/src/options/IResponseBodyQuerifier.ts +126 -1
- package/src/options/IResponseBodyStringifier.ts +107 -0
- package/src/typings/Creator.ts +10 -0
- package/src/utils/ArrayUtil.ts +22 -3
- package/src/utils/ExceptionManager.ts +50 -6
- package/src/utils/SourceFinder.ts +62 -10
- package/src/utils/VersioningStrategy.ts +55 -11
- package/lib/transform.d.ts +0 -10
- package/lib/transform.js +0 -25
- package/lib/transform.js.map +0 -1
- package/src/transform.ts +0 -26
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BadRequestException,
|
|
3
3
|
ExecutionContext,
|
|
4
|
+
HttpException,
|
|
5
|
+
PayloadTooLargeException,
|
|
4
6
|
createParamDecorator,
|
|
5
7
|
} from "@nestjs/common";
|
|
6
8
|
import type { HttpArgumentsHost } from "@nestjs/common/interfaces";
|
|
7
9
|
import type express from "express";
|
|
10
|
+
import fs from "fs";
|
|
8
11
|
import type ExpressMulter from "multer";
|
|
9
12
|
|
|
10
13
|
import type { IRequestFormDataProps } from "../options/IRequestFormDataProps";
|
|
@@ -35,10 +38,13 @@ import { validate_request_form_data } from "./internal/validate_request_form_dat
|
|
|
35
38
|
* types are allowed
|
|
36
39
|
* 4. By the way, union type never be not allowed
|
|
37
40
|
*
|
|
38
|
-
* By the way, if you're using `fastify`,
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
41
|
+
* By the way, if you're using `fastify`, pass a `fastify-multer` instance as
|
|
42
|
+
* the factory, and register a `multipart/form-data` content type parser that
|
|
43
|
+
* leaves the request stream to it when composing the NestJS application.
|
|
44
|
+
* Without the parser, Fastify rejects every multipart request with 415.
|
|
45
|
+
* `fastify-multer`'s own `contentParser` plugin declares the content type
|
|
46
|
+
* `multipart` without a subtype, which Fastify 5 (NestJS 11) refuses to
|
|
47
|
+
* register.
|
|
42
48
|
*
|
|
43
49
|
* ```typescript
|
|
44
50
|
* import { NestFactory } from "@nestjs/core";
|
|
@@ -46,19 +52,33 @@ import { validate_request_form_data } from "./internal/validate_request_form_dat
|
|
|
46
52
|
* FastifyAdapter,
|
|
47
53
|
* NestFastifyApplication,
|
|
48
54
|
* } from "@nestjs/platform-fastify";
|
|
49
|
-
* import fastifyMulter from "fastify-multer";
|
|
50
55
|
*
|
|
51
56
|
* export async function main() {
|
|
52
57
|
* const app = await NestFactory.create<NestFastifyApplication>(
|
|
53
58
|
* AppModule,
|
|
54
59
|
* new FastifyAdapter(),
|
|
55
60
|
* );
|
|
56
|
-
* app
|
|
61
|
+
* app
|
|
62
|
+
* .getHttpAdapter()
|
|
63
|
+
* .getInstance()
|
|
64
|
+
* .addContentTypeParser("multipart/form-data", (_req, _payload, done) =>
|
|
65
|
+
* done(null),
|
|
66
|
+
* );
|
|
57
67
|
* await app.listen(3000);
|
|
58
68
|
* }
|
|
69
|
+
*
|
|
70
|
+
* // in the controller, with `import FastifyMulter from "fastify-multer"`
|
|
71
|
+
* public async upload(
|
|
72
|
+
* @TypedFormData.Body(() => FastifyMulter()) body: IMultipart,
|
|
73
|
+
* ): Promise<void> {}
|
|
59
74
|
* ```
|
|
60
75
|
*
|
|
61
76
|
* @author Jeongho Nam - https://github.com/samchon
|
|
77
|
+
* @evidence contracts/common.md#principled-implementation The body must be multipart; multer parses it through the caller's factory into a `FormData` of fields and `File` objects, uploaded temp files are read and always removed afterwards, multer limits map to 413 or 400 responses, and the assembled form is checked by the transformed validator.
|
|
78
|
+
* @evidence contracts/common.md#clear-and-simple-design One decorator with the multipart decoding, the error mapping, and the file conversion as separate module-private functions, and the multer instance created once by a `Singleton`.
|
|
79
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Errors are mapped by multer's documented codes and by exact busboy messages, and no file name is special-cased; a runtime without `File` is refused up front.
|
|
80
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the factory, the memory and disk storage requirement, and the validation.
|
|
81
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
62
82
|
* @todo Change to ReadableStream through configuring storage engine of multer
|
|
63
83
|
*/
|
|
64
84
|
export namespace TypedFormData {
|
|
@@ -69,9 +89,11 @@ export namespace TypedFormData {
|
|
|
69
89
|
*
|
|
70
90
|
* Much easier and type safer than `@nest.UploadFile()` decorator.
|
|
71
91
|
*
|
|
72
|
-
* @param factory Factory function
|
|
92
|
+
* @param factory Factory function creating the `multer` or `fastify-multer`
|
|
73
93
|
* instance. In the factory function, you also can specify the multer
|
|
74
|
-
* composition options like `storage` engine
|
|
94
|
+
* composition options like `storage` engine: memory and disk storage
|
|
95
|
+
* deliver the uploaded bytes, and an engine keeping neither the buffer nor
|
|
96
|
+
* a file path is rejected.
|
|
75
97
|
*/
|
|
76
98
|
export function Body<Multer extends IMulterBase>(
|
|
77
99
|
factory: () => Multer | Promise<Multer>,
|
|
@@ -88,7 +110,7 @@ export namespace TypedFormData {
|
|
|
88
110
|
);
|
|
89
111
|
const checker = validate_request_form_data(props);
|
|
90
112
|
const uploader = new Singleton(async () =>
|
|
91
|
-
decode((await factory()) as ExpressMulter.Multer, props
|
|
113
|
+
decode((await factory()) as ExpressMulter.Multer, props),
|
|
92
114
|
);
|
|
93
115
|
return createParamDecorator(async function TypedFormDataBody(
|
|
94
116
|
_unknown: any,
|
|
@@ -117,12 +139,65 @@ export namespace TypedFormData {
|
|
|
117
139
|
})();
|
|
118
140
|
}
|
|
119
141
|
|
|
120
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Base type of the `multer` or `fastify-multer`.
|
|
144
|
+
*
|
|
145
|
+
* @evidence contracts/common.md#principled-implementation The interface names the multer methods that identify a multer instance structurally, so an application can pass its own instance without this package depending on a multer type at runtime.
|
|
146
|
+
* @evidence contracts/common.md#clear-and-simple-design Five method members with no behavior.
|
|
147
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
148
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states its purpose as the structural stand-in for multer.
|
|
149
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
150
|
+
*/
|
|
121
151
|
export interface IMulterBase {
|
|
152
|
+
/**
|
|
153
|
+
* Middleware factory for a single file field.
|
|
154
|
+
*
|
|
155
|
+
* @evidence contracts/common.md#principled-implementation The member mirrors multer's single-file middleware factory.
|
|
156
|
+
* @evidence contracts/common.md#clear-and-simple-design One method signature.
|
|
157
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
158
|
+
* @evidence contracts/common.md#meaningful-documentation The member is named after the multer method it mirrors.
|
|
159
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
160
|
+
*/
|
|
122
161
|
single(fieldName: string): any;
|
|
162
|
+
/**
|
|
163
|
+
* Middleware factory for an array of files under one field.
|
|
164
|
+
*
|
|
165
|
+
* @evidence contracts/common.md#principled-implementation The member mirrors multer's array-of-files middleware factory.
|
|
166
|
+
* @evidence contracts/common.md#clear-and-simple-design One method signature.
|
|
167
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
168
|
+
* @evidence contracts/common.md#meaningful-documentation The member is named after the multer method it mirrors.
|
|
169
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
170
|
+
*/
|
|
123
171
|
array(fieldName: string, maxCount?: number): any;
|
|
172
|
+
/**
|
|
173
|
+
* Middleware factory for several named file fields.
|
|
174
|
+
*
|
|
175
|
+
* @evidence contracts/common.md#principled-implementation The member mirrors multer's named-fields middleware factory, which is the one the decorator uses when the transform lists the file fields.
|
|
176
|
+
* @evidence contracts/common.md#clear-and-simple-design One method signature.
|
|
177
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
178
|
+
* @evidence contracts/common.md#meaningful-documentation The member is named after the multer method it mirrors.
|
|
179
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
180
|
+
*/
|
|
124
181
|
fields(fields: readonly object[]): any;
|
|
182
|
+
/**
|
|
183
|
+
* Middleware factory that accepts every file field.
|
|
184
|
+
*
|
|
185
|
+
* @evidence contracts/common.md#principled-implementation The member mirrors multer's accept-any-file middleware factory, which the decorator uses when no transform names the file fields.
|
|
186
|
+
* @evidence contracts/common.md#clear-and-simple-design One method signature.
|
|
187
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
188
|
+
* @evidence contracts/common.md#meaningful-documentation The member is named after the multer method it mirrors.
|
|
189
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
190
|
+
*/
|
|
125
191
|
any(): any;
|
|
192
|
+
/**
|
|
193
|
+
* Middleware factory that accepts text fields only.
|
|
194
|
+
*
|
|
195
|
+
* @evidence contracts/common.md#principled-implementation The member mirrors multer's text-fields-only middleware factory.
|
|
196
|
+
* @evidence contracts/common.md#clear-and-simple-design One method signature.
|
|
197
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
198
|
+
* @evidence contracts/common.md#meaningful-documentation The member is named after the multer method it mirrors.
|
|
199
|
+
* @evidence contracts/portability.md#os-neutral-implementation Uploaded disk paths come from the storage engine and are consumed directly by Node fs readFile and rm; filename is File metadata rather than a reconstructed native path. Structural multer method signatures specify middleware rather than native path spelling.
|
|
200
|
+
*/
|
|
126
201
|
none(): any;
|
|
127
202
|
}
|
|
128
203
|
}
|
|
@@ -130,18 +205,22 @@ export namespace TypedFormData {
|
|
|
130
205
|
/** @internal */
|
|
131
206
|
const decode = <T>(
|
|
132
207
|
multer: ExpressMulter.Multer,
|
|
133
|
-
props: IRequestFormDataProps<T
|
|
208
|
+
props: IRequestFormDataProps<T> | undefined,
|
|
134
209
|
) => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
210
|
+
// without the transform nothing names the file fields, so accept every file
|
|
211
|
+
const upload =
|
|
212
|
+
props === undefined
|
|
213
|
+
? multer.any()
|
|
214
|
+
: multer.fields(
|
|
215
|
+
props.files.map((file) => ({
|
|
216
|
+
name: file.name,
|
|
217
|
+
...(file.limit === 1 ? { maxCount: 1 } : {}),
|
|
218
|
+
})),
|
|
219
|
+
);
|
|
141
220
|
const interceptor = (request: express.Request, response: express.Response) =>
|
|
142
221
|
new Promise<void>((resolve, reject) =>
|
|
143
222
|
upload(request, response, (error) => {
|
|
144
|
-
if (error) reject(error);
|
|
223
|
+
if (error) reject(transformMulterError(error));
|
|
145
224
|
else resolve();
|
|
146
225
|
}),
|
|
147
226
|
);
|
|
@@ -151,35 +230,118 @@ const decode = <T>(
|
|
|
151
230
|
}): Promise<FormData> => {
|
|
152
231
|
await interceptor(socket.request, socket.response);
|
|
153
232
|
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
233
|
+
const files: Array<[string, Express.Multer.File]> = entries(
|
|
234
|
+
socket.request.files,
|
|
235
|
+
);
|
|
236
|
+
try {
|
|
237
|
+
const data: FormData = new FormData();
|
|
238
|
+
for (const [key, value] of Object.entries(socket.request.body))
|
|
239
|
+
if (Array.isArray(value))
|
|
240
|
+
for (const elem of value) data.append(key, String(elem));
|
|
241
|
+
else data.append(key, String(value));
|
|
242
|
+
for (const [key, file] of files) data.append(key, await toFile(file));
|
|
243
|
+
return data;
|
|
244
|
+
} finally {
|
|
245
|
+
// a disk storage engine leaves each file behind, and the handler only
|
|
246
|
+
// receives its contents as a File, never the path to remove it by
|
|
247
|
+
await Promise.all(
|
|
248
|
+
files.map(([, file]) =>
|
|
249
|
+
typeof file.path === "string"
|
|
250
|
+
? fs.promises.rm(file.path, { force: true })
|
|
251
|
+
: undefined,
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
161
255
|
};
|
|
162
256
|
};
|
|
163
257
|
|
|
258
|
+
/**
|
|
259
|
+
* The client error NestJS's own `FileInterceptor` answers for a multipart
|
|
260
|
+
* request the multer configuration rejects: 413 for a file over
|
|
261
|
+
* `limits.fileSize`, 400 for the other limits, an unexpected field, and a
|
|
262
|
+
* malformed body. Anything else, an `HttpException` a `fileFilter` threw
|
|
263
|
+
* included, is left as it is.
|
|
264
|
+
*
|
|
265
|
+
* @internal
|
|
266
|
+
*/
|
|
267
|
+
const transformMulterError = (error: unknown): unknown => {
|
|
268
|
+
if (!(error instanceof Error) || error instanceof HttpException) return error;
|
|
269
|
+
const code: unknown = (error as { code?: unknown }).code;
|
|
270
|
+
const field: unknown = (error as { field?: unknown }).field;
|
|
271
|
+
if (code === "LIMIT_FILE_SIZE")
|
|
272
|
+
return new PayloadTooLargeException(error.message);
|
|
273
|
+
else if (typeof code === "string" && MULTER_BAD_REQUESTS.has(code))
|
|
274
|
+
return new BadRequestException(
|
|
275
|
+
typeof field === "string" ? `${error.message} - ${field}` : error.message,
|
|
276
|
+
);
|
|
277
|
+
else if (error.message === "Multipart: Boundary not found")
|
|
278
|
+
return new BadRequestException(error.message);
|
|
279
|
+
else if (
|
|
280
|
+
BUSBOY_BAD_REQUESTS.has(error.message) ||
|
|
281
|
+
error.message.endsWith(
|
|
282
|
+
" terminated early due to unexpected end of multipart data",
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
return new BadRequestException(`Multipart: ${error.message}`);
|
|
286
|
+
return error;
|
|
287
|
+
};
|
|
288
|
+
|
|
164
289
|
/** @internal */
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
290
|
+
const MULTER_BAD_REQUESTS: Set<string> = new Set([
|
|
291
|
+
"LIMIT_PART_COUNT",
|
|
292
|
+
"LIMIT_FILE_COUNT",
|
|
293
|
+
"LIMIT_FIELD_KEY",
|
|
294
|
+
"LIMIT_FIELD_VALUE",
|
|
295
|
+
"LIMIT_FIELD_COUNT",
|
|
296
|
+
"LIMIT_UNEXPECTED_FILE",
|
|
297
|
+
"MISSING_FIELD_NAME",
|
|
298
|
+
]);
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* A malformed body, as `busboy` (under `multer`) and `@fastify/busboy` (under
|
|
302
|
+
* `fastify-multer`) word it.
|
|
303
|
+
*
|
|
304
|
+
* @internal
|
|
305
|
+
*/
|
|
306
|
+
const BUSBOY_BAD_REQUESTS: Set<string> = new Set([
|
|
307
|
+
"Malformed part header",
|
|
308
|
+
"Unexpected end of form",
|
|
309
|
+
"Unexpected end of file",
|
|
310
|
+
"Unexpected end of multipart data",
|
|
311
|
+
]);
|
|
312
|
+
|
|
313
|
+
/** @internal */
|
|
314
|
+
const entries = (
|
|
315
|
+
files:
|
|
316
|
+
| Express.Multer.File[]
|
|
317
|
+
| Record<string, Express.Multer.File[]>
|
|
318
|
+
| undefined,
|
|
319
|
+
): Array<[string, Express.Multer.File]> =>
|
|
320
|
+
files === undefined
|
|
321
|
+
? []
|
|
322
|
+
: Array.isArray(files)
|
|
323
|
+
? files.map((file) => [file.fieldname, file])
|
|
324
|
+
: Object.entries(files).flatMap(([key, value]) =>
|
|
325
|
+
value.map((file): [string, Express.Multer.File] => [key, file]),
|
|
175
326
|
);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* The uploaded file as a `File`: memory storage keeps its bytes in `buffer`,
|
|
330
|
+
* disk storage in the file at `path`. An engine keeping neither cannot deliver
|
|
331
|
+
* it, which must not pass as an empty or placeholder file.
|
|
332
|
+
*
|
|
333
|
+
* @internal
|
|
334
|
+
*/
|
|
335
|
+
const toFile = async (file: Express.Multer.File): Promise<File> => {
|
|
336
|
+
const bytes: Buffer | undefined =
|
|
337
|
+
file.buffer !== undefined
|
|
338
|
+
? file.buffer
|
|
339
|
+
: typeof file.path === "string"
|
|
340
|
+
? await fs.promises.readFile(file.path)
|
|
341
|
+
: undefined;
|
|
342
|
+
if (bytes === undefined)
|
|
343
|
+
throw new Error(
|
|
344
|
+
`Error on TypedFormData.Body(): the multer storage engine kept neither the buffer nor the path of the uploaded file ${JSON.stringify(file.fieldname)}, so it cannot be read as a File. Use memory or disk storage.`,
|
|
345
|
+
);
|
|
346
|
+
return new File([bytes as any], file.originalname, { type: file.mimetype });
|
|
347
|
+
};
|
|
@@ -47,6 +47,11 @@ import { validate_request_headers } from "./internal/validate_request_headers";
|
|
|
47
47
|
*
|
|
48
48
|
* @author Jeongho Nam - https://github.com/samchon
|
|
49
49
|
* @returns Parameter decorator
|
|
50
|
+
* @evidence contracts/common.md#principled-implementation The header record of the request is decoded by the transformed function into the typed object, whose failure is thrown as the response; header names arrive lower-cased from Node, which the generated decoder expects.
|
|
51
|
+
* @evidence contracts/common.md#clear-and-simple-design One parameter decorator built from the shared header validator runner.
|
|
52
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The decoder is generated from the type by the transform; without it the request headers are passed through only when the configuration guard is off.
|
|
53
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the decoding, the supported property types, and the validation modes.
|
|
54
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Header names and decoded values belong to the HTTP protocol and are not native filesystem identities.
|
|
50
55
|
*/
|
|
51
56
|
export function TypedHeaders<T extends object>(
|
|
52
57
|
validator?: IRequestHeadersValidator<T>,
|
|
@@ -22,15 +22,24 @@ import { NoTransformConfigurationError } from "./NoTransformConfigurationError";
|
|
|
22
22
|
*
|
|
23
23
|
* \@TypedRoute.Get("shopping/sales/:id/:no/:paused")
|
|
24
24
|
* public async pause(
|
|
25
|
-
* \@TypedParam("id"
|
|
26
|
-
* \@TypedParam("no")
|
|
27
|
-
* \@TypedParam("paused") paused: boolean | null
|
|
25
|
+
* \@TypedParam("id") id: string & tags.Format<"uuid">,
|
|
26
|
+
* \@TypedParam("no") no: number & tags.Type<"uint32">,
|
|
27
|
+
* \@TypedParam("paused") paused: boolean | null,
|
|
28
28
|
* ): Promise<void>;
|
|
29
29
|
* ```
|
|
30
30
|
*
|
|
31
31
|
* @author Jeongho Nam - https://github.com/samchon
|
|
32
32
|
* @param name URL Parameter name
|
|
33
|
+
* @param assert Conversion and assertion supplied by the compile-time transform
|
|
34
|
+
* @param validate Emit an errors array on validation failure when true;
|
|
35
|
+
* otherwise expose the first failure as flat path, reason, expected and value
|
|
36
|
+
* fields.
|
|
33
37
|
* @returns Parameter decorator
|
|
38
|
+
* @evidence contracts/common.md#principled-implementation The path parameter arrives as a string and is converted by the assertion function the transform generated for the declared type, so a value of the wrong type produces a 400 that names the parameter; the `validate` flag only changes the error body from the flat form to an `errors` array.
|
|
39
|
+
* @evidence contracts/common.md#clear-and-simple-design One decorator with one conversion function and one error-shaping branch.
|
|
40
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The conversion is generated from the declared type; without the transform the raw string is returned only when the configuration guard is off.
|
|
41
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the supported types and the two error shapes.
|
|
42
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation The named path segment is an HTTP route parameter whose text is decoded by the generated validator; no native path is resolved.
|
|
34
43
|
*/
|
|
35
44
|
export function TypedParam<T extends boolean | bigint | number | string | null>(
|
|
36
45
|
name: string,
|
|
@@ -15,11 +15,12 @@ import {
|
|
|
15
15
|
import { HttpArgumentsHost } from "@nestjs/common/interfaces";
|
|
16
16
|
import type express from "express";
|
|
17
17
|
import type { FastifyRequest } from "fastify";
|
|
18
|
-
import { catchError, map } from "rxjs";
|
|
18
|
+
import { catchError, map } from "rxjs/operators";
|
|
19
19
|
import typia from "typia";
|
|
20
20
|
|
|
21
21
|
import { IRequestQueryValidator } from "../options/IRequestQueryValidator";
|
|
22
22
|
import { IResponseBodyQuerifier } from "../options/IResponseBodyQuerifier";
|
|
23
|
+
import { TypedRoute } from "./TypedRoute";
|
|
23
24
|
import { get_path_and_querify } from "./internal/get_path_and_querify";
|
|
24
25
|
import { is_media_type } from "./internal/is_media_type";
|
|
25
26
|
import { route_error } from "./internal/route_error";
|
|
@@ -44,6 +45,11 @@ import { validate_request_query } from "./internal/validate_request_query";
|
|
|
44
45
|
*
|
|
45
46
|
* @author Jeongho Nam - https://github.com/samchon
|
|
46
47
|
* @returns Parameter decorator
|
|
48
|
+
* @evidence contracts/common.md#principled-implementation The query string is read from the request URL after the first question mark, grouped by the transformed decoder into the typed object, and a failure is thrown as the 400 response; the decoder receives a `URLSearchParams`, so both Express and Fastify requests are handled by the same code.
|
|
49
|
+
* @evidence contracts/common.md#clear-and-simple-design One parameter decorator on top of the shared query validator runner.
|
|
50
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The decoder is generated from the type by the transform; without it the grouped raw query is returned only when the configuration guard is off.
|
|
51
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the supported property types and the validation modes.
|
|
52
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Query decoding and response encoding operate on URLSearchParams and HTTP route metadata rather than native filesystem paths.
|
|
47
53
|
*/
|
|
48
54
|
export function TypedQuery<T extends object>(
|
|
49
55
|
validator?: IRequestQueryValidator<T>,
|
|
@@ -63,6 +69,17 @@ export function TypedQuery<T extends object>(
|
|
|
63
69
|
return output;
|
|
64
70
|
})();
|
|
65
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Variants of {@link TypedQuery}: `TypedQuery.Body` reads an urlencoded body,
|
|
74
|
+
* and `TypedQuery.Get`, `Post`, `Put`, `Patch`, and `Delete` return the value
|
|
75
|
+
* as an urlencoded response.
|
|
76
|
+
*
|
|
77
|
+
* @evidence contracts/common.md#principled-implementation `Body` requires the `application/x-www-form-urlencoded` media type and decodes the parsed body, and the route decorators serialize the returned value through the querifier the transform chose and set the response content type.
|
|
78
|
+
* @evidence contracts/common.md#clear-and-simple-design One body decorator and one generator that creates the five route decorators, with a small adapter class for parsed bodies.
|
|
79
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The route decorators copy typia's marker properties onto themselves exactly as `TypedRoute` does, which mutates only these exported functions.
|
|
80
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the body decorator and the route decorators.
|
|
81
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Query decoding and response encoding operate on URLSearchParams and HTTP route metadata rather than native filesystem paths.
|
|
82
|
+
*/
|
|
66
83
|
export namespace TypedQuery {
|
|
67
84
|
/**
|
|
68
85
|
* Request body decorator.
|
|
@@ -117,7 +134,7 @@ export namespace TypedQuery {
|
|
|
117
134
|
export const Post = Generator("Post");
|
|
118
135
|
|
|
119
136
|
/**
|
|
120
|
-
* Router decorator function for the
|
|
137
|
+
* Router decorator function for the PATCH method.
|
|
121
138
|
*
|
|
122
139
|
* @param path Path of the HTTP request
|
|
123
140
|
* @returns Method decorator
|
|
@@ -150,9 +167,9 @@ export namespace TypedQuery {
|
|
|
150
167
|
): MethodDecorator;
|
|
151
168
|
|
|
152
169
|
function route(...args: any[]): MethodDecorator {
|
|
153
|
-
const [path, stringify] = get_path_and_querify(
|
|
154
|
-
|
|
155
|
-
);
|
|
170
|
+
const [path, stringify] = get_path_and_querify(() => TypedRoute.__logger)(
|
|
171
|
+
`TypedQuery.${method}`,
|
|
172
|
+
)(...args);
|
|
156
173
|
return applyDecorators(
|
|
157
174
|
ROUTERS[method](path),
|
|
158
175
|
UseInterceptors(new TypedQueryRouteInterceptor(stringify)),
|
|
@@ -186,6 +203,10 @@ class FakeURLSearchParams {
|
|
|
186
203
|
return this.target[key] !== undefined;
|
|
187
204
|
}
|
|
188
205
|
|
|
206
|
+
public keys(): string[] {
|
|
207
|
+
return Object.keys(this.target).filter((key) => this.has(key));
|
|
208
|
+
}
|
|
209
|
+
|
|
189
210
|
public get(key: string): string | null {
|
|
190
211
|
const value = this.target[key];
|
|
191
212
|
return value === undefined
|
|
@@ -204,16 +225,23 @@ class FakeURLSearchParams {
|
|
|
204
225
|
/** @internal */
|
|
205
226
|
class TypedQueryRouteInterceptor implements NestInterceptor {
|
|
206
227
|
public constructor(
|
|
207
|
-
private readonly toSearchParams: (
|
|
228
|
+
private readonly toSearchParams: (
|
|
229
|
+
input: any,
|
|
230
|
+
method: string,
|
|
231
|
+
path: string,
|
|
232
|
+
) => URLSearchParams,
|
|
208
233
|
) {}
|
|
209
234
|
|
|
210
235
|
public intercept(context: ExecutionContext, next: CallHandler) {
|
|
211
236
|
const http: HttpArgumentsHost = context.switchToHttp();
|
|
237
|
+
const request: express.Request = http.getRequest();
|
|
212
238
|
const response: express.Response = http.getResponse();
|
|
213
239
|
response.header("Content-Type", "application/x-www-form-urlencoded");
|
|
214
240
|
|
|
215
241
|
return next.handle().pipe(
|
|
216
|
-
map((value) =>
|
|
242
|
+
map((value) =>
|
|
243
|
+
this.toSearchParams(value, request.method, request.url).toString(),
|
|
244
|
+
),
|
|
217
245
|
catchError((err) => route_error(http.getRequest(), err)),
|
|
218
246
|
);
|
|
219
247
|
}
|
|
@@ -23,17 +23,21 @@ import { route_error } from "./internal/route_error";
|
|
|
23
23
|
/**
|
|
24
24
|
* Type safe router decorator functions.
|
|
25
25
|
*
|
|
26
|
-
* `TypedRoute`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* safe through [typia](https://github.com/samchon/typia).
|
|
26
|
+
* `TypedRoute` provides router decorators that serialize response values with
|
|
27
|
+
* [typia](https://github.com/samchon/typia) functions generated from the
|
|
28
|
+
* declared return type.
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* The default assert serializer rejects a response outside the declared type
|
|
31
|
+
* with a 500 error. The configured stringify mode can choose a different
|
|
32
|
+
* validation policy; `validate.log` logs failures and returns JSON text.
|
|
33
|
+
* Interceptors that run after this serializer receive the serialized string.
|
|
35
34
|
*
|
|
36
35
|
* @author Jeongho Nam - https://github.com/samchon
|
|
36
|
+
* @evidence contracts/common.md#principled-implementation Each method decorator combines Nest's router decorator, a JSON content type, and an interceptor that stringifies the returned value with the function the transform selected, so the JSON text is produced by generated code; an error thrown by the handler is routed to the registered exception converters.
|
|
37
|
+
* @evidence contracts/common.md#clear-and-simple-design One generator function creates the five HTTP-method decorators, and the interceptor and the router table are module-private.
|
|
38
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The loop after the namespace copies the marker properties of typia's stringify functions onto the decorators for the transform, which mutates only these exported functions; the logger is a module variable, not a patched global.
|
|
39
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the decorator family, the path and stringify arguments, and the validation modes.
|
|
40
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Routing paths and serialized response values are HTTP protocol data; the interceptor and logger do not define a native file or subprocess boundary.
|
|
37
41
|
*/
|
|
38
42
|
export namespace TypedRoute {
|
|
39
43
|
/**
|
|
@@ -53,7 +57,7 @@ export namespace TypedRoute {
|
|
|
53
57
|
export const Post = Generator("Post");
|
|
54
58
|
|
|
55
59
|
/**
|
|
56
|
-
* Router decorator function for the
|
|
60
|
+
* Router decorator function for the PATCH method.
|
|
57
61
|
*
|
|
58
62
|
* @param path Path of the HTTP request
|
|
59
63
|
* @returns Method decorator
|
|
@@ -82,7 +86,7 @@ export namespace TypedRoute {
|
|
|
82
86
|
* If you've configured the transformation option to `validate.log` in the
|
|
83
87
|
* `tsconfig.json` file, then the error log information of the response
|
|
84
88
|
* validation failure would be logged through this function instead of
|
|
85
|
-
* throwing the
|
|
89
|
+
* throwing the 500 internal server error.
|
|
86
90
|
*
|
|
87
91
|
* By the way, be careful. If you've configured the response transformation
|
|
88
92
|
* option to be `validate.log`, client may get wrong response data. Therefore,
|
|
@@ -90,6 +94,11 @@ export namespace TypedRoute {
|
|
|
90
94
|
*
|
|
91
95
|
* @default console.log
|
|
92
96
|
* @param func Logger function
|
|
97
|
+
* @evidence contracts/common.md#principled-implementation It replaces the module's logger variable, which the `validate.log` mode calls when a response fails validation, and the default is `console.log`.
|
|
98
|
+
* @evidence contracts/common.md#clear-and-simple-design One assignment.
|
|
99
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It changes only the logger variable.
|
|
100
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states which mode uses the logger.
|
|
101
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Routing paths and serialized response values are HTTP protocol data; the interceptor and logger do not define a native file or subprocess boundary.
|
|
93
102
|
*/
|
|
94
103
|
export function setValidateErrorLogger(
|
|
95
104
|
func: (log: IValidateErrorLog) => void,
|
|
@@ -109,6 +118,12 @@ export namespace TypedRoute {
|
|
|
109
118
|
* information `IValidateErrorLog` would be logged through the
|
|
110
119
|
* {@link setValidateErrorLogger} function instead of throwing the 400 bad
|
|
111
120
|
* request error.
|
|
121
|
+
*
|
|
122
|
+
* @evidence contracts/common.md#principled-implementation The record carries the method, the path, the validation errors, and the offending data, which is what a logger needs to report a response that failed its type.
|
|
123
|
+
* @evidence contracts/common.md#clear-and-simple-design A flat record with no behavior.
|
|
124
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
125
|
+
* @evidence contracts/common.md#meaningful-documentation Each member documents its meaning.
|
|
126
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation Routing paths and serialized response values are HTTP protocol data; the interceptor and logger do not define a native file or subprocess boundary.
|
|
112
127
|
*/
|
|
113
128
|
export interface IValidateErrorLog {
|
|
114
129
|
/** HTTP method of the request. */
|
|
@@ -38,6 +38,11 @@ import { validate_request_query } from "./internal/validate_request_query";
|
|
|
38
38
|
* @author Jeongho Nam - https://github.com/samchon
|
|
39
39
|
* @param path Path(s) of the WebSocket request
|
|
40
40
|
* @returns Method decorator
|
|
41
|
+
* @evidence contracts/common.md#principled-implementation The decorator records the route paths as metadata on the method, with an empty list for no path, and the adapter reads that metadata to build the route table.
|
|
42
|
+
* @evidence contracts/common.md#clear-and-simple-design One decorator function that writes one metadata object.
|
|
43
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the reflect metadata API and does not touch the server.
|
|
44
|
+
* @evidence contracts/common.md#meaningful-documentation The comment documents the path forms and the required parameters.
|
|
45
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation The route and parameter metadata describe WebSocket handshakes and HTTP URL fields; native files and processes are not represented.
|
|
41
46
|
*/
|
|
42
47
|
export function WebSocketRoute(
|
|
43
48
|
path?: undefined | string | string[],
|
|
@@ -57,6 +62,16 @@ export function WebSocketRoute(
|
|
|
57
62
|
return descriptor;
|
|
58
63
|
};
|
|
59
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Parameter decorators of {@link WebSocketRoute}: `Acceptor`, `Driver`,
|
|
67
|
+
* `Header`, `Param`, and `Query`.
|
|
68
|
+
*
|
|
69
|
+
* @evidence contracts/common.md#principled-implementation Each decorator appends category and position metadata. Header registration stores the shared checker whose resolver preserves successful callback data for the decorated argument without replacing the acceptor header; legacy error-only callbacks retain raw input. Param and query keep their own decoder contracts, and every rejected handshake retains its original error handling.
|
|
70
|
+
* @evidence contracts/common.md#clear-and-simple-design Five decorators sharing one `emplace` helper for the metadata list.
|
|
71
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The validators are generated from the types; without the transform the raw values are passed only when the configuration guard is off.
|
|
72
|
+
* @evidence contracts/common.md#meaningful-documentation Each decorator documents its meaning.
|
|
73
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation The route and parameter metadata describe WebSocket handshakes and HTTP URL fields; native files and processes are not represented.
|
|
74
|
+
*/
|
|
60
75
|
export namespace WebSocketRoute {
|
|
61
76
|
/**
|
|
62
77
|
* Acceptor parameter decorator.
|
|
@@ -127,7 +142,9 @@ export namespace WebSocketRoute {
|
|
|
127
142
|
*
|
|
128
143
|
* Also, this `@WebSocketRoute.Header()` decorator is optional, and can be
|
|
129
144
|
* substituted by `@WebSocketRoute.Acceptor()` decorated parameter by
|
|
130
|
-
*
|
|
145
|
+
* accessing the {@link WebSocketAcceptor.header} property. Clone validators
|
|
146
|
+
* supply a separate decorated argument; the acceptor keeps its original
|
|
147
|
+
* header.
|
|
131
148
|
*/
|
|
132
149
|
export function Header<T>(
|
|
133
150
|
validator?: IRequestBodyValidator<T>,
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { NoTransformConfigurationError } from "./NoTransformConfigurationError";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Chooses whether a decorator called without the compile-time transform throws.
|
|
5
|
+
*
|
|
6
|
+
* The initial configuration throws with instructions to build with `ttsc`.
|
|
7
|
+
* Calling this setter with `false`, including its omitted-argument default,
|
|
8
|
+
* silences the error. Passing `true` restores throwing for subsequent decorator
|
|
9
|
+
* construction. Decorators already constructed keep their selected callbacks.
|
|
10
|
+
*
|
|
11
|
+
* @evidence contracts/common.md#principled-implementation The function sets the one flag that `NoTransformConfigurationError` reads, so the choice is made once for the process.
|
|
12
|
+
* @evidence contracts/common.md#clear-and-simple-design One setter with a boolean parameter.
|
|
13
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It changes an exported flag of this package and nothing foreign.
|
|
14
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states the default and the effect of the flag.
|
|
15
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation This setter changes an in-memory validation configuration flag and performs no filesystem or process operation.
|
|
16
|
+
*/
|
|
3
17
|
export const doNotThrowTransformError = (value: boolean = false) => {
|
|
4
18
|
NoTransformConfigurationError.throws = value;
|
|
5
19
|
};
|
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* The password an {@link EncryptedModule} gives each controller it reaches.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
2
6
|
export const ENCRYPTION_METADATA_KEY = "nestia:core:encryption:password";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The password an {@link EncryptedController} declares for its own class, kept
|
|
10
|
+
* apart from the module's so that the module never replaces it.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export const ENCRYPTION_CONTROLLER_METADATA_KEY =
|
|
15
|
+
"nestia:core:encryption:controller-password";
|