🎯 MapView v2.0 - Global Deployment Ready

 MAJOR FEATURES:
• Auto-zoom intelligence với smart bounds fitting
• Enhanced 3D GPS markers với pulsing effects
• Professional route display với 6-layer rendering
• Status-based parking icons với availability indicators
• Production-ready build optimizations

🗺️ AUTO-ZOOM FEATURES:
• Smart bounds fitting cho GPS + selected parking
• Adaptive padding (50px) cho visual balance
• Max zoom control (level 16) để tránh quá gần
• Dynamic centering khi không có selection

🎨 ENHANCED VISUALS:
• 3D GPS marker với multi-layer pulse effects
• Advanced parking icons với status colors
• Selection highlighting với animation
• Dimming system cho non-selected items

🛣️ ROUTE SYSTEM:
• OpenRouteService API integration
• Multi-layer route rendering (glow, shadow, main, animated)
• Real-time distance & duration calculation
• Visual route info trong popup

📱 PRODUCTION READY:
• SSR safe với dynamic imports
• Build errors resolved
• Global deployment via Vercel
• Optimized performance

🌍 DEPLOYMENT:
• Vercel: https://whatever-ctk2auuxr-phong12hexdockworks-projects.vercel.app
• Bundle size: 22.8 kB optimized
• Global CDN distribution
• HTTPS enabled

💾 VERSION CONTROL:
• MapView-v2.0.tsx backup created
• MAPVIEW_VERSIONS.md documentation
• Full version history tracking
This commit is contained in:
2025-07-20 19:52:16 +07:00
parent 3203463a6a
commit c65cc97a33
64624 changed files with 7199453 additions and 6462 deletions

22
backend/node_modules/@nestjs/websockets/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017-2024 Kamil Mysliwiec <https://kamilmysliwiec.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

151
backend/node_modules/@nestjs/websockets/Readme.md generated vendored Normal file
View File

