UNPKG

@nestia/core

Version:

Super-fast validation decorators of NestJS

243 lines (236 loc) 8.46 kB
import { IRequestBodyValidator } from "../options/IRequestBodyValidator"; import { IRequestQueryValidator } from "../options/IRequestQueryValidator"; import { NoTransformConfigurationError } from "./NoTransformConfigurationError"; import { IWebSocketRouteReflect } from "./internal/IWebSocketRouteReflect"; import { validate_request_body } from "./internal/validate_request_body"; import { validate_request_query } from "./internal/validate_request_query"; /** * WebSocket route decorator. * * `@WebSocketRoute()` is a route decorator function for WebSocket routes. If * you want to define a WebSocket route with this `@WebSocketRoute` decorator, * please don't forget to call the {@link WebSocketAdaptor.upgrade} function to * the {@link INestApplication} instance. * * Also, `WebSocketRoute` is a module containing parameter decorator functions * of below for the `@WebSocketRoute` decorated method, at the same time. Note * that, every parameters must be decorated by one of the parameter decorators * in the `WebSocketRoute` module. One thing more important is, * {@link WebSocketRoute.Acceptor} decorated parameter must be defined in the * method. If not, it would be both compilation/runtime error. * * - {@link WebSocketRoute.Acceptor} * - {@link WebSocketRoute.Driver} * - {@link WebSocketRoute.Header} * - {@link WebSocketRoute.Param} * - {@link WebSocketRoute.Query} * * For reference, key difference between `@WebSocketGateway()` of NestJS and * `@WebSocketRoute()` of Nestia is, `@WebSocketRoute()` can make multiple * WebSocket routes by configuring _paths_, besides `@WebSocketGateway()` can't * do it. * * Furthermore, if you build SDK (Software Development Kit) library through * `@nestia/sdk`, you can make safe WebSocket client taking advantages of * TypeScript type hints and checks. * * @author Jeongho Nam - https://github.com/samchon * @param path Path(s) of the WebSocket request * @returns Method decorator */ export function WebSocketRoute( path?: undefined | string | string[], ): MethodDecorator { return function WebSocketRoute( _target: Object, _propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>, ): TypedPropertyDescriptor<any> { Reflect.defineMetadata( "nestia/WebSocketRoute", { paths: path === undefined ? [] : Array.isArray(path) ? path : [path], } satisfies IWebSocketRouteReflect, descriptor.value, ); return descriptor; }; } export namespace WebSocketRoute { /** * Acceptor parameter decorator. * * `@WebSocketRoute.Acceptor()` is a parameter decorator function for the * `WebSocketAcceptor<Header, Provider, Listener>` (of `tgrid`) typed * parameter. * * In the controller method decorated by `@WebSocketRoute()` and * `@WebSocketRoute.Acceptor()`, call {@link WebSocketAcceptor.accept} function * with `Provider` instance when you want to accept the WebSocket client * connection. Otherwise you want to reject the connection, call * {@link WebSocketAcceptor.rejcet} function instead. * * For reference, this `@WebSocketRoute.Acceptor()` parameter decorator is * essential for the method decorated by `@WebSocketRoute()` decorator. If you * forget it, it would be both compilation/runtime error. */ export function Acceptor(): ParameterDecorator { return function WebSocketAcceptor( target: Object, propertyKey: string | symbol | undefined, parameterIndex: number, ) { emplace(target, propertyKey ?? "", { category: "acceptor", index: parameterIndex, }); }; } /** * Driver parameter decorator. * * `@WebSocketRoute.Driver()` is a parameter decorator function for the * `Driver<Listener>` (of `tgrid`) typed parameter. * * With the `@WebSocketRoute.Driver()` decorated parameter, you can call * function of `Listener` typed instance provided by remote WebSocket client * by calling the `Driver<Listener>` instance. * * For reference, this `@WebSocketRoute.Driver()` decorator is optional, and * can be substituted by `@WebSocketRoute.Acceptor()` decorated parameter by * calling the {@link WebSocketAcceptor.getDriver} function. */ export function Driver(): ParameterDecorator { return function WebSocketDriver( target: Object, propertyKey: string | symbol | undefined, parameterIndex: number, ) { emplace(target, propertyKey ?? "", { category: "driver", index: parameterIndex, }); }; } /** * Header decorator. * * `@WebSocketRoute.Header()` is a parameter decorator function for the * WebSocket header with type casting and assertion. * * For reference, `@WebSocketRoute.Header()` is different with HTTP headers. * It's for WebSocket protocol, especially for TGrid's * {@link WebSocketConnector} and {@link WebSocketAcceptor}'s special header. * * Also, this `@WebSocketRoute.Header()` decorator is optional, and can be * substituted by `@WebSocketRoute.Acceptor()` decorated parameter by * accessting to the {@link WebSocketAcceptor.header} property. */ export function Header<T>( validator?: IRequestBodyValidator<T>, ): ParameterDecorator { const validate = validate_request_body("WebSocketRoute.Header")(validator); return function WebSocketHeader( target: Object, propertyKey: string | symbol | undefined, parameterIndex: number, ) { emplace(target, propertyKey ?? "", { category: "header", index: parameterIndex, validate, }); }; } /** * URL parameter decorator. * * `@WebSocketRoute.Param()` is a parameter decorator function for the URL * parameter with type casting and assertion. * * It's almost same with the {@link TypedParam}, but `@WebSocketRoute.Param()` * is only for WebSocket protocol router function decorated by * {@link WebSocketRoute}. * * @param field URL parameter field name */ export function Param<T extends boolean | bigint | number | string | null>( field: string, assert?: (value: string) => T, ): ParameterDecorator { if (assert === undefined) { NoTransformConfigurationError("WebSocketRoute.Param"); assert = (value) => value as T; } return function WebSocketParam( target: Object, propertyKey: string | symbol | undefined, parameterIndex: number, ) { emplace(target, propertyKey ?? "", { category: "param", index: parameterIndex, field, assert, }); }; } /** * URL query decorator. * * `@WebSocketRoute.Query()` is a parameter decorator function for the URL * query string with type casting and assertion. * * It is almost same with {@link TypedQuery}, but `@WebSocketRoute.Query()` is * only for WebSocket protocol router function decorated by * {@link WebSocketRoute}. * * For reference, as same with {@link TypedQuery}, `@WebSocketRoute.Query()` * has same restriction for the target type `T`. If actual URL query parameter * values are different with their promised type `T`, it would be runtime * error. * * 1. Type `T` must be an object type * 2. Do not allow dynamic property * 3. Only `boolean`, `bigint`, `number`, `string` or their array types are * allowed * 4. By the way, union type never be not allowed */ export function Query<T extends object>( validator?: IRequestQueryValidator<T>, ): ParameterDecorator { const validate = validate_request_query("WebSocketRoute.Query")(validator); return function WebSocketQuery( target: Object, propertyKey: string | symbol | undefined, parameterIndex: number, ) { emplace(target, propertyKey ?? "", { category: "query", index: parameterIndex, validate, }); }; } /** @internal */ const emplace = ( target: Object, propertyKey: string | symbol, value: IWebSocketRouteReflect.IArgument, ) => { const array: IWebSocketRouteReflect.IArgument[] | undefined = Reflect.getMetadata( "nestia/WebSocketRoute/Parameters", target, propertyKey, ); if (array !== undefined) array.push(value); else Reflect.defineMetadata( "nestia/WebSocketRoute/Parameters", [value], target, propertyKey, ); }; }