mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
feature(websockets) extract web sockets drivers
This commit is contained in:
@@ -18,6 +18,10 @@ const packages = {
|
||||
'platform-fastify': ts.createProject(
|
||||
'packages/platform-fastify/tsconfig.json',
|
||||
),
|
||||
'platform-socket.io': ts.createProject(
|
||||
'packages/platform-socket.io/tsconfig.json',
|
||||
),
|
||||
'platform-ws': ts.createProject('packages/platform-ws/tsconfig.json'),
|
||||
};
|
||||
const modules = Object.keys(packages);
|
||||
const source = 'packages';
|
||||
@@ -42,7 +46,9 @@ gulp.task('copy-misc', function() {
|
||||
.pipe(gulp.dest(`${source}/websockets`))
|
||||
.pipe(gulp.dest(`${source}/testing`))
|
||||
.pipe(gulp.dest(`${source}/platform-fastify`))
|
||||
.pipe(gulp.dest(`${source}/platform-express`));
|
||||
.pipe(gulp.dest(`${source}/platform-express`))
|
||||
.pipe(gulp.dest(`${source}/platform-ws`))
|
||||
.pipe(gulp.dest(`${source}/platform-socket.io`));
|
||||
});
|
||||
|
||||
gulp.task('clean:output', function() {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
||||
import { FastifyAdapter } from '@nestjs/platform-fastify';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
INestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
describe('Hello world (fastify adapter)', () => {
|
||||
let app: INestApplication & INestFastifyApplication;
|
||||
let app: INestFastifyApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [ApplicationModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication(new FastifyAdapter());
|
||||
app = module.createNestApplication<INestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
import * as request from 'supertest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { INestApplication, Injectable } from '@nestjs/common';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
INestApplication,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import { APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import * as request from 'supertest';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
|
||||
@Injectable()
|
||||
export class OverrideInterceptor {
|
||||
intercept(context, stream) {
|
||||
export class OverrideInterceptor implements NestInterceptor {
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
return of(RETURN_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TransformInterceptor {
|
||||
intercept(context, stream) {
|
||||
return stream.pipe(map(data => ({ data })));
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
return next.handle().pipe(map(data => ({ data })));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
}
|
||||
|
||||
describe('WebSocketGateway', () => {
|
||||
const event = 'push';
|
||||
let ws, app;
|
||||
|
||||
it(`should handle message (2nd port)`, async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { WsAdapter } from '@nestjs/platform-ws';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { WsAdapter } from '@nestjs/websockets/adapters/ws-adapter';
|
||||
import { expect } from 'chai';
|
||||
import * as WebSocket from 'ws';
|
||||
import { ApplicationGateway } from '../src/app.gateway';
|
||||
|
||||
63
package-lock.json
generated
63
package-lock.json
generated
@@ -1686,9 +1686,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
|
||||
"integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"buffer-more-ints": {
|
||||
@@ -10492,9 +10492,9 @@
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz",
|
||||
"integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==",
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
|
||||
"integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
|
||||
"dev": true
|
||||
},
|
||||
"make-iterator": {
|
||||
@@ -15448,47 +15448,21 @@
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-6.0.1.tgz",
|
||||
"integrity": "sha512-LDS8WJRTEztvQEXbRK/0l/apWE0BNR/USGUhVcJDe2/hbNiB/v/lCqk6YJzgwKjScgdWOAhPQsfur7hmQ1Y1jA==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
|
||||
"integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arrify": "^1.0.0",
|
||||
"chalk": "^2.3.0",
|
||||
"buffer-from": "^1.1.0",
|
||||
"diff": "^3.1.0",
|
||||
"make-error": "^1.1.1",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"source-map-support": "^0.5.3",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
|
||||
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -15496,23 +15470,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.5.tgz",
|
||||
"integrity": "sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA==",
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
|
||||
"integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
|
||||
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"sinon-chai": "^2.8.0",
|
||||
"socket.io-client": "^2.0.4",
|
||||
"supertest": "^3.0.0",
|
||||
"ts-node": "^6.0.0",
|
||||
"ts-node": "^7.0.1",
|
||||
"tslint": "^5.11.0",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ export {
|
||||
ValidationError,
|
||||
WebSocketAdapter,
|
||||
WsExceptionFilter,
|
||||
WsMessageHandler,
|
||||
} from './interfaces';
|
||||
export * from './pipes';
|
||||
export * from './serializer';
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export interface WebSocketAdapter<T = any> {
|
||||
create(port: number, options?: T): any;
|
||||
bindClientConnect(server: any, callback: (...args: any[]) => void): any;
|
||||
bindClientDisconnect?(client: any, callback: (...args: any[]) => void): any;
|
||||
export interface WsMessageHandler<T = string> {
|
||||
message: T;
|
||||
callback: (...args: any[]) => Observable<any> | Promise<any>;
|
||||
}
|
||||
export interface WebSocketAdapter<
|
||||
TServer = any,
|
||||
TClient = any,
|
||||
TOptions = any
|
||||
> {
|
||||
create(port: number, options?: TOptions): TServer;
|
||||
bindClientConnect(server: TServer, callback: Function): any;
|
||||
bindClientDisconnect?(client: TClient, callback: Function): any;
|
||||
bindMessageHandlers(
|
||||
client: any,
|
||||
handlers: Array<{
|
||||
message: any;
|
||||
callback: (...args: any[]) => Observable<any> | Promise<any> | any;
|
||||
}>,
|
||||
client: TClient,
|
||||
handlers: WsMessageHandler[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
): any;
|
||||
close(server: any): any;
|
||||
close(server: TServer): any;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@ import { RequestHandler } from '@nestjs/common/interfaces';
|
||||
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
|
||||
import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface';
|
||||
|
||||
export abstract class AbstractHttpAdapter<T = any> implements HttpServer {
|
||||
protected httpServer: T;
|
||||
export abstract class AbstractHttpAdapter<
|
||||
TServer = any,
|
||||
TRequest = any,
|
||||
TResponse = any
|
||||
> implements HttpServer<TRequest, TResponse> {
|
||||
protected httpServer: TServer;
|
||||
|
||||
constructor(protected readonly instance: any) {}
|
||||
|
||||
@@ -60,11 +64,11 @@ export abstract class AbstractHttpAdapter<T = any> implements HttpServer {
|
||||
return this.instance.listen(port, hostname, callback);
|
||||
}
|
||||
|
||||
public getHttpServer(): T {
|
||||
return this.httpServer as T;
|
||||
public getHttpServer(): TServer {
|
||||
return this.httpServer as TServer;
|
||||
}
|
||||
|
||||
public setHttpServer(httpServer: T) {
|
||||
public setHttpServer(httpServer: TServer) {
|
||||
this.httpServer = httpServer;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ export class ExceptionsHandler extends BaseExceptionFilter {
|
||||
if (isEmpty(this.filters)) return false;
|
||||
|
||||
const filter = this.filters.find(({ exceptionMetatypes }) => {
|
||||
const hasMetatype =
|
||||
const typeExists =
|
||||
!exceptionMetatypes.length ||
|
||||
exceptionMetatypes.some(
|
||||
ExceptionMetatype => exception instanceof ExceptionMetatype,
|
||||
);
|
||||
return hasMetatype;
|
||||
return typeExists;
|
||||
});
|
||||
filter && filter.func(exception, ctx);
|
||||
return !!filter;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CanActivate } from '@nestjs/common';
|
||||
import { Controller } from '@nestjs/common/interfaces';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context-host';
|
||||
|
||||
export class GuardsConsumer {
|
||||
public async tryActivate(
|
||||
|
||||
18
packages/core/helpers/load-adapter.ts
Normal file
18
packages/core/helpers/load-adapter.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
const MISSING_REQUIRED_DEPENDENCY = (
|
||||
defaultPlatform: string,
|
||||
transport: string,
|
||||
) =>
|
||||
`No driver (${transport}) has been selected. In order to take advantage of the default driver, please, ensure to install the "${defaultPlatform}" package ($ npm install ${defaultPlatform}).`;
|
||||
|
||||
const logger = new Logger('PackageLoader');
|
||||
|
||||
export function loadAdapter(defaultPlatform: string, transport: string) {
|
||||
try {
|
||||
return require(defaultPlatform);
|
||||
} catch (e) {
|
||||
logger.error(MISSING_REQUIRED_DEPENDENCY(defaultPlatform, transport));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { CallHandler, Controller } from '@nestjs/common/interfaces';
|
||||
import { isEmpty } from '@nestjs/common/utils/shared.utils';
|
||||
import { defer, from as fromPromise, Observable } from 'rxjs';
|
||||
import { mergeAll, switchMap } from 'rxjs/operators';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context-host';
|
||||
|
||||
export class InterceptorsConsumer {
|
||||
public async intercept(
|
||||
|
||||
@@ -18,6 +18,7 @@ import iterate from 'iterare';
|
||||
import * as optional from 'optional';
|
||||
import { ApplicationConfig } from './application-config';
|
||||
import { MESSAGES } from './constants';
|
||||
import { loadAdapter } from './helpers/load-adapter';
|
||||
import { NestContainer } from './injector/container';
|
||||
import { MiddlewareContainer } from './middleware/container';
|
||||
import { MiddlewareModule } from './middleware/middleware-module';
|
||||
@@ -29,8 +30,6 @@ const { SocketModule } =
|
||||
optional('@nestjs/websockets/socket-module') || ({} as any);
|
||||
const { MicroservicesModule } =
|
||||
optional('@nestjs/microservices/microservices-module') || ({} as any);
|
||||
const { IoAdapter } =
|
||||
optional('@nestjs/websockets/adapters/io-adapter') || ({} as any);
|
||||
|
||||
export class NestApplication extends NestApplicationContext
|
||||
implements INestApplication {
|
||||
@@ -68,6 +67,7 @@ export class NestApplication extends NestApplicationContext
|
||||
public registerHttpServer() {
|
||||
this.httpServer = this.createServer();
|
||||
|
||||
const { IoAdapter } = optional('@nestjs/platform-socket.io') || ({} as any);
|
||||
const ioAdapter = IoAdapter ? new IoAdapter(this.httpServer) : null;
|
||||
this.config.setIoAdapter(ioAdapter);
|
||||
}
|
||||
@@ -93,8 +93,7 @@ export class NestApplication extends NestApplicationContext
|
||||
}
|
||||
|
||||
public async registerModules() {
|
||||
this.socketModule &&
|
||||
this.socketModule.register(this.container, this.config);
|
||||
this.registerWsModule();
|
||||
|
||||
if (this.microservicesModule) {
|
||||
this.microservicesModule.register(this.container, this.config);
|
||||
@@ -107,6 +106,17 @@ export class NestApplication extends NestApplicationContext
|
||||
);
|
||||
}
|
||||
|
||||
public registerWsModule() {
|
||||
if (!this.socketModule) {
|
||||
return;
|
||||
}
|
||||
const adapter = this.config.getIoAdapter();
|
||||
if (!adapter) {
|
||||
return loadAdapter('@nestjs/platform-socket.io', 'WebSockets');
|
||||
}
|
||||
this.socketModule.register(this.container, this.config);
|
||||
}
|
||||
|
||||
public async init(): Promise<this> {
|
||||
const useBodyParser =
|
||||
this.appOptions && this.appOptions.bodyParser !== false;
|
||||
|
||||
@@ -14,6 +14,7 @@ import { isFunction, isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import { ApplicationConfig } from './application-config';
|
||||
import { MESSAGES } from './constants';
|
||||
import { ExceptionsZone } from './errors/exceptions-zone';
|
||||
import { loadAdapter } from './helpers/load-adapter';
|
||||
import { NestContainer } from './injector/container';
|
||||
import { InstanceLoader } from './injector/instance-loader';
|
||||
import { MetadataScanner } from './metadata-scanner';
|
||||
@@ -58,8 +59,8 @@ export class NestFactoryStatic {
|
||||
applicationConfig,
|
||||
appOptions,
|
||||
);
|
||||
const target = this.createAdapterProxy<T>(instance, httpServer);
|
||||
return this.createNestInstance<T>(target);
|
||||
const target = this.createNestInstance(instance);
|
||||
return this.createAdapterProxy<T>(target, httpServer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +156,17 @@ export class NestFactoryStatic {
|
||||
return;
|
||||
}
|
||||
if (isFunction(receiver[prop])) {
|
||||
return (...args: any[]) => {
|
||||
return this.createExceptionZone(receiver, prop);
|
||||
}
|
||||
return receiver[prop];
|
||||
};
|
||||
}
|
||||
|
||||
private createExceptionZone(
|
||||
receiver: Record<string, any>,
|
||||
prop: string,
|
||||
): Function {
|
||||
return (...args: unknown[]) => {
|
||||
let result;
|
||||
ExceptionsZone.run(() => {
|
||||
result = receiver[prop](...args);
|
||||
@@ -163,9 +174,6 @@ export class NestFactoryStatic {
|
||||
return result;
|
||||
};
|
||||
}
|
||||
return receiver[prop];
|
||||
};
|
||||
}
|
||||
|
||||
private applyLogger(options: NestApplicationContextOptions | undefined) {
|
||||
if (!options) {
|
||||
@@ -175,10 +183,7 @@ export class NestFactoryStatic {
|
||||
}
|
||||
|
||||
private createHttpAdapter<T = any>(httpServer?: T): HttpServer {
|
||||
const { ExpressAdapter } = loadPackage(
|
||||
'@nestjs/platform-express',
|
||||
'NestFactory',
|
||||
);
|
||||
const { ExpressAdapter } = loadAdapter('@nestjs/platform-express', 'HTTP');
|
||||
return new ExpressAdapter(httpServer);
|
||||
}
|
||||
|
||||
@@ -192,11 +197,11 @@ export class NestFactoryStatic {
|
||||
return (new Proxy(app, {
|
||||
get: (receiver: Record<string, any>, prop: string) => {
|
||||
if (!(prop in receiver) && prop in adapter) {
|
||||
return adapter[prop];
|
||||
return this.createExceptionZone(receiver, prop);
|
||||
}
|
||||
return receiver[prop];
|
||||
},
|
||||
}) as any) as T;
|
||||
}) as unknown) as T;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ExceptionsHandler } from '../exceptions/exceptions-handler';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '../helpers/execution-context-host';
|
||||
|
||||
export type RouterProxyCallback = <TRequest, TResponse>(
|
||||
req?: TRequest,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Logger } from '../../../common/services/logger.service';
|
||||
import { AbstractHttpAdapter } from '../../adapters';
|
||||
import { InvalidExceptionFilterException } from '../../errors/exceptions/invalid-exception-filter.exception';
|
||||
import { ExceptionsHandler } from '../../exceptions/exceptions-handler';
|
||||
import { ExecutionContextHost } from '../../helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '../../helpers/execution-context-host';
|
||||
import { NoopHttpAdapter } from './../utils/noop-adapter';
|
||||
|
||||
describe('ExceptionsHandler', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { ExecutionContextHost } from '../../helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '../../helpers/execution-context-host';
|
||||
|
||||
describe('ExecutionContextHost', () => {
|
||||
let contextHost: ExecutionContextHost;
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('ExternalContextCreator', () => {
|
||||
describe('when can not activate', () => {
|
||||
it('should throw exception when "tryActivate" returns false', () => {
|
||||
sinon.stub(guardsConsumer, 'tryActivate', () => false);
|
||||
expect(proxyContext(1, 2, 3)).to.eventually.throw;
|
||||
proxyContext(1, 2, 3).catch(err => expect(err).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
describe('when can activate', () => {
|
||||
|
||||
@@ -127,7 +127,9 @@ describe('RouterExecutionContext', () => {
|
||||
});
|
||||
it('should throw exception when "tryActivate" returns false', () => {
|
||||
sinon.stub(guardsConsumer, 'tryActivate').callsFake(() => false);
|
||||
expect(proxyContext(request, response, next)).to.eventually.throw();
|
||||
proxyContext(request, response, next).catch(
|
||||
error => expect(error).to.not.be.undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -257,7 +259,7 @@ describe('RouterExecutionContext', () => {
|
||||
it('should throw exception when "tryActivate" returns false', () => {
|
||||
const guardsFn = contextCreator.createGuardsFn([null], null, null);
|
||||
sinon.stub(guardsConsumer, 'tryActivate').callsFake(() => false);
|
||||
expect(guardsFn([])).to.eventually.throw();
|
||||
guardsFn([]).catch(err => expect(err).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
describe('createHandleResponseFn', () => {
|
||||
|
||||
@@ -165,9 +165,9 @@ describe('DependenciesScanner', () => {
|
||||
});
|
||||
describe('when "related" is nil', () => {
|
||||
it('should throw exception', () => {
|
||||
expect(
|
||||
scanner.insertRelatedModule(undefined, [] as any, 'test'),
|
||||
).to.eventually.throws();
|
||||
scanner
|
||||
.insertRelatedModule(undefined, [] as any, 'test')
|
||||
.catch(err => expect(err).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { Observable } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { RpcExceptionsHandler } from '../exceptions/rpc-exceptions-handler';
|
||||
|
||||
@@ -21,8 +21,6 @@ import { ServerFactory } from './server/server-factory';
|
||||
|
||||
const { SocketModule } =
|
||||
optional('@nestjs/websockets/socket-module') || ({} as any);
|
||||
const { IoAdapter } =
|
||||
optional('@nestjs/websockets/adapters/io-adapter') || ({} as any);
|
||||
|
||||
export class NestMicroservice extends NestApplicationContext
|
||||
implements INestMicroservice {
|
||||
@@ -49,6 +47,7 @@ export class NestMicroservice extends NestApplicationContext
|
||||
}
|
||||
|
||||
public registerWsAdapter() {
|
||||
const { IoAdapter } = optional('@nestjs/platform-socket.io') || ({} as any);
|
||||
const ioAdapter = IoAdapter ? new IoAdapter() : null;
|
||||
this.applicationConfig.setIoAdapter(ioAdapter);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import { join } from 'path';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -7,6 +8,11 @@ import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.e
|
||||
import { InvalidGrpcServiceException } from '../../errors/invalid-grpc-service.exception';
|
||||
import { InvalidProtoDefinitionException } from '../../errors/invalid-proto-definition.exception';
|
||||
// tslint:disable:no-string-literal
|
||||
class NoopLogger extends Logger {
|
||||
log(message: any, context?: string): void {}
|
||||
error(message: any, trace?: string, context?: string): void {}
|
||||
warn(message: any, context?: string): void {}
|
||||
}
|
||||
|
||||
class GrpcService {
|
||||
test = null;
|
||||
@@ -188,9 +194,13 @@ describe('ClientGrpcProxy', () => {
|
||||
describe('when package does not exist', () => {
|
||||
it('should throw "InvalidGrpcPackageException"', () => {
|
||||
sinon.stub(client, 'lookupPackage').callsFake(() => null);
|
||||
expect(() => client.createClient()).to.throw(
|
||||
InvalidGrpcPackageException,
|
||||
);
|
||||
(client as any).logger = new NoopLogger();
|
||||
|
||||
try {
|
||||
client.createClient();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceof(InvalidGrpcPackageException);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -201,6 +211,7 @@ describe('ClientGrpcProxy', () => {
|
||||
sinon.stub(client, 'getOptionsProp').callsFake(() => {
|
||||
throw new Error();
|
||||
});
|
||||
(client as any).logger = new NoopLogger();
|
||||
expect(() => client.loadProto()).to.throws(
|
||||
InvalidProtoDefinitionException,
|
||||
);
|
||||
@@ -231,7 +242,7 @@ describe('ClientGrpcProxy', () => {
|
||||
|
||||
describe('connect', () => {
|
||||
it('should throw exception', () => {
|
||||
expect(client.connect()).to.eventually.throws(Error);
|
||||
client.connect().catch(error => expect(error).to.be.instanceof(Error));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,7 +133,9 @@ describe('RpcContextCreator', () => {
|
||||
);
|
||||
const data = 'test';
|
||||
|
||||
expect(proxy(data)).to.eventually.rejectedWith(RpcException);
|
||||
proxy(null, data).catch(err =>
|
||||
expect(err).to.be.instanceOf(RpcException),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -167,7 +169,7 @@ describe('RpcContextCreator', () => {
|
||||
it('should throw exception when "tryActivate" returns false', () => {
|
||||
const guardsFn = contextCreator.createGuardsFn([null], null, null);
|
||||
sinon.stub(guardsConsumer, 'tryActivate').callsFake(() => false);
|
||||
expect(guardsFn([])).to.eventually.throw();
|
||||
guardsFn([]).catch(err => expect(err).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import { join } from 'path';
|
||||
import { of } from 'rxjs';
|
||||
@@ -5,6 +6,12 @@ import * as sinon from 'sinon';
|
||||
import { InvalidGrpcPackageException } from '../../errors/invalid-grpc-package.exception';
|
||||
import { ServerGrpc } from '../../server/server-grpc';
|
||||
|
||||
class NoopLogger extends Logger {
|
||||
log(message: any, context?: string): void {}
|
||||
error(message: any, trace?: string, context?: string): void {}
|
||||
warn(message: any, context?: string): void {}
|
||||
}
|
||||
|
||||
describe('ServerGrpc', () => {
|
||||
let server: ServerGrpc;
|
||||
beforeEach(() => {
|
||||
@@ -42,11 +49,14 @@ describe('ServerGrpc', () => {
|
||||
|
||||
describe('bindEvents', () => {
|
||||
describe('when package does not exist', () => {
|
||||
it('should throw "InvalidGrpcPackageException"', () => {
|
||||
it('should throw "InvalidGrpcPackageException"', async () => {
|
||||
sinon.stub(server, 'lookupPackage').callsFake(() => null);
|
||||
expect(server.bindEvents()).to.eventually.throws(
|
||||
InvalidGrpcPackageException,
|
||||
);
|
||||
(server as any).logger = new NoopLogger();
|
||||
try {
|
||||
await server.bindEvents();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceof(InvalidGrpcPackageException);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('when package exist', () => {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"url": "https://github.com/nestjs/nest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/express": "^4.0.39",
|
||||
"body-parser": "1.18.3",
|
||||
"cors": "2.8.4",
|
||||
"express": "4.16.3",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CallHandler } from '@nestjs/common';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { expect } from 'chai';
|
||||
import { of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
@@ -57,9 +57,9 @@ describe('FileFieldsInterceptor', () => {
|
||||
(target as any).fields = {
|
||||
array: () => callback,
|
||||
};
|
||||
expect(
|
||||
target.intercept(new ExecutionContextHost([]), handler),
|
||||
).to.eventually.throw();
|
||||
(target.intercept(new ExecutionContextHost([]), handler) as any).catch(
|
||||
error => expect(error).to.not.be.undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CallHandler } from '@nestjs/common';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { expect } from 'chai';
|
||||
import { of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
@@ -39,9 +39,9 @@ describe('FileInterceptor', () => {
|
||||
(target as any).multer = {
|
||||
single: () => callback,
|
||||
};
|
||||
expect(
|
||||
target.intercept(new ExecutionContextHost([]), handler),
|
||||
).to.eventually.throw();
|
||||
(target.intercept(new ExecutionContextHost([]), handler) as any).catch(
|
||||
error => expect(error).to.not.be.undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CallHandler } from '@nestjs/common';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { expect } from 'chai';
|
||||
import { of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
@@ -41,9 +41,9 @@ describe('FilesInterceptor', () => {
|
||||
(target as any).multer = {
|
||||
array: () => callback,
|
||||
};
|
||||
expect(
|
||||
target.intercept(new ExecutionContextHost([]), handler),
|
||||
).to.eventually.throw();
|
||||
(target.intercept(new ExecutionContextHost([]), handler) as any).catch(
|
||||
error => expect(error).to.not.be.undefined,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RequestMethod } from '@nestjs/common';
|
||||
import { ErrorHandler, RequestHandler } from '@nestjs/common/interfaces';
|
||||
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
|
||||
import { NestApplicationOptions } from '@nestjs/common/interfaces/nest-application-options.interface';
|
||||
import { loadPackage } from '@nestjs/common/utils/load-package.util';
|
||||
@@ -24,54 +23,6 @@ export class FastifyAdapter extends AbstractHttpAdapter {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
public use(handler: RequestHandler | ErrorHandler);
|
||||
public use(path: any, handler: RequestHandler | ErrorHandler);
|
||||
public use(...args: any[]) {
|
||||
return this.instance.use(...args);
|
||||
}
|
||||
|
||||
public get(handler: RequestHandler);
|
||||
public get(path: any, handler: RequestHandler);
|
||||
public get(...args: any[]) {
|
||||
return this.instance.get(...args);
|
||||
}
|
||||
|
||||
public post(handler: RequestHandler);
|
||||
public post(path: any, handler: RequestHandler);
|
||||
public post(...args: any[]) {
|
||||
return this.instance.post(...args);
|
||||
}
|
||||
|
||||
public head(handler: RequestHandler);
|
||||
public head(path: any, handler: RequestHandler);
|
||||
public head(...args: any[]) {
|
||||
return this.instance.head(...args);
|
||||
}
|
||||
|
||||
public delete(handler: RequestHandler);
|
||||
public delete(path: any, handler: RequestHandler);
|
||||
public delete(...args: any[]) {
|
||||
return this.instance.delete(...args);
|
||||
}
|
||||
|
||||
public put(handler: RequestHandler);
|
||||
public put(path: any, handler: RequestHandler);
|
||||
public put(...args: any[]) {
|
||||
return this.instance.put(...args);
|
||||
}
|
||||
|
||||
public patch(handler: RequestHandler);
|
||||
public patch(path: any, handler: RequestHandler);
|
||||
public patch(...args: any[]) {
|
||||
return this.instance.patch(...args);
|
||||
}
|
||||
|
||||
public options(handler: RequestHandler);
|
||||
public options(path: any, handler: RequestHandler);
|
||||
public options(...args: any[]) {
|
||||
return this.instance.options(...args);
|
||||
}
|
||||
|
||||
public listen(port: string | number, callback?: () => void);
|
||||
public listen(port: string | number, hostname: string, callback?: () => void);
|
||||
public listen(port: any, hostname?: any, callback?: any) {
|
||||
|
||||
76
packages/platform-socket.io/Readme.md
Normal file
76
packages/platform-socket.io/Readme.md
Normal file
@@ -0,0 +1,76 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/nestjs/nest
|
||||
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
|
||||
[linux-url]: https://travis-ci.org/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
|
||||
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
|
||||
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
<p>Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://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>
|
||||
|
||||
<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 e.g. <a href="https://github.com/fastify/fastify" target="blank">Fastify</a>, allowing for easy use of the myriad 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, 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, loosely coupled and easily maintainable applications.</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:
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" /></a>
|
||||
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://scal.io"><img src="https://nestjs.com/img/scalio-logo.svg" width="110" /></a> <a href="http://angularity.io"><img src="http://angularity.io/media/logo.svg" height="30" /></a> <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> --> <a href="https://hostpresto.com"><img src="https://nestjs.com/img/hostpresto.png" height="30" /></a> <a href="https://genuinebee.com/"><img src="https://nestjs.com/img/genuinebee.svg" height="38" /></a> <a href="http://architectnow.net/"><img src="https://nestjs.com/img/architectnow.png" height="24" /></a> <a href="https://quander.io/"><img src="https://nestjs.com/img/quander.png" height="28" /></a>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
<a href="https://opencollective.com/nest"><img src="https://opencollective.com/nest/backers.svg?width=890"></a>
|
||||
|
||||
## Stay in touch
|
||||
|
||||
* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
||||
1
packages/platform-socket.io/adapters/index.ts
Normal file
1
packages/platform-socket.io/adapters/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './io-adapter';
|
||||
@@ -1,26 +1,14 @@
|
||||
import { INestApplicationContext, WebSocketAdapter } from '@nestjs/common';
|
||||
import { isFunction, isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import { NestApplication } from '@nestjs/core';
|
||||
import { Server } from 'http';
|
||||
import {
|
||||
AbstractWsAdapter,
|
||||
MessageMappingProperties,
|
||||
} from '@nestjs/websockets';
|
||||
import { DISCONNECT_EVENT } from '@nestjs/websockets/constants';
|
||||
import { fromEvent, Observable } from 'rxjs';
|
||||
import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';
|
||||
import * as io from 'socket.io';
|
||||
import { CONNECTION_EVENT, DISCONNECT_EVENT } from '../constants';
|
||||
import { MessageMappingProperties } from '../gateway-metadata-explorer';
|
||||
import { BaseWsExceptionFilter } from './../exceptions/base-ws-exception-filter';
|
||||
|
||||
export class IoAdapter implements WebSocketAdapter {
|
||||
protected readonly baseExceptionFilter = new BaseWsExceptionFilter();
|
||||
protected readonly httpServer: Server;
|
||||
|
||||
constructor(appOrHttpServer?: INestApplicationContext | Server) {
|
||||
if (appOrHttpServer && appOrHttpServer instanceof NestApplication) {
|
||||
this.httpServer = appOrHttpServer.getUnderlyingHttpServer();
|
||||
} else {
|
||||
this.httpServer = appOrHttpServer as Server;
|
||||
}
|
||||
}
|
||||
|
||||
export class IoAdapter extends AbstractWsAdapter {
|
||||
public create(
|
||||
port: number,
|
||||
options?: any & { namespace?: string; server?: any },
|
||||
@@ -43,14 +31,6 @@ export class IoAdapter implements WebSocketAdapter {
|
||||
return io(port, options);
|
||||
}
|
||||
|
||||
public bindClientConnect(server: any, callback: (...args: any[]) => void) {
|
||||
server.on(CONNECTION_EVENT, callback);
|
||||
}
|
||||
|
||||
public bindClientDisconnect(client: any, callback: (...args: any[]) => void) {
|
||||
client.on(DISCONNECT_EVENT, callback);
|
||||
}
|
||||
|
||||
public bindMessageHandlers(
|
||||
client: any,
|
||||
handlers: MessageMappingProperties[],
|
||||
@@ -72,15 +52,12 @@ export class IoAdapter implements WebSocketAdapter {
|
||||
}),
|
||||
takeUntil(disconnect$),
|
||||
);
|
||||
const onMessage = ([response, ack]) => {
|
||||
source$.subscribe(([response, ack]) => {
|
||||
if (response.event) {
|
||||
return client.emit(response.event, response.data);
|
||||
}
|
||||
isFunction(ack) && ack(response);
|
||||
};
|
||||
const onError = (err: any) =>
|
||||
this.baseExceptionFilter.handleError(client, err);
|
||||
source$.subscribe(onMessage as any, onError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,12 +76,4 @@ export class IoAdapter implements WebSocketAdapter {
|
||||
}
|
||||
return { data: payload };
|
||||
}
|
||||
|
||||
public bindMiddleware(server, middleware: (socket, next) => void) {
|
||||
server.use(middleware);
|
||||
}
|
||||
|
||||
public close(server: any) {
|
||||
isFunction(server.close) && server.close();
|
||||
}
|
||||
}
|
||||
8
packages/platform-socket.io/index.ts
Normal file
8
packages/platform-socket.io/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Nest @platform-socket.io
|
||||
* Copyright(c) 2017 - 2018 Kamil Mysliwiec
|
||||
* https://nestjs.com
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
export * from './adapters';
|
||||
19
packages/platform-socket.io/package.json
Normal file
19
packages/platform-socket.io/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@nestjs/platform-socket.io",
|
||||
"version": "5.4.1",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@platform-socket.io)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nestjs/nest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/express": "^4.0.39",
|
||||
"body-parser": "1.18.3",
|
||||
"cors": "2.8.4",
|
||||
"express": "4.16.3",
|
||||
"multer": "1.3.0"
|
||||
},
|
||||
"peerDependencies": {}
|
||||
}
|
||||
7
packages/platform-socket.io/tsconfig.json
Normal file
7
packages/platform-socket.io/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"include": ["*.ts", "**/*.ts"]
|
||||
}
|
||||
76
packages/platform-ws/Readme.md
Normal file
76
packages/platform-ws/Readme.md
Normal file
@@ -0,0 +1,76 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/nestjs/nest
|
||||
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
|
||||
[linux-url]: https://travis-ci.org/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
|
||||
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
|
||||
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
<p>Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://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>
|
||||
|
||||
<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 e.g. <a href="https://github.com/fastify/fastify" target="blank">Fastify</a>, allowing for easy use of the myriad 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, 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, loosely coupled and easily maintainable applications.</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:
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="220" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" /></a>
|
||||
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://scal.io"><img src="https://nestjs.com/img/scalio-logo.svg" width="110" /></a> <a href="http://angularity.io"><img src="http://angularity.io/media/logo.svg" height="30" /></a> <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> --> <a href="https://hostpresto.com"><img src="https://nestjs.com/img/hostpresto.png" height="30" /></a> <a href="https://genuinebee.com/"><img src="https://nestjs.com/img/genuinebee.svg" height="38" /></a> <a href="http://architectnow.net/"><img src="https://nestjs.com/img/architectnow.png" height="24" /></a> <a href="https://quander.io/"><img src="https://nestjs.com/img/quander.png" height="28" /></a>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
<a href="https://opencollective.com/nest"><img src="https://opencollective.com/nest/backers.svg?width=890"></a>
|
||||
|
||||
## Stay in touch
|
||||
|
||||
* Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
||||
1
packages/platform-ws/adapters/index.ts
Normal file
1
packages/platform-ws/adapters/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './ws-adapter';
|
||||
103
packages/platform-ws/adapters/ws-adapter.ts
Normal file
103
packages/platform-ws/adapters/ws-adapter.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { INestApplicationContext, Logger } from '@nestjs/common';
|
||||
import { loadPackage } from '@nestjs/common/utils/load-package.util';
|
||||
import { AbstractWsAdapter } from '@nestjs/websockets';
|
||||
import {
|
||||
CLOSE_EVENT,
|
||||
CONNECTION_EVENT,
|
||||
ERROR_EVENT,
|
||||
} from '@nestjs/websockets/constants';
|
||||
import { MessageMappingProperties } from '@nestjs/websockets/gateway-metadata-explorer';
|
||||
import { EMPTY as empty, fromEvent, Observable } from 'rxjs';
|
||||
import { filter, first, mergeMap, share, takeUntil } from 'rxjs/operators';
|
||||
|
||||
let wsPackage: any = {};
|
||||
|
||||
enum READY_STATE {
|
||||
CONNECTING_STATE = 0,
|
||||
OPEN_STATE = 1,
|
||||
CLOSING_STATE = 2,
|
||||
CLOSED_STATE = 3,
|
||||
}
|
||||
|
||||
export class WsAdapter extends AbstractWsAdapter {
|
||||
protected readonly logger = new Logger(WsAdapter.name);
|
||||
|
||||
constructor(appOrHttpServer?: INestApplicationContext | any) {
|
||||
super(appOrHttpServer);
|
||||
wsPackage = loadPackage('ws', 'WsAdapter');
|
||||
}
|
||||
|
||||
public create(
|
||||
port: number,
|
||||
options?: any & { namespace?: string; server?: any },
|
||||
): any {
|
||||
const { server, ...wsOptions } = options;
|
||||
if (port === 0 && this.httpServer) {
|
||||
return this.bindErrorHandler(
|
||||
new wsPackage.Server({
|
||||
server: this.httpServer,
|
||||
...wsOptions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return server
|
||||
? server
|
||||
: this.bindErrorHandler(
|
||||
new wsPackage.Server({
|
||||
port,
|
||||
...wsOptions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public bindMessageHandlers(
|
||||
client: any,
|
||||
handlers: MessageMappingProperties[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
) {
|
||||
const close$ = fromEvent(client, CLOSE_EVENT).pipe(
|
||||
share(),
|
||||
first(),
|
||||
);
|
||||
const source$ = fromEvent(client, 'message').pipe(
|
||||
mergeMap(data =>
|
||||
this.bindMessageHandler(data, handlers, transform).pipe(
|
||||
filter(result => result),
|
||||
),
|
||||
),
|
||||
takeUntil(close$),
|
||||
);
|
||||
const onMessage = (response: any) => {
|
||||
if (client.readyState !== READY_STATE.OPEN_STATE) {
|
||||
return;
|
||||
}
|
||||
client.send(JSON.stringify(response));
|
||||
};
|
||||
source$.subscribe(onMessage);
|
||||
}
|
||||
|
||||
public bindMessageHandler(
|
||||
buffer: any,
|
||||
handlers: MessageMappingProperties[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
): Observable<any> {
|
||||
try {
|
||||
const message = JSON.parse(buffer.data);
|
||||
const messageHandler = handlers.find(
|
||||
handler => handler.message === message.event,
|
||||
);
|
||||
const { callback } = messageHandler;
|
||||
return transform(callback(message.data));
|
||||
} catch {
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
public bindErrorHandler(server: any) {
|
||||
server.on(CONNECTION_EVENT, ws =>
|
||||
ws.on(ERROR_EVENT, (err: any) => this.logger.error(err)),
|
||||
);
|
||||
server.on(ERROR_EVENT, (err: any) => this.logger.error(err));
|
||||
return server;
|
||||
}
|
||||
}
|
||||
8
packages/platform-ws/index.ts
Normal file
8
packages/platform-ws/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Nest @platform-ws
|
||||
* Copyright(c) 2017 - 2018 Kamil Mysliwiec
|
||||
* https://nestjs.com
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
export * from './adapters';
|
||||
19
packages/platform-ws/package.json
Normal file
19
packages/platform-ws/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@nestjs/platform-ws",
|
||||
"version": "5.4.1",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@platform-ws)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nestjs/nest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/express": "^4.0.39",
|
||||
"body-parser": "1.18.3",
|
||||
"cors": "2.8.4",
|
||||
"express": "4.16.3",
|
||||
"multer": "1.3.0"
|
||||
},
|
||||
"peerDependencies": {}
|
||||
}
|
||||
7
packages/platform-ws/tsconfig.json
Normal file
7
packages/platform-ws/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"include": ["*.ts", "**/*.ts"]
|
||||
}
|
||||
@@ -1,123 +1,47 @@
|
||||
import {
|
||||
INestApplicationContext,
|
||||
Logger,
|
||||
WebSocketAdapter,
|
||||
} from '@nestjs/common';
|
||||
import { loadPackage } from '@nestjs/common/utils/load-package.util';
|
||||
import { INestApplicationContext, WebSocketAdapter } from '@nestjs/common';
|
||||
import { WsMessageHandler } from '@nestjs/common/interfaces';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { NestApplication } from '@nestjs/core';
|
||||
import { Server } from 'http';
|
||||
import { EMPTY as empty, fromEvent, Observable } from 'rxjs';
|
||||
import { filter, first, mergeMap, share, takeUntil } from 'rxjs/operators';
|
||||
import { CLOSE_EVENT, CONNECTION_EVENT, ERROR_EVENT } from '../constants';
|
||||
import { MessageMappingProperties } from '../gateway-metadata-explorer';
|
||||
import { BaseWsExceptionFilter } from './../exceptions/base-ws-exception-filter';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CONNECTION_EVENT, DISCONNECT_EVENT } from '../constants';
|
||||
|
||||
let wsPackage: any = {};
|
||||
|
||||
enum READY_STATE {
|
||||
CONNECTING_STATE = 0,
|
||||
OPEN_STATE = 1,
|
||||
CLOSING_STATE = 2,
|
||||
CLOSED_STATE = 3,
|
||||
export interface BaseWsInstance {
|
||||
on: (event: string, callback: Function) => void;
|
||||
close: Function;
|
||||
}
|
||||
|
||||
export class WsAdapter implements WebSocketAdapter {
|
||||
protected readonly baseExceptionFilter = new BaseWsExceptionFilter();
|
||||
protected readonly logger = new Logger(WsAdapter.name);
|
||||
protected readonly httpServer: Server;
|
||||
export abstract class AbstractWsAdapter<
|
||||
TServer extends BaseWsInstance = any,
|
||||
TClient extends BaseWsInstance = any,
|
||||
TOptions = any
|
||||
> implements WebSocketAdapter<TServer, TClient, TOptions> {
|
||||
protected readonly httpServer: any;
|
||||
|
||||
constructor(appOrHttpServer?: INestApplicationContext | Server) {
|
||||
wsPackage = loadPackage('ws', 'WsAdapter');
|
||||
constructor(appOrHttpServer?: INestApplicationContext | any) {
|
||||
if (appOrHttpServer && appOrHttpServer instanceof NestApplication) {
|
||||
this.httpServer = appOrHttpServer.getUnderlyingHttpServer();
|
||||
} else {
|
||||
this.httpServer = appOrHttpServer as Server;
|
||||
this.httpServer = appOrHttpServer;
|
||||
}
|
||||
}
|
||||
|
||||
public create(
|
||||
port: number,
|
||||
options?: any & { namespace?: string; server?: any },
|
||||
): any {
|
||||
const { server, ...wsOptions } = options;
|
||||
if (port === 0 && this.httpServer) {
|
||||
return this.bindErrorHandler(
|
||||
new wsPackage.Server({
|
||||
server: this.httpServer,
|
||||
...wsOptions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return server
|
||||
? server
|
||||
: this.bindErrorHandler(
|
||||
new wsPackage.Server({
|
||||
port,
|
||||
...wsOptions,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public bindClientConnect(server, callback: (...args: any[]) => void) {
|
||||
public bindClientConnect(server: TServer, callback: Function) {
|
||||
server.on(CONNECTION_EVENT, callback);
|
||||
}
|
||||
|
||||
public bindClientDisconnect(client, callback: (...args: any[]) => void) {
|
||||
client.on(CLOSE_EVENT, callback);
|
||||
public bindClientDisconnect(client: TClient, callback: Function) {
|
||||
client.on(DISCONNECT_EVENT, callback);
|
||||
}
|
||||
|
||||
public bindMessageHandlers(
|
||||
client: any,
|
||||
handlers: MessageMappingProperties[],
|
||||
public close(server: TServer) {
|
||||
const isCallable = server && isFunction(server.close);
|
||||
isCallable && server.close();
|
||||
}
|
||||
|
||||
public abstract create(port: number, options?: TOptions): TServer;
|
||||
public abstract bindMessageHandlers(
|
||||
client: TClient,
|
||||
handlers: WsMessageHandler[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
) {
|
||||
const close$ = fromEvent(client, CLOSE_EVENT).pipe(share(), first());
|
||||
const source$ = fromEvent(client, 'message').pipe(
|
||||
mergeMap(data =>
|
||||
this.bindMessageHandler(data, handlers, transform).pipe(
|
||||
filter(result => result),
|
||||
),
|
||||
),
|
||||
takeUntil(close$),
|
||||
);
|
||||
const onMessage = (response: any) => {
|
||||
if (client.readyState !== READY_STATE.OPEN_STATE) {
|
||||
return;
|
||||
}
|
||||
client.send(JSON.stringify(response));
|
||||
};
|
||||
const onError = (err: any) =>
|
||||
this.baseExceptionFilter.handleError(client, err);
|
||||
source$.subscribe(onMessage, onError);
|
||||
}
|
||||
|
||||
public bindMessageHandler(
|
||||
buffer: any,
|
||||
handlers: MessageMappingProperties[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
): Observable<any> {
|
||||
try {
|
||||
const message = JSON.parse(buffer.data);
|
||||
const messageHandler = handlers.find(
|
||||
handler => handler.message === message.event,
|
||||
);
|
||||
const { callback } = messageHandler;
|
||||
return transform(callback(message.data));
|
||||
} catch {
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
public close(server: any) {
|
||||
server && isFunction(server.close) && server.close();
|
||||
}
|
||||
|
||||
public bindErrorHandler(server: any) {
|
||||
server.on(CONNECTION_EVENT, ws =>
|
||||
ws.on(ERROR_EVENT, (err: any) => this.logger.error(err)),
|
||||
);
|
||||
server.on(ERROR_EVENT, (err: any) => this.logger.error(err));
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
import { empty } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { WsExceptionsHandler } from '../exceptions/ws-exceptions-handler';
|
||||
|
||||
@@ -2,6 +2,6 @@ import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.excepti
|
||||
|
||||
export class InvalidSocketPortException extends RuntimeException {
|
||||
constructor(port: number | string, type: any) {
|
||||
super(`Invalid port (${port}) in gateway ${type}!`);
|
||||
super(`Invalid port (${port}) in gateway ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
*/
|
||||
import 'reflect-metadata';
|
||||
|
||||
export * from './adapters/io-adapter';
|
||||
export * from './adapters/ws-adapter';
|
||||
export * from './adapters';
|
||||
export * from './errors';
|
||||
export * from './exceptions';
|
||||
export { MessageMappingProperties } from './gateway-metadata-explorer';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "@nestjs/websockets",
|
||||
"version": "5.5.0",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@websockets)",
|
||||
"description":
|
||||
"Nest - modern, fast, powerful node.js web framework (@websockets)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -9,8 +10,7 @@
|
||||
"url": "https://github.com/nestjs/nest"
|
||||
},
|
||||
"dependencies": {
|
||||
"iterare": "0.0.8",
|
||||
"socket.io": "^2.1.1"
|
||||
"iterare": "0.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^5.0.0",
|
||||
|
||||
@@ -27,27 +27,32 @@ export class SocketModule {
|
||||
|
||||
public register(container: NestContainer, config: ApplicationConfig) {
|
||||
this.applicationConfig = config;
|
||||
this.webSocketsController = new WebSocketsController(
|
||||
new SocketServerProvider(this.socketsContainer, config),
|
||||
const serverProvider = new SocketServerProvider(
|
||||
this.socketsContainer,
|
||||
config,
|
||||
this.getContextCreator(container),
|
||||
);
|
||||
const contextCreator = this.getContextCreator(container);
|
||||
this.webSocketsController = new WebSocketsController(
|
||||
serverProvider,
|
||||
config,
|
||||
contextCreator,
|
||||
);
|
||||
const modules = container.getModules();
|
||||
modules.forEach(({ providers }, moduleName: string) =>
|
||||
this.hookGatewaysIntoServers(providers, moduleName),
|
||||
this.mergeAllGateways(providers, moduleName),
|
||||
);
|
||||
}
|
||||
|
||||
public hookGatewaysIntoServers(
|
||||
public mergeAllGateways(
|
||||
providers: Map<string, InstanceWrapper<Injectable>>,
|
||||
moduleName: string,
|
||||
) {
|
||||
providers.forEach(wrapper =>
|
||||
this.hookGatewayIntoServer(wrapper, moduleName),
|
||||
this.mergeGatewayAndServer(wrapper, moduleName),
|
||||
);
|
||||
}
|
||||
|
||||
public hookGatewayIntoServer(
|
||||
public mergeGatewayAndServer(
|
||||
wrapper: InstanceWrapper<Injectable>,
|
||||
moduleName: string,
|
||||
) {
|
||||
@@ -59,7 +64,7 @@ export class SocketModule {
|
||||
if (!metadataKeys.includes(GATEWAY_METADATA)) {
|
||||
return;
|
||||
}
|
||||
this.webSocketsController.hookGatewayIntoServer(
|
||||
this.webSocketsController.mergeGatewayAndServer(
|
||||
instance as NestGateway,
|
||||
metatype,
|
||||
moduleName,
|
||||
|
||||
@@ -129,8 +129,9 @@ describe('WsContextCreator', () => {
|
||||
module,
|
||||
);
|
||||
const data = 'test';
|
||||
|
||||
expect(proxy(null, data)).to.eventually.rejectedWith(WsException);
|
||||
proxy(null, data).catch(err =>
|
||||
expect(err).to.be.instanceOf(WsException),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -164,7 +165,7 @@ describe('WsContextCreator', () => {
|
||||
it('should throw exception when "tryActivate" returns false', () => {
|
||||
const guardsFn = contextCreator.createGuardsFn([null], null, null);
|
||||
sinon.stub(guardsConsumer, 'tryActivate').callsFake(() => false);
|
||||
expect(guardsFn([])).to.eventually.throw();
|
||||
guardsFn([]).catch(err => expect(err).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { WsExceptionsHandler } from '../../exceptions/ws-exceptions-handler';
|
||||
import { WsException } from '../../errors/ws-exception';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context.host';
|
||||
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
||||
|
||||
describe('WsExceptionsHandler', () => {
|
||||
let handler: WsExceptionsHandler;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { SocketServerProvider } from '../socket-server-provider';
|
||||
import { SocketsContainer } from '../container';
|
||||
import { ApplicationConfig } from '@nestjs/core/application-config';
|
||||
import { IoAdapter } from '@nestjs/websockets/adapters/io-adapter';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { SocketsContainer } from '../container';
|
||||
import { SocketServerProvider } from '../socket-server-provider';
|
||||
import { AbstractWsAdapter } from './../adapters/ws-adapter';
|
||||
|
||||
class NoopAdapter extends AbstractWsAdapter {
|
||||
public create(port: number, options?: any) {}
|
||||
public bindMessageHandlers(client: any, handlers) {}
|
||||
}
|
||||
|
||||
describe('SocketServerProvider', () => {
|
||||
let instance: SocketServerProvider;
|
||||
@@ -14,7 +19,7 @@ describe('SocketServerProvider', () => {
|
||||
mockContainer = sinon.mock(socketsContainer);
|
||||
instance = new SocketServerProvider(
|
||||
socketsContainer,
|
||||
new ApplicationConfig(new IoAdapter()),
|
||||
new ApplicationConfig(new NoopAdapter()),
|
||||
);
|
||||
});
|
||||
describe('scanForSocketServer', () => {
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
import { ApplicationConfig } from '@nestjs/core/application-config';
|
||||
import { expect } from 'chai';
|
||||
import { of } from 'rxjs';
|
||||
import { fromEvent, Observable, of } from 'rxjs';
|
||||
import * as sinon from 'sinon';
|
||||
import { MetadataScanner } from '../../core/metadata-scanner';
|
||||
import { AbstractWsAdapter } from '../adapters/ws-adapter';
|
||||
import { PORT_METADATA } from '../constants';
|
||||
import { WsContextCreator } from '../context/ws-context-creator';
|
||||
import { InvalidSocketPortException } from '../errors/invalid-socket-port.exception';
|
||||
import { GatewayMetadataExplorer } from '../gateway-metadata-explorer';
|
||||
import { IoAdapter } from '../index';
|
||||
import { SocketServerProvider } from '../socket-server-provider';
|
||||
import { WebSocketGateway } from '../utils/socket-gateway.decorator';
|
||||
import { WebSocketsController } from '../web-sockets-controller';
|
||||
|
||||
class NoopAdapter extends AbstractWsAdapter {
|
||||
public create(port: number, options?: any) {}
|
||||
public bindMessageHandlers(
|
||||
client: any,
|
||||
handlers,
|
||||
transform: (data: any) => Observable<any>,
|
||||
) {
|
||||
handlers.forEach(({ message, callback }) => {
|
||||
const source$ = fromEvent(client, message);
|
||||
source$.subscribe(data => null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('WebSocketsController', () => {
|
||||
let instance: WebSocketsController;
|
||||
let provider: SocketServerProvider,
|
||||
@@ -24,7 +38,7 @@ describe('WebSocketsController', () => {
|
||||
class Test {}
|
||||
|
||||
beforeEach(() => {
|
||||
config = new ApplicationConfig(new IoAdapter());
|
||||
config = new ApplicationConfig(new NoopAdapter());
|
||||
provider = new SocketServerProvider(null, config);
|
||||
mockProvider = sinon.mock(provider);
|
||||
instance = new WebSocketsController(
|
||||
@@ -33,7 +47,7 @@ describe('WebSocketsController', () => {
|
||||
sinon.createStubInstance(WsContextCreator),
|
||||
);
|
||||
});
|
||||
describe('hookGatewayIntoServer', () => {
|
||||
describe('mergeGatewayAndServer', () => {
|
||||
let subscribeObservableServer: sinon.SinonSpy;
|
||||
|
||||
@WebSocketGateway('test' as any)
|
||||
@@ -49,7 +63,7 @@ describe('WebSocketsController', () => {
|
||||
it('should throws "InvalidSocketPortException" when port is not a number', () => {
|
||||
Reflect.defineMetadata(PORT_METADATA, 'test', InvalidGateway);
|
||||
expect(() =>
|
||||
instance.hookGatewayIntoServer(
|
||||
instance.mergeGatewayAndServer(
|
||||
new InvalidGateway(),
|
||||
InvalidGateway,
|
||||
'',
|
||||
@@ -58,13 +72,13 @@ describe('WebSocketsController', () => {
|
||||
});
|
||||
it('should call "subscribeObservableServer" with default values when metadata is empty', () => {
|
||||
const gateway = new DefaultGateway();
|
||||
instance.hookGatewayIntoServer(gateway, DefaultGateway, '');
|
||||
instance.mergeGatewayAndServer(gateway, DefaultGateway, '');
|
||||
expect(subscribeObservableServer.calledWith(gateway, {}, 0, '')).to.be
|
||||
.true;
|
||||
});
|
||||
it('should call "subscribeObservableServer" when metadata is valid', () => {
|
||||
const gateway = new Test();
|
||||
instance.hookGatewayIntoServer(gateway, Test, '');
|
||||
instance.mergeGatewayAndServer(gateway, Test, '');
|
||||
expect(
|
||||
subscribeObservableServer.calledWith(gateway, { namespace }, port, ''),
|
||||
).to.be.true;
|
||||
@@ -303,7 +317,7 @@ describe('WebSocketsController', () => {
|
||||
});
|
||||
it('should bind each handler to client', () => {
|
||||
instance.subscribeMessages(handlers, client, gateway);
|
||||
expect(onSpy.calledThrice).to.be.true;
|
||||
expect(onSpy.calledTwice).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('pickResult', () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class WebSocketsController {
|
||||
private readonly contextCreator: WsContextCreator,
|
||||
) {}
|
||||
|
||||
public hookGatewayIntoServer(
|
||||
public mergeGatewayAndServer(
|
||||
instance: NestGateway,
|
||||
metatype: Type<any>,
|
||||
module: string,
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { NestFactory, FastifyAdapter } from '@nestjs/core';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
INestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { ApplicationModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(ApplicationModule, new FastifyAdapter());
|
||||
const app = await NestFactory.create<INestFastifyApplication>(
|
||||
ApplicationModule,
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
await app.listen(3000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user