@@ -0,0 +1,151 @@
<p align="center">
<a href="https://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="https://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also provides compatibility with a wide range of other libraries, like <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad of third-party plugins which are available.</p>
## Philosophy
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend 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>
<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>
## Getting started
- To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
- 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
- [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
- [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
## Questions
For questions and support please use the official [Discord channel](https://discord.gg/G7Qnnhy). The issue list of this repo is **exclusively** for bug reports and feature requests.
## Issues
Please make sure to read the [Issue Reporting Checklist](https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md#-submitting-an-issue) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
## Consulting
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
#### Principal Sponsors
<table style="text-align:center;">
<tr>
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
<td><a href="https://marblism.com?utm_source=nest" target="_blank"><img src="https://nestjs.com/img/logos/marblism-logo.png" width="180" valign="middle" /></a></td>
<td><a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
<td><a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/logos/amplication-logo.svg" width="190" valign="middle" /></a></td>
</tr>
</table>
#### Gold Sponsors
<table style="text-align:center;">
<tr>
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
</tr>
<tr>
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
<td><a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/logos/fuse-logo.svg" width="105" valign="middle" /></a></td>
<td><a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/logos/ridi-logo.svg" width="105" valign="middle" /></a></td>
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
<td><a href="https://skunk.team" target="_blank"><img src="https://nestjs.com/img/logos/skunk-logo.png" height="60" valign="middle" /></a></td>
</tr>
</table>
#### Silver Sponsors
<table style="text-align:center;">
<tr>
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
<td><a href="https://bloodycase.com/?promocode=NEST" target="_blank"><img src="https://nestjs.com/img/logos/bloodycase-logo.png" width="65" valign="middle" /></a></td>
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-dark-logo.svg#2" width="150" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
</tr>
</table>
#### Sponsors
<table>
<tr>
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/swingdev-logo.svg#1" width="110" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/novologic.png" width="110" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/mantro-logo.svg" width="95" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/logos/triplebyte.png" width="107" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
</tr>
<tr>
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
</tr>
<tr>
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/logos/casinowizard-logo.png" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
</tr>
<tr>
<td align="center" valign="middle"><a href="https://julienferand.dev/" target="_blank"><img src="https://nestjs.com/img/logos/julienferand-logo.jpeg" width="55" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.slotsup.com/" target="_blank"><img src="https://nestjs.com/img/logos/slotsup-logo.png" width="60" valign="middle" /></a></td>
</tr>
</table>
## Backers
<a href="https://opencollective.com/nest" target="_blank"><img src="https://opencollective.com/nest/backers.svg?width=1000"></a>
## Stay in touch
- Author - [Kamil Myśliwiec](https://x.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- X - [@nestframework](https://x.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).

View File

@@ -0,0 +1 @@
export * from './ws-adapter';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./ws-adapter"), exports);

View File

@@ -0,0 +1,20 @@
import { INestApplicationContext, WebSocketAdapter } from '@nestjs/common';
import { WsMessageHandler } from '@nestjs/common/interfaces';
import { Observable } from 'rxjs';
export interface BaseWsInstance {
on: (event: string, callback: Function) => void;
close: Function;
}
export declare abstract class AbstractWsAdapter<TServer extends BaseWsInstance = any, TClient extends BaseWsInstance = any, TOptions = any> implements WebSocketAdapter<TServer, TClient, TOptions> {
protected readonly httpServer: any;
private _forceCloseConnections;
set forceCloseConnections(value: boolean);
get forceCloseConnections(): boolean;
constructor(appOrHttpServer?: INestApplicationContext | any);
bindClientConnect(server: TServer, callback: Function): void;
bindClientDisconnect(client: TClient, callback: Function): void;
close(server: TServer): Promise<void>;
dispose(): Promise<void>;
abstract create(port: number, options?: TOptions): TServer;
abstract bindMessageHandlers(client: TClient, handlers: WsMessageHandler[], transform: (data: any) => Observable<any>): any;
}

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbstractWsAdapter = void 0;
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const core_1 = require("@nestjs/core");
const constants_1 = require("../constants");
class AbstractWsAdapter {
set forceCloseConnections(value) {
this._forceCloseConnections = value;
}
get forceCloseConnections() {
return this._forceCloseConnections;
}
constructor(appOrHttpServer) {
if (appOrHttpServer && appOrHttpServer instanceof core_1.NestApplication) {
this.httpServer = appOrHttpServer.getUnderlyingHttpServer();
}
else {
this.httpServer = appOrHttpServer;
}
}
bindClientConnect(server, callback) {
server.on(constants_1.CONNECTION_EVENT, callback);
}
bindClientDisconnect(client, callback) {
client.on(constants_1.DISCONNECT_EVENT, callback);
}
async close(server) {
const isCallable = server && (0, shared_utils_1.isFunction)(server.close);
isCallable && (await new Promise(resolve => server.close(resolve)));
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
async dispose() { }
}
exports.AbstractWsAdapter = AbstractWsAdapter;

12
backend/node_modules/@nestjs/websockets/constants.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export declare const MESSAGE_MAPPING_METADATA = "websockets:message_mapping";
export declare const MESSAGE_METADATA = "message";
export declare const GATEWAY_SERVER_METADATA = "websockets:is_socket";
export declare const GATEWAY_METADATA = "websockets:is_gateway";
export declare const NAMESPACE_METADATA = "namespace";
export declare const PORT_METADATA = "port";
export declare const GATEWAY_OPTIONS = "websockets:gateway_options";
export declare const PARAM_ARGS_METADATA = "__routeArguments__";
export declare const CONNECTION_EVENT = "connection";
export declare const DISCONNECT_EVENT = "disconnect";
export declare const CLOSE_EVENT = "close";
export declare const ERROR_EVENT = "error";

16
backend/node_modules/@nestjs/websockets/constants.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERROR_EVENT = exports.CLOSE_EVENT = exports.DISCONNECT_EVENT = exports.CONNECTION_EVENT = exports.PARAM_ARGS_METADATA = exports.GATEWAY_OPTIONS = exports.PORT_METADATA = exports.NAMESPACE_METADATA = exports.GATEWAY_METADATA = exports.GATEWAY_SERVER_METADATA = exports.MESSAGE_METADATA = exports.MESSAGE_MAPPING_METADATA = void 0;
const constants_1 = require("@nestjs/common/constants");
exports.MESSAGE_MAPPING_METADATA = 'websockets:message_mapping';
exports.MESSAGE_METADATA = 'message';
exports.GATEWAY_SERVER_METADATA = 'websockets:is_socket';
exports.GATEWAY_METADATA = 'websockets:is_gateway';
exports.NAMESPACE_METADATA = 'namespace';
exports.PORT_METADATA = 'port';
exports.GATEWAY_OPTIONS = 'websockets:gateway_options';
exports.PARAM_ARGS_METADATA = constants_1.ROUTE_ARGS_METADATA;
exports.CONNECTION_EVENT = 'connection';
exports.DISCONNECT_EVENT = 'disconnect';
exports.CLOSE_EVENT = 'close';
exports.ERROR_EVENT = 'error';

View File

@@ -0,0 +1,11 @@
import { BaseExceptionFilterContext } from '@nestjs/core/exceptions/base-exception-filter-context';
import { NestContainer } from '@nestjs/core/injector/container';
import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler';
/**
* @publicApi
*/
export declare class ExceptionFiltersContext extends BaseExceptionFilterContext {
constructor(container: NestContainer);
create(instance: object, callback: <TClient>(client: TClient, data: any) => any, moduleKey: string): WsExceptionsHandler;
getGlobalMetadata<T extends any[]>(): T;
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExceptionFiltersContext = void 0;
const constants_1 = require("@nestjs/common/constants");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const base_exception_filter_context_1 = require("@nestjs/core/exceptions/base-exception-filter-context");
const ws_exceptions_handler_1 = require("../exceptions/ws-exceptions-handler");
/**
* @publicApi
*/
class ExceptionFiltersContext extends base_exception_filter_context_1.BaseExceptionFilterContext {
constructor(container) {
super(container);
}
create(instance, callback, moduleKey) {
this.moduleContext = moduleKey;
const exceptionHandler = new ws_exceptions_handler_1.WsExceptionsHandler();
const filters = this.createContext(instance, callback, constants_1.EXCEPTION_FILTERS_METADATA);
if ((0, shared_utils_1.isEmpty)(filters)) {
return exceptionHandler;
}
exceptionHandler.setCustomFilters(filters.reverse());
return exceptionHandler;
}
getGlobalMetadata() {
return [];
}
}
exports.ExceptionFiltersContext = ExceptionFiltersContext;

View File

@@ -0,0 +1,48 @@
import { ContextType, Controller, PipeTransform } from '@nestjs/common/interfaces';
import { GuardsConsumer } from '@nestjs/core/guards/guards-consumer';
import { GuardsContextCreator } from '@nestjs/core/guards/guards-context-creator';
import { ParamProperties } from '@nestjs/core/helpers/context-utils';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { ParamsMetadata } from '@nestjs/core/helpers/interfaces';
import { InterceptorsConsumer, InterceptorsContextCreator } from '@nestjs/core/interceptors';
import { PipesConsumer, PipesContextCreator } from '@nestjs/core/pipes';
import { WsParamsFactory } from '../factories/ws-params-factory';
import { ExceptionFiltersContext } from './exception-filters-context';
import { WsProxy } from './ws-proxy';
type WsParamProperties = ParamProperties & {
metatype?: any;
};
export interface WsHandlerMetadata {
argsLength: number;
paramtypes: any[];
getParamsMetadata: (moduleKey: string) => WsParamProperties[];
}
export declare class WsContextCreator {
private readonly wsProxy;
private readonly exceptionFiltersContext;
private readonly pipesContextCreator;
private readonly pipesConsumer;
private readonly guardsContextCreator;
private readonly guardsConsumer;
private readonly interceptorsContextCreator;
private readonly interceptorsConsumer;
private readonly contextUtils;
private readonly wsParamsFactory;
private readonly handlerMetadataStorage;
constructor(wsProxy: WsProxy, exceptionFiltersContext: ExceptionFiltersContext, pipesContextCreator: PipesContextCreator, pipesConsumer: PipesConsumer, guardsContextCreator: GuardsContextCreator, guardsConsumer: GuardsConsumer, interceptorsContextCreator: InterceptorsContextCreator, interceptorsConsumer: InterceptorsConsumer);
create<T extends ParamsMetadata = ParamsMetadata>(instance: Controller, callback: (...args: unknown[]) => void, moduleKey: string, methodName: string): (...args: any[]) => Promise<void>;
reflectCallbackParamtypes(instance: Controller, callback: (...args: any[]) => any): any[];
reflectCallbackPattern(callback: (...args: any[]) => any): string;
createGuardsFn<TContext extends string = ContextType>(guards: any[], instance: Controller, callback: (...args: unknown[]) => any, contextType?: TContext): Function | null;
getMetadata<TMetadata, TContext extends ContextType = ContextType>(instance: Controller, methodName: string, contextType: TContext): WsHandlerMetadata;
exchangeKeysForValues<TMetadata = any>(keys: string[], metadata: TMetadata, moduleContext: string, paramsFactory: WsParamsFactory, contextFactory: (args: unknown[]) => ExecutionContextHost): ParamProperties[];
createPipesFn(pipes: PipeTransform[], paramsOptions: (ParamProperties & {
metatype?: unknown;
})[]): (args: unknown[], ...params: unknown[]) => Promise<void>;
getParamValue<T>(value: T, { metatype, type, data }: {
metatype: any;
type: any;
data: any;
}, pipes: PipeTransform[]): Promise<any>;
}
export {};

View File

@@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsContextCreator = void 0;
const constants_1 = require("@nestjs/common/constants");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const constants_2 = require("@nestjs/core/guards/constants");
const context_utils_1 = require("@nestjs/core/helpers/context-utils");
const handler_metadata_storage_1 = require("@nestjs/core/helpers/handler-metadata-storage");
const constants_3 = require("../constants");
const ws_exception_1 = require("../errors/ws-exception");
const ws_params_factory_1 = require("../factories/ws-params-factory");
const ws_metadata_constants_1 = require("./ws-metadata-constants");
class WsContextCreator {
constructor(wsProxy, exceptionFiltersContext, pipesContextCreator, pipesConsumer, guardsContextCreator, guardsConsumer, interceptorsContextCreator, interceptorsConsumer) {
this.wsProxy = wsProxy;
this.exceptionFiltersContext = exceptionFiltersContext;
this.pipesContextCreator = pipesContextCreator;
this.pipesConsumer = pipesConsumer;
this.guardsContextCreator = guardsContextCreator;
this.guardsConsumer = guardsConsumer;
this.interceptorsContextCreator = interceptorsContextCreator;
this.interceptorsConsumer = interceptorsConsumer;
this.contextUtils = new context_utils_1.ContextUtils();
this.wsParamsFactory = new ws_params_factory_1.WsParamsFactory();
this.handlerMetadataStorage = new handler_metadata_storage_1.HandlerMetadataStorage();
}
create(instance, callback, moduleKey, methodName) {
const contextType = 'ws';
const { argsLength, paramtypes, getParamsMetadata } = this.getMetadata(instance, methodName, contextType);
const exceptionHandler = this.exceptionFiltersContext.create(instance, callback, moduleKey);
const pipes = this.pipesContextCreator.create(instance, callback, moduleKey);
const guards = this.guardsContextCreator.create(instance, callback, moduleKey);
const interceptors = this.interceptorsContextCreator.create(instance, callback, moduleKey);
const paramsMetadata = getParamsMetadata(moduleKey);
const paramsOptions = paramsMetadata
? this.contextUtils.mergeParamsMetatypes(paramsMetadata, paramtypes)
: [];
const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);
const fnCanActivate = this.createGuardsFn(guards, instance, callback, contextType);
const handler = (initialArgs, args) => async () => {
if (fnApplyPipes) {
await fnApplyPipes(initialArgs, ...args);
return callback.apply(instance, initialArgs);
}
return callback.apply(instance, args);
};
const targetPattern = this.reflectCallbackPattern(callback);
return this.wsProxy.create(async (...args) => {
args.push(targetPattern);
const initialArgs = this.contextUtils.createNullArray(argsLength);
fnCanActivate && (await fnCanActivate(args));
return this.interceptorsConsumer.intercept(interceptors, args, instance, callback, handler(initialArgs, args), contextType);
}, exceptionHandler, targetPattern);
}
reflectCallbackParamtypes(instance, callback) {
return Reflect.getMetadata(constants_1.PARAMTYPES_METADATA, instance, callback.name);
}
reflectCallbackPattern(callback) {
return Reflect.getMetadata(constants_3.MESSAGE_METADATA, callback);
}
createGuardsFn(guards, instance, callback, contextType) {
const canActivateFn = async (args) => {
const canActivate = await this.guardsConsumer.tryActivate(guards, args, instance, callback, contextType);
if (!canActivate) {
throw new ws_exception_1.WsException(constants_2.FORBIDDEN_MESSAGE);
}
};
return guards.length ? canActivateFn : null;
}
getMetadata(instance, methodName, contextType) {
const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);
if (cacheMetadata) {
return cacheMetadata;
}
const metadata = this.contextUtils.reflectCallbackMetadata(instance, methodName, constants_3.PARAM_ARGS_METADATA) || ws_metadata_constants_1.DEFAULT_CALLBACK_METADATA;
const keys = Object.keys(metadata);
const argsLength = this.contextUtils.getArgumentsLength(keys, metadata);
const paramtypes = this.contextUtils.reflectCallbackParamtypes(instance, methodName);
const contextFactory = this.contextUtils.getContextFactory(contextType, instance, instance[methodName]);
const getParamsMetadata = (moduleKey) => this.exchangeKeysForValues(keys, metadata, moduleKey, this.wsParamsFactory, contextFactory);
const handlerMetadata = {
argsLength,
paramtypes,
getParamsMetadata,
};
this.handlerMetadataStorage.set(instance, methodName, handlerMetadata);
return handlerMetadata;
}
exchangeKeysForValues(keys, metadata, moduleContext, paramsFactory, contextFactory) {
this.pipesContextCreator.setModuleContext(moduleContext);
return keys.map(key => {
const { index, data, pipes: pipesCollection } = metadata[key];
const pipes = this.pipesContextCreator.createConcreteContext(pipesCollection);
const type = this.contextUtils.mapParamType(key);
if (key.includes(constants_1.CUSTOM_ROUTE_ARGS_METADATA)) {
const { factory } = metadata[key];
const customExtractValue = this.contextUtils.getCustomFactory(factory, data, contextFactory);
return { index, extractValue: customExtractValue, type, data, pipes };
}
const numericType = Number(type);
const extractValue = (...args) => paramsFactory.exchangeKeyForValue(numericType, data, args);
return { index, extractValue, type: numericType, data, pipes };
});
}
createPipesFn(pipes, paramsOptions) {
const pipesFn = async (args, ...params) => {
const resolveParamValue = async (param) => {
const { index, extractValue, type, data, metatype, pipes: paramPipes, } = param;
const value = extractValue(...params);
args[index] = await this.getParamValue(value, { metatype, type, data }, pipes.concat(paramPipes));
};
await Promise.all(paramsOptions.map(resolveParamValue));
};
return paramsOptions.length ? pipesFn : null;
}
async getParamValue(value, { metatype, type, data }, pipes) {
return (0, shared_utils_1.isEmpty)(pipes)
? value
: this.pipesConsumer.apply(value, { metatype, type, data }, pipes);
}
}
exports.WsContextCreator = WsContextCreator;

View File

@@ -0,0 +1,12 @@
export declare const DEFAULT_CALLBACK_METADATA: {
"3:1": {
index: number;
data: any;
pipes: any[];
};
"0:0": {
index: number;
data: any;
pipes: any[];
};
};

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_CALLBACK_METADATA = void 0;
const ws_paramtype_enum_1 = require("../enums/ws-paramtype.enum");
exports.DEFAULT_CALLBACK_METADATA = {
[`${ws_paramtype_enum_1.WsParamtype.PAYLOAD}:1`]: { index: 1, data: undefined, pipes: [] },
[`${ws_paramtype_enum_1.WsParamtype.SOCKET}:0`]: { index: 0, data: undefined, pipes: [] },
};

View File

@@ -0,0 +1,5 @@
import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler';
export declare class WsProxy {
create(targetCallback: (...args: unknown[]) => Promise<any>, exceptionsHandler: WsExceptionsHandler, targetPattern?: string): (...args: unknown[]) => Promise<any>;
handleError<T>(exceptionsHandler: WsExceptionsHandler, args: unknown[], error: T): void;
}

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsProxy = void 0;
const execution_context_host_1 = require("@nestjs/core/helpers/execution-context-host");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
class WsProxy {
create(targetCallback, exceptionsHandler, targetPattern) {
return async (...args) => {
args = [...args, targetPattern ?? 'unknown'];
try {
const result = await targetCallback(...args);
return !(0, rxjs_1.isObservable)(result)
? result
: result.pipe((0, operators_1.catchError)(error => {
this.handleError(exceptionsHandler, args, error);
return rxjs_1.EMPTY;
}));
}
catch (error) {
this.handleError(exceptionsHandler, args, error);
}
};
}
handleError(exceptionsHandler, args, error) {
const host = new execution_context_host_1.ExecutionContextHost(args);
host.setType('ws');
exceptionsHandler.handle(error, host);
}
}
exports.WsProxy = WsProxy;

View File

@@ -0,0 +1,4 @@
/**
* @publicApi
*/
export declare const ConnectedSocket: () => ParameterDecorator;

View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectedSocket = void 0;
const ws_paramtype_enum_1 = require("../enums/ws-paramtype.enum");
const param_utils_1 = require("../utils/param.utils");
/**
* @publicApi
*/
exports.ConnectedSocket = (0, param_utils_1.createWsParamDecorator)(ws_paramtype_enum_1.WsParamtype.SOCKET);

View File

@@ -0,0 +1,6 @@
/**
* Attaches native Web Socket Server to a given property.
*
* @publicApi
*/
export declare const WebSocketServer: () => PropertyDecorator;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketServer = void 0;
const constants_1 = require("../constants");
/**
* Attaches native Web Socket Server to a given property.
*
* @publicApi
*/
const WebSocketServer = () => {
return (target, propertyKey) => {
Reflect.set(target, propertyKey, null);
Reflect.defineMetadata(constants_1.GATEWAY_SERVER_METADATA, true, target, propertyKey);
};
};
exports.WebSocketServer = WebSocketServer;

View File

@@ -0,0 +1,5 @@
export * from './connected-socket.decorator';
export * from './gateway-server.decorator';
export * from './message-body.decorator';
export * from './socket-gateway.decorator';
export * from './subscribe-message.decorator';

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./connected-socket.decorator"), exports);
tslib_1.__exportStar(require("./gateway-server.decorator"), exports);
tslib_1.__exportStar(require("./message-body.decorator"), exports);
tslib_1.__exportStar(require("./socket-gateway.decorator"), exports);
tslib_1.__exportStar(require("./subscribe-message.decorator"), exports);

View File

@@ -0,0 +1,45 @@
import { PipeTransform, Type } from '@nestjs/common';
/**
* WebSockets message body parameter decorator.
*
* @publicApi
*/
export declare function MessageBody(): ParameterDecorator;
/**
* WebSockets message body parameter decorator.
*
* Example:
* ```typescript
* create(@MessageBody(new ValidationPipe()) createDto: CreateCatDto)
* ```
* @param pipes one or more pipes - either instances or classes - to apply to
* the bound parameter.
*
* @publicApi
*/
export declare function MessageBody(...pipes: (Type<PipeTransform> | PipeTransform)[]): ParameterDecorator;
/**
* WebSockets message body parameter decorator. Extracts a property from the
* message payload object. May also apply pipes to the bound parameter.
*
* For example, extracting all params:
* ```typescript
* findMany(@MessageBody() ids: string[])
* ```
*
* For example, extracting a single param:
* ```typescript
* create(@MessageBody('data') createDto: { data: string })
* ```
*
* For example, extracting a single param with pipe:
* ```typescript
* create(@MessageBody('data', new ValidationPipe()) createDto: { data: string })
* ```
* @param propertyKey name of single property to extract from the message payload
* @param pipes one or more pipes - either instances or classes - to apply to
* the bound parameter.
*
* @publicApi
*/
export declare function MessageBody(propertyKey: string, ...pipes: (Type<PipeTransform> | PipeTransform)[]): ParameterDecorator;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageBody = MessageBody;
const ws_paramtype_enum_1 = require("../enums/ws-paramtype.enum");
const param_utils_1 = require("../utils/param.utils");
function MessageBody(propertyOrPipe, ...pipes) {
return (0, param_utils_1.createPipesWsParamDecorator)(ws_paramtype_enum_1.WsParamtype.PAYLOAD)(propertyOrPipe, ...pipes);
}

View File

@@ -0,0 +1,10 @@
import { GatewayMetadata } from '../interfaces';
/**
* Decorator that marks a class as a Nest gateway that enables real-time, bidirectional
* and event-based communication between the browser and the server.
*
* @publicApi
*/
export declare function WebSocketGateway(port?: number): ClassDecorator;
export declare function WebSocketGateway<T extends Record<string, any> = GatewayMetadata>(options?: T): ClassDecorator;
export declare function WebSocketGateway<T extends Record<string, any> = GatewayMetadata>(port?: number, options?: T): ClassDecorator;

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketGateway = WebSocketGateway;
const constants_1 = require("../constants");
function WebSocketGateway(portOrOptions, options) {
const isPortInt = Number.isInteger(portOrOptions);
// eslint-disable-next-line prefer-const
let [port, opt] = isPortInt ? [portOrOptions, options] : [0, portOrOptions];
opt = opt || {};
return (target) => {
Reflect.defineMetadata(constants_1.GATEWAY_METADATA, true, target);
Reflect.defineMetadata(constants_1.PORT_METADATA, port, target);
Reflect.defineMetadata(constants_1.GATEWAY_OPTIONS, opt, target);
};
}

View File

@@ -0,0 +1,6 @@
/**
* Subscribes to messages that fulfils chosen pattern.
*
* @publicApi
*/
export declare const SubscribeMessage: <T = string>(message: T) => MethodDecorator;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubscribeMessage = void 0;
const constants_1 = require("../constants");
/**
* Subscribes to messages that fulfils chosen pattern.
*
* @publicApi
*/
const SubscribeMessage = (message) => {
return (target, key, descriptor) => {
Reflect.defineMetadata(constants_1.MESSAGE_MAPPING_METADATA, true, descriptor.value);
Reflect.defineMetadata(constants_1.MESSAGE_METADATA, message, descriptor.value);
return descriptor;
};
};
exports.SubscribeMessage = SubscribeMessage;

View File

@@ -0,0 +1,4 @@
export declare enum WsParamtype {
SOCKET = 0,
PAYLOAD = 3
}

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsParamtype = void 0;
var WsParamtype;
(function (WsParamtype) {
WsParamtype[WsParamtype["SOCKET"] = 0] = "SOCKET";
WsParamtype[WsParamtype["PAYLOAD"] = 3] = "PAYLOAD";
})(WsParamtype || (exports.WsParamtype = WsParamtype = {}));

View File

@@ -0,0 +1 @@
export * from './ws-exception';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./ws-exception"), exports);

View File

@@ -0,0 +1,4 @@
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
export declare class InvalidSocketPortException extends RuntimeException {
constructor(port: number | string, type: any);
}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidSocketPortException = void 0;
const runtime_exception_1 = require("@nestjs/core/errors/exceptions/runtime.exception");
class InvalidSocketPortException extends runtime_exception_1.RuntimeException {
constructor(port, type) {
super(`Invalid port (${port}) in gateway ${type}`);
}
}
exports.InvalidSocketPortException = InvalidSocketPortException;

View File

@@ -0,0 +1,6 @@
export declare class WsException extends Error {
private readonly error;
constructor(error: string | object);
initMessage(): void;
getError(): string | object;
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsException = void 0;
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
class WsException extends Error {
constructor(error) {
super();
this.error = error;
this.initMessage();
}
initMessage() {
if ((0, shared_utils_1.isString)(this.error)) {
this.message = this.error;
}
else if ((0, shared_utils_1.isObject)(this.error) &&
(0, shared_utils_1.isString)(this.error.message)) {
this.message = this.error.message;
}
else if (this.constructor) {
this.message = this.constructor.name
.match(/[A-Z][a-z]+|[0-9]+/g)
.join(' ');
}
}
getError() {
return this.error;
}
}
exports.WsException = WsException;

View File

@@ -0,0 +1,15 @@
import { ArgumentsHost, WsExceptionFilter } from '@nestjs/common';
/**
* @publicApi
*/
export declare class BaseWsExceptionFilter<TError = any> implements WsExceptionFilter<TError> {
private static readonly logger;
catch(exception: TError, host: ArgumentsHost): void;
handleError<TClient extends {
emit: Function;
}>(client: TClient, exception: TError): void;
handleUnknownError<TClient extends {
emit: Function;
}>(exception: TError, client: TClient): void;
isExceptionObject(err: any): err is Error;
}

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseWsExceptionFilter = void 0;
const common_1 = require("@nestjs/common");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const constants_1 = require("@nestjs/core/constants");
const ws_exception_1 = require("../errors/ws-exception");
/**
* @publicApi
*/
class BaseWsExceptionFilter {
catch(exception, host) {
const client = host.switchToWs().getClient();
this.handleError(client, exception);
}
handleError(client, exception) {
if (!(exception instanceof ws_exception_1.WsException)) {
return this.handleUnknownError(exception, client);
}
const status = 'error';
const result = exception.getError();
const message = (0, shared_utils_1.isObject)(result)
? result
: {
status,
message: result,
};
client.emit('exception', message);
}
handleUnknownError(exception, client) {
const status = 'error';
client.emit('exception', {
status,
message: constants_1.MESSAGES.UNKNOWN_EXCEPTION_MESSAGE,
});
if (this.isExceptionObject(exception)) {
return BaseWsExceptionFilter.logger.error(exception.message, exception.stack);
}
return BaseWsExceptionFilter.logger.error(exception);
}
isExceptionObject(err) {
return (0, shared_utils_1.isObject)(err) && !!err.message;
}
}
exports.BaseWsExceptionFilter = BaseWsExceptionFilter;
BaseWsExceptionFilter.logger = new common_1.Logger('WsExceptionsHandler');

View File

@@ -0,0 +1 @@
export * from './base-ws-exception-filter';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./base-ws-exception-filter"), exports);

View File

@@ -0,0 +1,13 @@
import { ArgumentsHost } from '@nestjs/common';
import { ExceptionFilterMetadata } from '@nestjs/common/interfaces/exceptions/exception-filter-metadata.interface';
import { WsException } from '../errors/ws-exception';
import { BaseWsExceptionFilter } from './base-ws-exception-filter';
/**
* @publicApi
*/
export declare class WsExceptionsHandler extends BaseWsExceptionFilter {
private filters;
handle(exception: Error | WsException | any, host: ArgumentsHost): void;
setCustomFilters(filters: ExceptionFilterMetadata[]): void;
invokeCustomFilters<T = any>(exception: T, args: ArgumentsHost): boolean;
}

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsExceptionsHandler = void 0;
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const select_exception_filter_metadata_util_1 = require("@nestjs/common/utils/select-exception-filter-metadata.util");
const invalid_exception_filter_exception_1 = require("@nestjs/core/errors/exceptions/invalid-exception-filter.exception");
const base_ws_exception_filter_1 = require("./base-ws-exception-filter");
/**
* @publicApi
*/
class WsExceptionsHandler extends base_ws_exception_filter_1.BaseWsExceptionFilter {
constructor() {
super(...arguments);
this.filters = [];
}
handle(exception, host) {
const client = host.switchToWs().getClient();
if (this.invokeCustomFilters(exception, host) || !client.emit) {
return;
}
super.catch(exception, host);
}
setCustomFilters(filters) {
if (!Array.isArray(filters)) {
throw new invalid_exception_filter_exception_1.InvalidExceptionFilterException();
}
this.filters = filters;
}
invokeCustomFilters(exception, args) {
if ((0, shared_utils_1.isEmpty)(this.filters))
return false;
const filter = (0, select_exception_filter_metadata_util_1.selectExceptionFilterMetadata)(this.filters, exception);
filter && filter.func(exception, args);
return !!filter;
}
}
exports.WsExceptionsHandler = WsExceptionsHandler;

View File

@@ -0,0 +1,4 @@
import { ServerAndEventStreamsHost } from '../interfaces/server-and-event-streams-host.interface';
export declare class ServerAndEventStreamsFactory {
static create<T = any>(server: T): ServerAndEventStreamsHost<T>;
}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerAndEventStreamsFactory = void 0;
const rxjs_1 = require("rxjs");
class ServerAndEventStreamsFactory {
static create(server) {
const init = new rxjs_1.ReplaySubject();
init.next(server);
const connection = new rxjs_1.Subject();
const disconnect = new rxjs_1.Subject();
return {
init,
connection,
disconnect,
server,
};
}
}
exports.ServerAndEventStreamsFactory = ServerAndEventStreamsFactory;

View File

@@ -0,0 +1,3 @@
export declare class WsParamsFactory {
exchangeKeyForValue(type: number, data: string | undefined, args: unknown[]): any;
}

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WsParamsFactory = void 0;
const ws_paramtype_enum_1 = require("../enums/ws-paramtype.enum");
class WsParamsFactory {
exchangeKeyForValue(type, data, args) {
if (!args) {
return null;
}
switch (type) {
case ws_paramtype_enum_1.WsParamtype.SOCKET:
return args[0];
case ws_paramtype_enum_1.WsParamtype.PAYLOAD:
return data ? args[1]?.[data] : args[1];
default:
return null;
}
}
}
exports.WsParamsFactory = WsParamsFactory;

View File

@@ -0,0 +1,15 @@
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
import { Observable } from 'rxjs';
import { NestGateway } from './interfaces/nest-gateway.interface';
export interface MessageMappingProperties {
message: any;
methodName: string;
callback: (...args: any[]) => Observable<any> | Promise<any> | any;
}
export declare class GatewayMetadataExplorer {
private readonly metadataScanner;
constructor(metadataScanner: MetadataScanner);
explore(instance: NestGateway): MessageMappingProperties[];
exploreMethodMetadata(instancePrototype: object, methodName: string): MessageMappingProperties;
scanForServerHooks(instance: NestGateway): IterableIterator<string>;
}

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GatewayMetadataExplorer = void 0;
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const constants_1 = require("./constants");
class GatewayMetadataExplorer {
constructor(metadataScanner) {
this.metadataScanner = metadataScanner;
}
explore(instance) {
const instancePrototype = Object.getPrototypeOf(instance);
return this.metadataScanner
.getAllMethodNames(instancePrototype)
.map(method => this.exploreMethodMetadata(instancePrototype, method))
.filter(metadata => metadata);
}
exploreMethodMetadata(instancePrototype, methodName) {
const callback = instancePrototype[methodName];
const isMessageMapping = Reflect.getMetadata(constants_1.MESSAGE_MAPPING_METADATA, callback);
if ((0, shared_utils_1.isUndefined)(isMessageMapping)) {
return null;
}
const message = Reflect.getMetadata(constants_1.MESSAGE_METADATA, callback);
return {
callback,
message,
methodName,
};
}
*scanForServerHooks(instance) {
for (const propertyKey in instance) {
if ((0, shared_utils_1.isFunction)(propertyKey)) {
continue;
}
const property = String(propertyKey);
const isServer = Reflect.getMetadata(constants_1.GATEWAY_SERVER_METADATA, instance, property);
if (!(0, shared_utils_1.isUndefined)(isServer)) {
yield property;
}
}
}
}
exports.GatewayMetadataExplorer = GatewayMetadataExplorer;

7
backend/node_modules/@nestjs/websockets/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
import 'reflect-metadata';
export * from './adapters';
export * from './decorators';
export * from './errors';
export * from './exceptions';
export { MessageMappingProperties } from './gateway-metadata-explorer';
export * from './interfaces';

15
backend/node_modules/@nestjs/websockets/index.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
/*
* Nest @websockets
* Copyright(c) 2017 - 2023 Kamil Mysliwiec
* https://nestjs.com
* MIT Licensed
*/
require("reflect-metadata");
tslib_1.__exportStar(require("./adapters"), exports);
tslib_1.__exportStar(require("./decorators"), exports);
tslib_1.__exportStar(require("./errors"), exports);
tslib_1.__exportStar(require("./exceptions"), exports);
tslib_1.__exportStar(require("./interfaces"), exports);

View File

@@ -0,0 +1,119 @@
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
/**
* External interface
* @see https://github.com/socketio/socket.io/blob/master/lib/index.ts
* @publicApi
*/
export interface GatewayMetadata {
/**
* The name of a namespace
*/
namespace?: string | RegExp;
/**
* Name of the path to capture
* @default "/socket.io"
*/
path?: string;
/**
* Whether to serve the client files
* @default true
*/
serveClient?: boolean;
/**
* The adapter to use
* @default the in-memory adapter (https://github.com/socketio/socket.io-adapter)
*/
adapter?: any;
/**
* The parser to use
* @default the default parser (https://github.com/socketio/socket.io-parser)
*/
parser?: any;
/**
* How many ms before a client without namespace is closed
* @default 45_000
*/
connectTimeout?: number;
/**
* How many ms without a pong packet to consider the connection closed
* @default 20_000
*/
pingTimeout?: number;
/**
* How many ms before sending a new ping packet
* @default 25_000
*/
pingInterval?: number;
/**
* How many ms before an uncompleted transport upgrade is cancelled
* @default 10_000
*/
upgradeTimeout?: number;
/**
* How many bytes or characters a message can be, before closing the session (to avoid DoS).
* @default 1e6 (1 MB)
*/
maxHttpBufferSize?: number;
/**
* A function that receives a given handshake or upgrade request as its first parameter,
* and can decide whether to continue or not. The second argument is a function that needs
* to be called with the decided information: fn(err, success), where success is a boolean
* value where false means that the request is rejected, and err is an error code.
*/
allowRequest?: (req: any, fn: (err: string | null | undefined, success: boolean) => void) => void;
/**
* The low-level transports that are enabled
* @default ["polling", "websocket"]
*/
transports?: Array<'polling' | 'websocket'>;
/**
* Whether to allow transport upgrades
* @default true
*/
allowUpgrades?: boolean;
/**
* Parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
* @default false
*/
perMessageDeflate?: boolean | object;
/**
* Parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable.
* @default true
*/
httpCompression?: boolean | object;
/**
* What WebSocket server implementation to use. Specified module must
* conform to the ws interface (see ws module api docs). Default value is ws.
* An alternative c++ addon is also available by installing uws module.
*/
wsEngine?: string;
/**
* An optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
*/
initialPacket?: any;
/**
* Configuration of the cookie that contains the client sid to send as part of handshake response headers. This cookie
* might be used for sticky-session. Defaults to not sending any cookie.
* @default false
*/
cookie?: any | boolean;
/**
* The options that will be forwarded to the cors module
*/
cors?: CorsOptions;
/**
* Whether to enable compatibility with Socket.IO v2 clients
* @default false
*/
allowEIO3?: boolean;
/**
* Destroy unhandled upgrade requests
* @default true
*/
destroyUpgrade?: boolean;
/**
* Milliseconds after which unhandled requests are ended
* @default 1_000
*/
destroyUpgradeTimeout?: number;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,3 @@
export * from './on-gateway-connection.interface';
export * from './on-gateway-disconnect.interface';
export * from './on-gateway-init.interface';

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./on-gateway-connection.interface"), exports);
tslib_1.__exportStar(require("./on-gateway-disconnect.interface"), exports);
tslib_1.__exportStar(require("./on-gateway-init.interface"), exports);

View File

@@ -0,0 +1,6 @@
/**
* @publicApi
*/
export interface OnGatewayConnection<T = any> {
handleConnection(client: T, ...args: any[]): any;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,6 @@
/**
* @publicApi
*/
export interface OnGatewayDisconnect<T = any> {
handleDisconnect(client: T): any;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,6 @@
/**
* @publicApi
*/
export interface OnGatewayInit<T = any> {
afterInit(server: T): any;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,5 @@
export * from './gateway-metadata.interface';
export * from './hooks';
export * from './server-and-event-streams-host.interface';
export * from './web-socket-server.interface';
export * from './ws-response.interface';

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./gateway-metadata.interface"), exports);
tslib_1.__exportStar(require("./hooks"), exports);
tslib_1.__exportStar(require("./server-and-event-streams-host.interface"), exports);
tslib_1.__exportStar(require("./web-socket-server.interface"), exports);
tslib_1.__exportStar(require("./ws-response.interface"), exports);

View File

@@ -0,0 +1,8 @@
/**
* @publicApi
*/
export interface NestGateway {
afterInit?: (server: any) => void;
handleConnection?: (...args: any[]) => void;
handleDisconnect?: (client: any) => void;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,10 @@
import { ReplaySubject, Subject } from 'rxjs';
/**
* @publicApi
*/
export interface ServerAndEventStreamsHost<T = any> {
server: T;
init: ReplaySubject<T>;
connection: Subject<any>;
disconnect: Subject<any>;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,7 @@
/**
* @publicApi
*/
export interface WebSocketServerOptions {
port: number;
namespace: string;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,4 @@
export type WebsocketEntrypointMetadata = {
port: number;
message: unknown;
};

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,7 @@
/**
* @publicApi
*/
export interface WsResponse<T = any> {
event: string;
data: T;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

36
backend/node_modules/@nestjs/websockets/package.json generated vendored Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "@nestjs/websockets",
"version": "10.4.19",
"description": "Nest - modern, fast, powerful node.js web framework (@websockets)",
"author": "Kamil Mysliwiec",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nestjs/nest.git",
"directory": "packages/websockets"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"iterare": "1.2.1",
"object-hash": "3.0.0",
"tslib": "2.8.1"
},
"devDependencies": {
"@nestjs/common": "10.4.19",
"@nestjs/core": "10.4.19"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-socket.io": "^10.0.0",
"reflect-metadata": "^0.1.12 || ^0.2.0",
"rxjs": "^7.1.0"
},
"peerDependenciesMeta": {
"@nestjs/platform-socket.io": {
"optional": true
}
}
}

View File

@@ -0,0 +1,21 @@
import { InjectionToken } from '@nestjs/common/interfaces';
import { Injectable } from '@nestjs/common/interfaces/injectable.interface';
import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
import { ApplicationConfig } from '@nestjs/core/application-config';
import { NestContainer } from '@nestjs/core/injector/container';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { GraphInspector } from '@nestjs/core/inspector/graph-inspector';
export declare class SocketModule<THttpServer = any, TAppOptions extends NestApplicationContextOptions = NestApplicationContextOptions> {
private readonly socketsContainer;
private applicationConfig;
private webSocketsController;
private isAdapterInitialized;
private httpServer;
private appOptions;
register(container: NestContainer, applicationConfig: ApplicationConfig, graphInspector: GraphInspector, appOptions: TAppOptions, httpServer?: THttpServer): void;
connectAllGateways(providers: Map<InjectionToken, InstanceWrapper<Injectable>>, moduleName: string): void;
connectGatewayToServer(wrapper: InstanceWrapper<Injectable>, moduleName: string): void;
close(): Promise<any>;
private initializeAdapter;
private getContextCreator;
}

View File

@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketModule = void 0;
const guards_consumer_1 = require("@nestjs/core/guards/guards-consumer");
const guards_context_creator_1 = require("@nestjs/core/guards/guards-context-creator");
const load_adapter_1 = require("@nestjs/core/helpers/load-adapter");
const interceptors_consumer_1 = require("@nestjs/core/interceptors/interceptors-consumer");
const interceptors_context_creator_1 = require("@nestjs/core/interceptors/interceptors-context-creator");
const pipes_consumer_1 = require("@nestjs/core/pipes/pipes-consumer");
const pipes_context_creator_1 = require("@nestjs/core/pipes/pipes-context-creator");
const iterare_1 = require("iterare");
const constants_1 = require("./constants");
const exception_filters_context_1 = require("./context/exception-filters-context");
const ws_context_creator_1 = require("./context/ws-context-creator");
const ws_proxy_1 = require("./context/ws-proxy");
const socket_server_provider_1 = require("./socket-server-provider");
const sockets_container_1 = require("./sockets-container");
const web_sockets_controller_1 = require("./web-sockets-controller");
class SocketModule {
constructor() {
this.socketsContainer = new sockets_container_1.SocketsContainer();
}
register(container, applicationConfig, graphInspector, appOptions, httpServer) {
this.applicationConfig = applicationConfig;
this.appOptions = appOptions;
this.httpServer = httpServer;
const contextCreator = this.getContextCreator(container);
const serverProvider = new socket_server_provider_1.SocketServerProvider(this.socketsContainer, applicationConfig);
this.webSocketsController = new web_sockets_controller_1.WebSocketsController(serverProvider, applicationConfig, contextCreator, graphInspector, this.appOptions);
const modules = container.getModules();
modules.forEach(({ providers }, moduleName) => this.connectAllGateways(providers, moduleName));
}
connectAllGateways(providers, moduleName) {
(0, iterare_1.iterate)(providers.values())
.filter(wrapper => wrapper && !wrapper.isNotMetatype)
.forEach(wrapper => this.connectGatewayToServer(wrapper, moduleName));
}
connectGatewayToServer(wrapper, moduleName) {
const { instance, metatype } = wrapper;
const metadataKeys = Reflect.getMetadataKeys(metatype);
if (!metadataKeys.includes(constants_1.GATEWAY_METADATA)) {
return;
}
if (!this.isAdapterInitialized) {
this.initializeAdapter();
}
this.webSocketsController.connectGatewayToServer(instance, metatype, moduleName, wrapper.id);
}
async close() {
if (!this.applicationConfig) {
return;
}
const adapter = this.applicationConfig.getIoAdapter();
if (!adapter) {
return;
}
const servers = this.socketsContainer.getAll();
await Promise.all((0, iterare_1.iterate)(servers.values())
.filter(({ server }) => server)
.map(async ({ server }) => adapter.close(server)));
await adapter?.dispose();
this.socketsContainer.clear();
}
initializeAdapter() {
const forceCloseConnections = this.appOptions
.forceCloseConnections;
const adapter = this.applicationConfig.getIoAdapter();
if (adapter) {
adapter.forceCloseConnections =
forceCloseConnections;
this.isAdapterInitialized = true;
return;
}
const { IoAdapter } = (0, load_adapter_1.loadAdapter)('@nestjs/platform-socket.io', 'WebSockets', () => require('@nestjs/platform-socket.io'));
const ioAdapter = new IoAdapter(this.httpServer);
ioAdapter.forceCloseConnections = forceCloseConnections;
this.applicationConfig.setIoAdapter(ioAdapter);
this.isAdapterInitialized = true;
}
getContextCreator(container) {
return new ws_context_creator_1.WsContextCreator(new ws_proxy_1.WsProxy(), new exception_filters_context_1.ExceptionFiltersContext(container), new pipes_context_creator_1.PipesContextCreator(container), new pipes_consumer_1.PipesConsumer(), new guards_context_creator_1.GuardsContextCreator(container), new guards_consumer_1.GuardsConsumer(), new interceptors_context_creator_1.InterceptorsContextCreator(container), new interceptors_consumer_1.InterceptorsConsumer());
}
}
exports.SocketModule = SocketModule;

View File

@@ -0,0 +1,14 @@
import { ApplicationConfig } from '@nestjs/core/application-config';
import { GatewayMetadata } from './interfaces/gateway-metadata.interface';
import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface';
import { SocketsContainer } from './sockets-container';
export declare class SocketServerProvider {
private readonly socketsContainer;
private readonly applicationConfig;
constructor(socketsContainer: SocketsContainer, applicationConfig: ApplicationConfig);
scanForSocketServer<T extends GatewayMetadata = any>(options: T, port: number): ServerAndEventStreamsHost;
private createSocketServer;
private decorateWithNamespace;
private getServerOfNamespace;
private validateNamespace;
}

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketServerProvider = void 0;
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const server_and_event_streams_factory_1 = require("./factories/server-and-event-streams-factory");
class SocketServerProvider {
constructor(socketsContainer, applicationConfig) {
this.socketsContainer = socketsContainer;
this.applicationConfig = applicationConfig;
}
scanForSocketServer(options, port) {
const serverAndStreamsHost = this.socketsContainer.getOneByConfig({
port,
path: options.path,
});
if (serverAndStreamsHost && options.namespace) {
return this.decorateWithNamespace(options, port, serverAndStreamsHost.server);
}
return serverAndStreamsHost
? serverAndStreamsHost
: this.createSocketServer(options, port);
}
createSocketServer(options, port) {
const adapter = this.applicationConfig.getIoAdapter();
const { namespace, server, ...partialOptions } = options;
const ioServer = adapter.create(port, partialOptions);
const serverAndEventStreamsHost = server_and_event_streams_factory_1.ServerAndEventStreamsFactory.create(ioServer);
this.socketsContainer.addOne({ port, path: options.path }, serverAndEventStreamsHost);
if (!namespace) {
return serverAndEventStreamsHost;
}
return this.decorateWithNamespace(options, port, ioServer);
}
decorateWithNamespace(options, port, targetServer) {
const namespaceServer = this.getServerOfNamespace(options, port, targetServer);
const serverAndEventStreamsHost = server_and_event_streams_factory_1.ServerAndEventStreamsFactory.create(namespaceServer);
this.socketsContainer.addOne({ port, path: options.path, namespace: options.namespace }, serverAndEventStreamsHost);
return serverAndEventStreamsHost;
}
getServerOfNamespace(options, port, server) {
const adapter = this.applicationConfig.getIoAdapter();
return adapter.create(port, {
...options,
namespace: this.validateNamespace(options.namespace || ''),
server,
});
}
validateNamespace(namespace) {
if (!(0, shared_utils_1.isString)(namespace)) {
return namespace;
}
return (0, shared_utils_1.addLeadingSlash)(namespace);
}
}
exports.SocketServerProvider = SocketServerProvider;

View File

@@ -0,0 +1,9 @@
import { GatewayMetadata, ServerAndEventStreamsHost } from './interfaces';
export declare class SocketsContainer {
private readonly serverAndEventStreamsHosts;
getAll(): Map<string | RegExp, ServerAndEventStreamsHost>;
getOneByConfig<T extends GatewayMetadata = any>(options: T): ServerAndEventStreamsHost;
addOne<T extends GatewayMetadata = any>(options: T, host: ServerAndEventStreamsHost): void;
clear(): void;
private generateHashByOptions;
}

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketsContainer = void 0;
const hash = require("object-hash");
class SocketsContainer {
constructor() {
this.serverAndEventStreamsHosts = new Map();
}
getAll() {
return this.serverAndEventStreamsHosts;
}
getOneByConfig(options) {
const uniqueToken = this.generateHashByOptions(options);
return this.serverAndEventStreamsHosts.get(uniqueToken);
}
addOne(options, host) {
const uniqueToken = this.generateHashByOptions(options);
this.serverAndEventStreamsHosts.set(uniqueToken, host);
}
clear() {
this.serverAndEventStreamsHosts.clear();
}
generateHashByOptions(options) {
return hash(options, { ignoreUnknown: true });
}
}
exports.SocketsContainer = SocketsContainer;

View File

@@ -0,0 +1,22 @@
{
"extends": "../tsconfig.build.json",
"compilerOptions": {
"outDir": ".",
"rootDir": ".",
"paths": {
"@nestjs/common": ["../common"],
"@nestjs/common/*": ["../common/*"],
"@nestjs/core": ["../core"],
"@nestjs/core/*": ["../core/*"]
}
},
"exclude": ["node_modules", "dist", "test/**/*", "*.spec.ts"],
"references": [
{
"path": "../common/tsconfig.build.json"
},
{
"path": "../core/tsconfig.build.json"
}
]
}

View File

@@ -0,0 +1 @@
export declare function compareElementAt(prev: unknown[], curr: unknown[], index: number): boolean;

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compareElementAt = compareElementAt;
function compareElementAt(prev, curr, index) {
return prev && curr && prev[index] === curr[index];
}

View File

@@ -0,0 +1 @@
export * from './param.utils';

View File

@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./param.utils"), exports);

View File

@@ -0,0 +1,5 @@
import { PipeTransform, Type } from '@nestjs/common';
import 'reflect-metadata';
import { WsParamtype } from '../enums/ws-paramtype.enum';
export declare function createWsParamDecorator(paramtype: WsParamtype): (...pipes: (Type<PipeTransform> | PipeTransform)[]) => ParameterDecorator;
export declare const createPipesWsParamDecorator: (paramtype: WsParamtype) => (data?: any, ...pipes: (Type<PipeTransform> | PipeTransform)[]) => ParameterDecorator;

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPipesWsParamDecorator = void 0;
exports.createWsParamDecorator = createWsParamDecorator;
const route_params_decorator_1 = require("@nestjs/common/decorators/http/route-params.decorator");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
require("reflect-metadata");
const constants_1 = require("../constants");
function createWsParamDecorator(paramtype) {
return (...pipes) => (target, key, index) => {
const args = Reflect.getMetadata(constants_1.PARAM_ARGS_METADATA, target.constructor, key) || {};
Reflect.defineMetadata(constants_1.PARAM_ARGS_METADATA, (0, route_params_decorator_1.assignMetadata)(args, paramtype, index, undefined, ...pipes), target.constructor, key);
};
}
const createPipesWsParamDecorator = (paramtype) => (data, ...pipes) => (target, key, index) => {
const args = Reflect.getMetadata(constants_1.PARAM_ARGS_METADATA, target.constructor, key) || {};
const hasParamData = (0, shared_utils_1.isNil)(data) || (0, shared_utils_1.isString)(data);
const paramData = hasParamData ? data : undefined;
const paramPipes = hasParamData ? pipes : [data, ...pipes];
Reflect.defineMetadata(constants_1.PARAM_ARGS_METADATA, (0, route_params_decorator_1.assignMetadata)(args, paramtype, index, paramData, ...paramPipes), target.constructor, key);
};
exports.createPipesWsParamDecorator = createPipesWsParamDecorator;

View File

@@ -0,0 +1,33 @@
import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
import { Type } from '@nestjs/common/interfaces/type.interface';
import { ApplicationConfig } from '@nestjs/core/application-config';
import { GraphInspector } from '@nestjs/core/inspector/graph-inspector';
import { Observable, Subject } from 'rxjs';
import { WsContextCreator } from './context/ws-context-creator';
import { MessageMappingProperties } from './gateway-metadata-explorer';
import { GatewayMetadata } from './interfaces/gateway-metadata.interface';
import { NestGateway } from './interfaces/nest-gateway.interface';
import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface';
import { SocketServerProvider } from './socket-server-provider';
export declare class WebSocketsController {
private readonly socketServerProvider;
private readonly config;
private readonly contextCreator;
private readonly graphInspector;
private readonly appOptions;
private readonly logger;
private readonly metadataExplorer;
constructor(socketServerProvider: SocketServerProvider, config: ApplicationConfig, contextCreator: WsContextCreator, graphInspector: GraphInspector, appOptions?: NestApplicationContextOptions);
connectGatewayToServer(instance: NestGateway, metatype: Type<unknown> | Function, moduleKey: string, instanceWrapperId: string): void;
subscribeToServerEvents<T extends GatewayMetadata>(instance: NestGateway, options: T, port: number, moduleKey: string, instanceWrapperId: string): void;
subscribeEvents(instance: NestGateway, subscribersMap: MessageMappingProperties[], observableServer: ServerAndEventStreamsHost): void;
getConnectionHandler(context: WebSocketsController, instance: NestGateway, subscribersMap: MessageMappingProperties[], disconnect: Subject<any>, connection: Subject<any>): (...args: unknown[]) => void;
subscribeInitEvent(instance: NestGateway, event: Subject<any>): void;
subscribeConnectionEvent(instance: NestGateway, event: Subject<any>): void;
subscribeDisconnectEvent(instance: NestGateway, event: Subject<any>): void;
subscribeMessages<T = any>(subscribersMap: MessageMappingProperties[], client: T, instance: NestGateway): void;
pickResult(deferredResult: Promise<any>): Promise<Observable<any>>;
inspectEntrypointDefinitions(instance: NestGateway, port: number, messageHandlers: MessageMappingProperties[], instanceWrapperId: string): void;
private assignServerToProperties;
private printSubscriptionLogs;
}

View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebSocketsController = void 0;
const logger_service_1 = require("@nestjs/common/services/logger.service");
const metadata_scanner_1 = require("@nestjs/core/metadata-scanner");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const constants_1 = require("./constants");
const invalid_socket_port_exception_1 = require("./errors/invalid-socket-port.exception");
const gateway_metadata_explorer_1 = require("./gateway-metadata-explorer");
const compare_element_util_1 = require("./utils/compare-element.util");
class WebSocketsController {
constructor(socketServerProvider, config, contextCreator, graphInspector, appOptions = {}) {
this.socketServerProvider = socketServerProvider;
this.config = config;
this.contextCreator = contextCreator;
this.graphInspector = graphInspector;
this.appOptions = appOptions;
this.logger = new logger_service_1.Logger(WebSocketsController.name, {
timestamp: true,
});
this.metadataExplorer = new gateway_metadata_explorer_1.GatewayMetadataExplorer(new metadata_scanner_1.MetadataScanner());
}
connectGatewayToServer(instance, metatype, moduleKey, instanceWrapperId) {
const options = Reflect.getMetadata(constants_1.GATEWAY_OPTIONS, metatype) || {};
const port = Reflect.getMetadata(constants_1.PORT_METADATA, metatype) || 0;
if (!Number.isInteger(port)) {
throw new invalid_socket_port_exception_1.InvalidSocketPortException(port, metatype);
}
this.subscribeToServerEvents(instance, options, port, moduleKey, instanceWrapperId);
}
subscribeToServerEvents(instance, options, port, moduleKey, instanceWrapperId) {
const nativeMessageHandlers = this.metadataExplorer.explore(instance);
const messageHandlers = nativeMessageHandlers.map(({ callback, message, methodName }) => ({
message,
methodName,
callback: this.contextCreator.create(instance, callback, moduleKey, methodName),
}));
this.inspectEntrypointDefinitions(instance, port, messageHandlers, instanceWrapperId);
if (this.appOptions.preview) {
return;
}
const observableServer = this.socketServerProvider.scanForSocketServer(options, port);
this.assignServerToProperties(instance, observableServer.server);
this.subscribeEvents(instance, messageHandlers, observableServer);
}
subscribeEvents(instance, subscribersMap, observableServer) {
const { init, disconnect, connection, server } = observableServer;
const adapter = this.config.getIoAdapter();
this.subscribeInitEvent(instance, init);
this.subscribeConnectionEvent(instance, connection);
this.subscribeDisconnectEvent(instance, disconnect);
const handler = this.getConnectionHandler(this, instance, subscribersMap, disconnect, connection);
adapter.bindClientConnect(server, handler);
this.printSubscriptionLogs(instance, subscribersMap);
}
getConnectionHandler(context, instance, subscribersMap, disconnect, connection) {
const adapter = this.config.getIoAdapter();
return (...args) => {
const [client] = args;
connection.next(args);
context.subscribeMessages(subscribersMap, client, instance);
const disconnectHook = adapter.bindClientDisconnect;
disconnectHook &&
disconnectHook.call(adapter, client, () => disconnect.next(client));
};
}
subscribeInitEvent(instance, event) {
if (instance.afterInit) {
event.subscribe(instance.afterInit.bind(instance));
}
}
subscribeConnectionEvent(instance, event) {
if (instance.handleConnection) {
event
.pipe((0, operators_1.distinctUntilChanged)((prev, curr) => (0, compare_element_util_1.compareElementAt)(prev, curr, 0)))
.subscribe((args) => instance.handleConnection(...args));
}
}
subscribeDisconnectEvent(instance, event) {
if (instance.handleDisconnect) {
event
.pipe((0, operators_1.distinctUntilChanged)())
.subscribe(instance.handleDisconnect.bind(instance));
}
}
subscribeMessages(subscribersMap, client, instance) {
const adapter = this.config.getIoAdapter();
const handlers = subscribersMap.map(({ callback, message }) => ({
message,
callback: callback.bind(instance, client),
}));
adapter.bindMessageHandlers(client, handlers, data => (0, rxjs_1.from)(this.pickResult(data)).pipe((0, operators_1.mergeAll)()));
}
async pickResult(deferredResult) {
const result = await deferredResult;
if ((0, rxjs_1.isObservable)(result)) {
return result;
}
if (result instanceof Promise) {
return (0, rxjs_1.from)(result);
}
return (0, rxjs_1.of)(result);
}
inspectEntrypointDefinitions(instance, port, messageHandlers, instanceWrapperId) {
messageHandlers.forEach(handler => {
this.graphInspector.insertEntrypointDefinition({
type: 'websocket',
methodName: handler.methodName,
className: instance.constructor?.name,
classNodeId: instanceWrapperId,
metadata: {
port,
key: handler.message,
message: handler.message,
},
}, instanceWrapperId);
});
}
assignServerToProperties(instance, server) {
for (const propertyKey of this.metadataExplorer.scanForServerHooks(instance)) {
Reflect.set(instance, propertyKey, server);
}
}
printSubscriptionLogs(instance, subscribersMap) {
const gatewayClassName = instance?.constructor?.name;
if (!gatewayClassName) {
return;
}
subscribersMap.forEach(({ message }) => this.logger.log(`${gatewayClassName} subscribed to the "${message}" message`));
}
}
exports.WebSocketsController = WebSocketsController;