@nestjs/common 11.1.16 → 11.1.17
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 +1 -1
- package/interfaces/microservices/pre-request-hook.interface.d.ts +22 -0
- package/interfaces/microservices/pre-request-hook.interface.js +1 -0
- package/internal.d.ts +30 -0
- package/internal.js +19 -0
- package/package.json +3 -3
- package/pipes/standard-schema-validation.pipe.d.ts +86 -0
- package/pipes/standard-schema-validation.pipe.js +120 -0
- package/serializer/standard-schema-serializer.interceptor.d.ts +50 -0
- package/serializer/standard-schema-serializer.interceptor.js +75 -0
- package/serializer/standard-schema-serializer.interfaces.d.ts +18 -0
- package/serializer/standard-schema-serializer.interfaces.js +1 -0
package/Readme.md
CHANGED
|
@@ -29,7 +29,7 @@ Nest is a framework for building efficient, scalable <a href="https://nodejs.org
|
|
|
29
29
|
|
|
30
30
|
## Philosophy
|
|
31
31
|
|
|
32
|
-
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and
|
|
32
|
+
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front-end and back-end applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a>, and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
|
33
33
|
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
|
34
34
|
|
|
35
35
|
## Getting started
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { ExecutionContext } from '../features/execution-context.interface.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interface describing a global preRequest hook for microservices.
|
|
5
|
+
*
|
|
6
|
+
* Hooks are executed before guards, allowing setup of context (e.g. AsyncLocalStorage)
|
|
7
|
+
* that is available to all downstream enhancers.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const als = new AsyncLocalStorage();
|
|
12
|
+
* app.registerPreRequestHook((context, next) => {
|
|
13
|
+
* als.enterWith({ correlationId: uuid() });
|
|
14
|
+
* return next();
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @publicApi
|
|
19
|
+
*/
|
|
20
|
+
export interface PreRequestHook {
|
|
21
|
+
(context: ExecutionContext, next: () => Observable<unknown>): Observable<unknown>;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/internal.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal module - not part of the public API.
|
|
3
|
+
* These exports are used by sibling @nestjs packages.
|
|
4
|
+
* Do not depend on these in your application code.
|
|
5
|
+
* @internal
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
export * from './constants.js';
|
|
9
|
+
export { RouteParamtypes } from './enums/route-paramtypes.enum.js';
|
|
10
|
+
export * from './utils/shared.utils.js';
|
|
11
|
+
export * from './utils/load-package.util.js';
|
|
12
|
+
export * from './utils/cli-colors.util.js';
|
|
13
|
+
export * from './utils/random-string-generator.util.js';
|
|
14
|
+
export * from './utils/select-exception-filter-metadata.util.js';
|
|
15
|
+
export type { Controller, Injectable } from './interfaces/index.js';
|
|
16
|
+
export type { NestApplicationContextOptions } from './interfaces/nest-application-context-options.interface.js';
|
|
17
|
+
export type { NestMicroserviceOptions } from './interfaces/microservices/nest-microservice-options.interface.js';
|
|
18
|
+
export type { CorsOptions, CorsOptionsDelegate, CustomOrigin, } from './interfaces/external/cors-options.interface.js';
|
|
19
|
+
export type { ExceptionFilterMetadata } from './interfaces/exceptions/exception-filter-metadata.interface.js';
|
|
20
|
+
export type { RpcExceptionFilterMetadata } from './interfaces/exceptions/rpc-exception-filter-metadata.interface.js';
|
|
21
|
+
export type { VersionValue } from './interfaces/version-options.interface.js';
|
|
22
|
+
export type { GlobalPrefixOptions } from './interfaces/global-prefix-options.interface.js';
|
|
23
|
+
export type { MiddlewareConfiguration, RouteInfo, } from './interfaces/middleware/middleware-configuration.interface.js';
|
|
24
|
+
export type { MiddlewareConfigProxy } from './interfaces/middleware/middleware-config-proxy.interface.js';
|
|
25
|
+
export type { ModuleMetadata } from './interfaces/modules/module-metadata.interface.js';
|
|
26
|
+
export type { HttpArgumentsHost, RpcArgumentsHost, WsArgumentsHost, } from './interfaces/features/arguments-host.interface.js';
|
|
27
|
+
export type { RequestHandler } from './interfaces/http/http-server.interface.js';
|
|
28
|
+
export type { GetOrResolveOptions, SelectOptions, } from './interfaces/nest-application-context.interface.js';
|
|
29
|
+
export type { ShutdownHooksOptions } from './interfaces/shutdown-hooks-options.interface.js';
|
|
30
|
+
export { assignMetadata } from './decorators/http/route-params.decorator.js';
|
package/internal.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal module - not part of the public API.
|
|
3
|
+
* These exports are used by sibling @nestjs packages.
|
|
4
|
+
* Do not depend on these in your application code.
|
|
5
|
+
* @internal
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
// Constants
|
|
9
|
+
export * from './constants.js';
|
|
10
|
+
// Enums (internal)
|
|
11
|
+
export { RouteParamtypes } from './enums/route-paramtypes.enum.js';
|
|
12
|
+
// Utils
|
|
13
|
+
export * from './utils/shared.utils.js';
|
|
14
|
+
export * from './utils/load-package.util.js';
|
|
15
|
+
export * from './utils/cli-colors.util.js';
|
|
16
|
+
export * from './utils/random-string-generator.util.js';
|
|
17
|
+
export * from './utils/select-exception-filter-metadata.util.js';
|
|
18
|
+
// Decorators (internal)
|
|
19
|
+
export { assignMetadata } from './decorators/http/route-params.decorator.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs/common",
|
|
3
|
-
"version": "11.1.
|
|
3
|
+
"version": "11.1.17",
|
|
4
4
|
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
|
5
5
|
"author": "Kamil Mysliwiec",
|
|
6
6
|
"homepage": "https://nestjs.com",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"file-type": "21.3.
|
|
21
|
+
"file-type": "21.3.2",
|
|
22
22
|
"iterare": "1.2.1",
|
|
23
23
|
"load-esm": "1.0.3",
|
|
24
24
|
"tslib": "2.8.1",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"optional": true
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "447a373ebebd2c58b5b3c8d718f25922a025f2fe"
|
|
42
42
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { ArgumentMetadata, PipeTransform } from '../interfaces/features/pipe-transform.interface.js';
|
|
3
|
+
import { ErrorHttpStatusCode } from '../utils/http-error-by-code.util.js';
|
|
4
|
+
/**
|
|
5
|
+
* @publicApi
|
|
6
|
+
*/
|
|
7
|
+
export interface StandardSchemaValidationPipeOptions {
|
|
8
|
+
/**
|
|
9
|
+
* If true, the pipe will return the value produced by the schema
|
|
10
|
+
* (which may differ from the input if the schema coerces/transforms values).
|
|
11
|
+
* If false, the original input value is returned after successful validation.
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
transform?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* If true, the pipe will also validate parameters decorated with custom decorators
|
|
17
|
+
* (created with `createParamDecorator`). When false, custom parameters are skipped.
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
validateCustomDecorators?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Options to pass to the standard schema `validate` function.
|
|
23
|
+
* These options are forwarded as the second argument to the schema's `~standard.validate` method.
|
|
24
|
+
*/
|
|
25
|
+
validateOptions?: Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* The HTTP status code to be used in the response when the validation fails.
|
|
28
|
+
* @default HttpStatus.BAD_REQUEST
|
|
29
|
+
*/
|
|
30
|
+
errorHttpStatusCode?: ErrorHttpStatusCode;
|
|
31
|
+
/**
|
|
32
|
+
* A factory function that returns an exception object to be thrown
|
|
33
|
+
* if validation fails.
|
|
34
|
+
* @param issues The issues returned by the standard schema validation
|
|
35
|
+
* @returns The exception object
|
|
36
|
+
*/
|
|
37
|
+
exceptionFactory?: (issues: readonly StandardSchemaV1.Issue[]) => any;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Defines the built-in StandardSchemaValidation Pipe.
|
|
41
|
+
*
|
|
42
|
+
* Uses a standard schema object (conforming to the Standard Schema spec)
|
|
43
|
+
* attached to the parameter metadata to validate incoming values.
|
|
44
|
+
*
|
|
45
|
+
* @see [Standard Schema](https://github.com/standard-schema/standard-schema)
|
|
46
|
+
*
|
|
47
|
+
* @publicApi
|
|
48
|
+
*/
|
|
49
|
+
export declare class StandardSchemaValidationPipe implements PipeTransform {
|
|
50
|
+
protected readonly options?: StandardSchemaValidationPipeOptions | undefined;
|
|
51
|
+
protected isTransformEnabled: boolean;
|
|
52
|
+
protected validateCustomDecorators: boolean;
|
|
53
|
+
protected validateOptions: Record<string, unknown> | undefined;
|
|
54
|
+
protected exceptionFactory: (issues: readonly StandardSchemaV1.Issue[]) => any;
|
|
55
|
+
constructor(options?: StandardSchemaValidationPipeOptions | undefined);
|
|
56
|
+
/**
|
|
57
|
+
* Method that validates the incoming value against the standard schema
|
|
58
|
+
* provided in the parameter metadata.
|
|
59
|
+
*
|
|
60
|
+
* @param value currently processed route argument
|
|
61
|
+
* @param metadata contains metadata about the currently processed route argument
|
|
62
|
+
*/
|
|
63
|
+
transform<T = any>(value: T, metadata: ArgumentMetadata): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Determines whether validation should be performed for the given metadata.
|
|
66
|
+
* Skips validation for custom decorators unless `validateCustomDecorators` is enabled.
|
|
67
|
+
*
|
|
68
|
+
* @param metadata contains metadata about the currently processed route argument
|
|
69
|
+
* @returns `true` if validation should be performed
|
|
70
|
+
*/
|
|
71
|
+
protected toValidate(metadata: ArgumentMetadata): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Validates a value against a standard schema.
|
|
74
|
+
* Can be overridden to customize validation behavior.
|
|
75
|
+
*
|
|
76
|
+
* @param value The value to validate
|
|
77
|
+
* @param schema The standard schema to validate against
|
|
78
|
+
* @param options Optional options forwarded to the schema's validate method
|
|
79
|
+
* @returns The validation result
|
|
80
|
+
*/
|
|
81
|
+
protected validate<T = unknown>(value: unknown, schema: StandardSchemaV1, options?: Record<string, unknown>): Promise<StandardSchemaV1.Result<T>> | StandardSchemaV1.Result<T>;
|
|
82
|
+
/**
|
|
83
|
+
* Strips dangerous prototype pollution keys from an object.
|
|
84
|
+
*/
|
|
85
|
+
protected stripProtoKeys(value: any): void;
|
|
86
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { __decorate, __metadata, __param } from "tslib";
|
|
2
|
+
import { types } from 'util';
|
|
3
|
+
import { Injectable } from '../decorators/core/injectable.decorator.js';
|
|
4
|
+
import { Optional } from '../decorators/core/optional.decorator.js';
|
|
5
|
+
import { HttpStatus } from '../enums/http-status.enum.js';
|
|
6
|
+
import { HttpErrorByCode, } from '../utils/http-error-by-code.util.js';
|
|
7
|
+
/**
|
|
8
|
+
* Built-in JavaScript types that should be excluded from prototype stripping
|
|
9
|
+
* to avoid conflicts with test frameworks like Jest's useFakeTimers
|
|
10
|
+
*/
|
|
11
|
+
const BUILT_IN_TYPES = [Date, RegExp, Error, Map, Set, WeakMap, WeakSet];
|
|
12
|
+
/**
|
|
13
|
+
* Defines the built-in StandardSchemaValidation Pipe.
|
|
14
|
+
*
|
|
15
|
+
* Uses a standard schema object (conforming to the Standard Schema spec)
|
|
16
|
+
* attached to the parameter metadata to validate incoming values.
|
|
17
|
+
*
|
|
18
|
+
* @see [Standard Schema](https://github.com/standard-schema/standard-schema)
|
|
19
|
+
*
|
|
20
|
+
* @publicApi
|
|
21
|
+
*/
|
|
22
|
+
let StandardSchemaValidationPipe = class StandardSchemaValidationPipe {
|
|
23
|
+
options;
|
|
24
|
+
isTransformEnabled;
|
|
25
|
+
validateCustomDecorators;
|
|
26
|
+
validateOptions;
|
|
27
|
+
exceptionFactory;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.options = options;
|
|
30
|
+
const { transform = true, validateCustomDecorators = false, validateOptions, exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST, } = options || {};
|
|
31
|
+
this.isTransformEnabled = transform;
|
|
32
|
+
this.validateCustomDecorators = validateCustomDecorators;
|
|
33
|
+
this.validateOptions = validateOptions;
|
|
34
|
+
this.exceptionFactory =
|
|
35
|
+
exceptionFactory ||
|
|
36
|
+
(issues => {
|
|
37
|
+
const messages = issues.map(issue => issue.message);
|
|
38
|
+
return new HttpErrorByCode[errorHttpStatusCode](messages);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Method that validates the incoming value against the standard schema
|
|
43
|
+
* provided in the parameter metadata.
|
|
44
|
+
*
|
|
45
|
+
* @param value currently processed route argument
|
|
46
|
+
* @param metadata contains metadata about the currently processed route argument
|
|
47
|
+
*/
|
|
48
|
+
async transform(value, metadata) {
|
|
49
|
+
const schema = metadata.schema;
|
|
50
|
+
if (!schema || !this.toValidate(metadata)) {
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
this.stripProtoKeys(value);
|
|
54
|
+
const result = await this.validate(value, schema, this.validateOptions);
|
|
55
|
+
if (result.issues) {
|
|
56
|
+
throw this.exceptionFactory(result.issues);
|
|
57
|
+
}
|
|
58
|
+
return this.isTransformEnabled ? result.value : value;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Determines whether validation should be performed for the given metadata.
|
|
62
|
+
* Skips validation for custom decorators unless `validateCustomDecorators` is enabled.
|
|
63
|
+
*
|
|
64
|
+
* @param metadata contains metadata about the currently processed route argument
|
|
65
|
+
* @returns `true` if validation should be performed
|
|
66
|
+
*/
|
|
67
|
+
toValidate(metadata) {
|
|
68
|
+
const { type } = metadata;
|
|
69
|
+
if (type === 'custom' && !this.validateCustomDecorators) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validates a value against a standard schema.
|
|
76
|
+
* Can be overridden to customize validation behavior.
|
|
77
|
+
*
|
|
78
|
+
* @param value The value to validate
|
|
79
|
+
* @param schema The standard schema to validate against
|
|
80
|
+
* @param options Optional options forwarded to the schema's validate method
|
|
81
|
+
* @returns The validation result
|
|
82
|
+
*/
|
|
83
|
+
validate(value, schema, options) {
|
|
84
|
+
return schema['~standard'].validate(value, options);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Strips dangerous prototype pollution keys from an object.
|
|
88
|
+
*/
|
|
89
|
+
stripProtoKeys(value) {
|
|
90
|
+
if (value == null ||
|
|
91
|
+
typeof value !== 'object' ||
|
|
92
|
+
types.isTypedArray(value)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (BUILT_IN_TYPES.some(type => value instanceof type)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
for (const v of value) {
|
|
100
|
+
this.stripProtoKeys(v);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
delete value.__proto__;
|
|
105
|
+
delete value.prototype;
|
|
106
|
+
const constructorType = value?.constructor;
|
|
107
|
+
if (constructorType && !BUILT_IN_TYPES.includes(constructorType)) {
|
|
108
|
+
delete value.constructor;
|
|
109
|
+
}
|
|
110
|
+
for (const key in value) {
|
|
111
|
+
this.stripProtoKeys(value[key]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
StandardSchemaValidationPipe = __decorate([
|
|
116
|
+
Injectable(),
|
|
117
|
+
__param(0, Optional()),
|
|
118
|
+
__metadata("design:paramtypes", [Object])
|
|
119
|
+
], StandardSchemaValidationPipe);
|
|
120
|
+
export { StandardSchemaValidationPipe };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { CallHandler, ExecutionContext, NestInterceptor } from '../interfaces/index.js';
|
|
4
|
+
import { StandardSchemaSerializerContextOptions } from './standard-schema-serializer.interfaces.js';
|
|
5
|
+
interface PlainLiteralObject {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* @publicApi
|
|
10
|
+
*/
|
|
11
|
+
export interface StandardSchemaSerializerInterceptorOptions {
|
|
12
|
+
/**
|
|
13
|
+
* A default standard schema to use for serialization when no schema
|
|
14
|
+
* is provided via `@SerializeOptions()`.
|
|
15
|
+
*/
|
|
16
|
+
schema?: StandardSchemaV1;
|
|
17
|
+
/**
|
|
18
|
+
* Default options forwarded to the schema's `~standard.validate()` call.
|
|
19
|
+
* Can be overridden per-handler via `@SerializeOptions({ validateOptions })`.
|
|
20
|
+
*/
|
|
21
|
+
validateOptions?: StandardSchemaV1.Options;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An interceptor that serializes outgoing responses using a Standard Schema.
|
|
25
|
+
*
|
|
26
|
+
* The schema can be provided either:
|
|
27
|
+
* - As a default option in the interceptor constructor
|
|
28
|
+
* - Per-handler or per-class via `@SerializeOptions({ schema })` decorator
|
|
29
|
+
*
|
|
30
|
+
* When a schema is present, the interceptor validates/transforms the response
|
|
31
|
+
* through the schema's `~standard.validate()` method. If validation fails,
|
|
32
|
+
* the issues are thrown as an error.
|
|
33
|
+
*
|
|
34
|
+
* @see [Standard Schema](https://github.com/standard-schema/standard-schema)
|
|
35
|
+
*
|
|
36
|
+
* @publicApi
|
|
37
|
+
*/
|
|
38
|
+
export declare class StandardSchemaSerializerInterceptor implements NestInterceptor {
|
|
39
|
+
protected readonly reflector: any;
|
|
40
|
+
protected readonly defaultOptions: StandardSchemaSerializerInterceptorOptions;
|
|
41
|
+
constructor(reflector: any, defaultOptions?: StandardSchemaSerializerInterceptorOptions);
|
|
42
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
43
|
+
/**
|
|
44
|
+
* Serializes responses that are non-null objects nor streamable files.
|
|
45
|
+
*/
|
|
46
|
+
serialize(response: PlainLiteralObject | Array<PlainLiteralObject>, schema: StandardSchemaV1 | undefined, validateOptions?: StandardSchemaV1.Options): PlainLiteralObject | Array<PlainLiteralObject> | Promise<PlainLiteralObject | Array<PlainLiteralObject>>;
|
|
47
|
+
transformToPlain(plainOrClass: any, schema: StandardSchemaV1, validateOptions?: StandardSchemaV1.Options): Promise<PlainLiteralObject>;
|
|
48
|
+
protected getContextOptions(context: ExecutionContext): StandardSchemaSerializerContextOptions | undefined;
|
|
49
|
+
}
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { __decorate, __metadata, __param } from "tslib";
|
|
2
|
+
import { map } from 'rxjs/operators';
|
|
3
|
+
import { Inject, Injectable, Optional } from '../decorators/core/index.js';
|
|
4
|
+
import { StreamableFile } from '../file-stream/index.js';
|
|
5
|
+
import { isObject } from '../utils/shared.utils.js';
|
|
6
|
+
import { CLASS_SERIALIZER_OPTIONS } from './class-serializer.constants.js';
|
|
7
|
+
// NOTE (external)
|
|
8
|
+
// We need to deduplicate them here due to the circular dependency
|
|
9
|
+
// between core and common packages
|
|
10
|
+
const REFLECTOR = 'Reflector';
|
|
11
|
+
/**
|
|
12
|
+
* An interceptor that serializes outgoing responses using a Standard Schema.
|
|
13
|
+
*
|
|
14
|
+
* The schema can be provided either:
|
|
15
|
+
* - As a default option in the interceptor constructor
|
|
16
|
+
* - Per-handler or per-class via `@SerializeOptions({ schema })` decorator
|
|
17
|
+
*
|
|
18
|
+
* When a schema is present, the interceptor validates/transforms the response
|
|
19
|
+
* through the schema's `~standard.validate()` method. If validation fails,
|
|
20
|
+
* the issues are thrown as an error.
|
|
21
|
+
*
|
|
22
|
+
* @see [Standard Schema](https://github.com/standard-schema/standard-schema)
|
|
23
|
+
*
|
|
24
|
+
* @publicApi
|
|
25
|
+
*/
|
|
26
|
+
let StandardSchemaSerializerInterceptor = class StandardSchemaSerializerInterceptor {
|
|
27
|
+
reflector;
|
|
28
|
+
defaultOptions;
|
|
29
|
+
constructor(reflector, defaultOptions = {}) {
|
|
30
|
+
this.reflector = reflector;
|
|
31
|
+
this.defaultOptions = defaultOptions;
|
|
32
|
+
}
|
|
33
|
+
intercept(context, next) {
|
|
34
|
+
const contextOptions = this.getContextOptions(context);
|
|
35
|
+
const schema = contextOptions?.schema ?? this.defaultOptions.schema;
|
|
36
|
+
const validateOptions = contextOptions?.validateOptions ?? this.defaultOptions.validateOptions;
|
|
37
|
+
return next
|
|
38
|
+
.handle()
|
|
39
|
+
.pipe(map((res) => this.serialize(res, schema, validateOptions)));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Serializes responses that are non-null objects nor streamable files.
|
|
43
|
+
*/
|
|
44
|
+
serialize(response, schema, validateOptions) {
|
|
45
|
+
if (!schema || !isObject(response) || response instanceof StreamableFile) {
|
|
46
|
+
return response;
|
|
47
|
+
}
|
|
48
|
+
return Array.isArray(response)
|
|
49
|
+
? Promise.all(response.map(item => this.transformToPlain(item, schema, validateOptions)))
|
|
50
|
+
: this.transformToPlain(response, schema, validateOptions);
|
|
51
|
+
}
|
|
52
|
+
async transformToPlain(plainOrClass, schema, validateOptions) {
|
|
53
|
+
if (!plainOrClass) {
|
|
54
|
+
return plainOrClass;
|
|
55
|
+
}
|
|
56
|
+
const result = await schema['~standard'].validate(plainOrClass, validateOptions);
|
|
57
|
+
if (result.issues) {
|
|
58
|
+
throw new Error(`Serialization failed: ${result.issues.map(i => i.message).join(', ')}`);
|
|
59
|
+
}
|
|
60
|
+
return result.value;
|
|
61
|
+
}
|
|
62
|
+
getContextOptions(context) {
|
|
63
|
+
return this.reflector.getAllAndOverride(CLASS_SERIALIZER_OPTIONS, [
|
|
64
|
+
context.getHandler(),
|
|
65
|
+
context.getClass(),
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
StandardSchemaSerializerInterceptor = __decorate([
|
|
70
|
+
Injectable(),
|
|
71
|
+
__param(0, Inject(REFLECTOR)),
|
|
72
|
+
__param(1, Optional()),
|
|
73
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
74
|
+
], StandardSchemaSerializerInterceptor);
|
|
75
|
+
export { StandardSchemaSerializerInterceptor };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
/**
|
|
3
|
+
* Options for the `StandardSchemaSerializerInterceptor`, passed via
|
|
4
|
+
* `@SerializeOptions({ schema })`.
|
|
5
|
+
*
|
|
6
|
+
* @publicApi
|
|
7
|
+
*/
|
|
8
|
+
export interface StandardSchemaSerializerContextOptions {
|
|
9
|
+
/**
|
|
10
|
+
* A standard schema to use for serialization.
|
|
11
|
+
* Used by `StandardSchemaSerializerInterceptor` to validate/transform the response.
|
|
12
|
+
*/
|
|
13
|
+
schema?: StandardSchemaV1;
|
|
14
|
+
/**
|
|
15
|
+
* Optional options forwarded to the schema's `~standard.validate()` call.
|
|
16
|
+
*/
|
|
17
|
+
validateOptions?: StandardSchemaV1.Options;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|