Compare commits

..

2 Commits

Author SHA1 Message Date
Kamil Myśliwiec
e5612e0197 chore: merge 10.0.0 2023-06-12 10:58:30 +02:00
Kamil Myśliwiec
6c54448bf7 fix(fastify): validate middleware paths 2023-06-12 10:51:02 +02:00
186 changed files with 146229 additions and 108529 deletions

View File

@@ -30,7 +30,7 @@ jobs:
build:
working_directory: ~/nest
docker:
- image: cimg/node:16.20
- image: cimg/node:16.19
steps:
- checkout
- run:
@@ -52,7 +52,7 @@ jobs:
test_node_16:
working_directory: ~/nest
docker:
- image: cimg/node:16.20
- image: cimg/node:16.19
steps:
- checkout
- *restore-cache
@@ -70,12 +70,12 @@ jobs:
test_node_18:
<<: *unit-tests-template
docker:
- image: cimg/node:18.16
- image: cimg/node:18.14
test_node_19:
<<: *unit-tests-template
docker:
- image: cimg/node:19.9
- image: cimg/node:19.8
lint:
working_directory: ~/nest
@@ -130,7 +130,7 @@ jobs:
codechecks_benchmarks:
working_directory: ~/nest
docker:
- image: cimg/node:16.20
- image: cimg/node:16.19
steps:
- checkout
- *restore-cache
@@ -146,7 +146,7 @@ jobs:
samples:
working_directory: ~/nest
docker:
- image: cimg/node:16.20
- image: cimg/node:16.19
environment:
- DISABLE_OPENCOLLECTIVE: true
steps:

View File

@@ -6,7 +6,7 @@ body:
attributes:
value: |
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
If you are not sure that your issue is a bug, you could:
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page

View File

@@ -6,7 +6,7 @@ body:
attributes:
value: |
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
If you are not sure that your issue is a bug, you could:
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page

View File

@@ -6,7 +6,7 @@ body:
attributes:
value: |
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
If you are not sure that your issue is a bug, you could:
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page

View File

@@ -25,7 +25,7 @@ services:
- "9001:9001"
restart: always
mysql:
image: mysql:8.0.33
image: mysql:8.0.32
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test
@@ -50,7 +50,7 @@ services:
zookeeper:
container_name: test-zookeeper
hostname: zookeeper
image: confluentinc/cp-zookeeper:7.4.0
image: confluentinc/cp-zookeeper:7.3.2
ports:
- "2181:2181"
environment:
@@ -59,7 +59,7 @@ services:
kafka:
container_name: test-kafka
hostname: kafka
image: confluentinc/cp-kafka:7.4.0
image: confluentinc/cp-kafka:7.3.2
depends_on:
- zookeeper
ports:

View File

@@ -192,7 +192,7 @@ describe('Middleware (FastifyAdapter)', () => {
});
});
describe('should execute middleware only once for given routes', () => {
describe.only('should execute middleware only once for given routes', () => {
class Middleware implements NestMiddleware {
use(request: any, reply: any, next: () => void) {
if (request.middlewareExecutionCount === undefined) {

View File

@@ -4,7 +4,9 @@ import { CatsModule } from './cats/cats.module';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost:27017/test'),
MongooseModule.forRoot('mongodb://localhost:27017/test', {
useNewUrlParser: true,
}),
CatsModule,
],
})

View File

@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
createMongooseOptions(): MongooseModuleOptions {
return {
uri: 'mongodb://localhost:27017/test',
useNewUrlParser: true,
};
}
}

View File

@@ -10,6 +10,7 @@ class ConfigService implements MongooseOptionsFactory {
createMongooseOptions(): MongooseModuleOptions {
return {
uri: 'mongodb://localhost:27017/test',
useNewUrlParser: true,
};
}
}

View File

@@ -6,6 +6,7 @@ import { CatsModule } from './cats/cats.module';
imports: [
MongooseModule.forRootAsync({
useFactory: () => ({
useNewUrlParser: true,
uri: 'mongodb://localhost:27017/test',
}),
}),

View File

@@ -77,7 +77,7 @@ describe('Fastify FileSend', () => {
expect(headers['content-disposition']).to.equal(
'attachment; filename="Readme.md"',
);
expect(headers['content-length']).to.equal(`${readme.byteLength}`);
expect(headers['content-length']).to.equal(readme.byteLength);
expect(payload).to.equal(readmeString);
});
});

View File

@@ -1,6 +1,6 @@
{
"type": "mysql",
"host": "127.0.0.1",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",

View File

@@ -7,7 +7,7 @@ import { PhotoModule } from './photo/photo.module';
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: '127.0.0.1',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',

View File

@@ -11,7 +11,7 @@ class ConfigService implements TypeOrmOptionsFactory {
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: '127.0.0.1',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',

View File

@@ -11,7 +11,7 @@ class ConfigService implements TypeOrmOptionsFactory {
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: '127.0.0.1',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',

View File

@@ -8,7 +8,7 @@ import { PhotoModule } from './photo/photo.module';
TypeOrmModule.forRootAsync({
useFactory: () => ({
type: 'mysql',
host: '127.0.0.1',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',

View File

@@ -11,7 +11,7 @@ export class DatabaseModule {
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: '127.0.0.1',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',

View File

@@ -418,67 +418,4 @@ describe('URI Versioning', () => {
await app.close();
});
});
// ======================================================================== //
describe('with middleware applied', () => {
before(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: '1',
});
await app.init();
});
describe('GET /middleware', () => {
it('should return "Hello from middleware function!"', () => {
return request(app.getHttpServer())
.get('/v1/middleware')
.expect(200)
.expect('Hello from middleware function!');
});
});
describe('GET /middleware/override', () => {
it('should return "Hello from middleware function!"', () => {
return request(app.getHttpServer())
.get('/v2/middleware/override')
.expect(200)
.expect('Hello from middleware function!');
});
});
describe('GET /middleware/multiple', () => {
it('should return "Hello from middleware function!" (v1)', () => {
return request(app.getHttpServer())
.get('/v1/middleware/multiple')
.expect(200)
.expect('Hello from middleware function!');
});
it('should return "Hello from middleware function!" (v2)', () => {
return request(app.getHttpServer())
.get('/v2/middleware/multiple')
.expect(200)
.expect('Hello from middleware function!');
});
});
describe('GET /middleware/neutral', () => {
it('should return "Hello from middleware function!"', () => {
return request(app.getHttpServer())
.get('/middleware/neutral')
.expect(200)
.expect('Hello from middleware function!');
});
});
after(async () => {
await app.close();
});
});
});

View File

@@ -1,14 +1,11 @@
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { AppV1Controller } from './app-v1.controller';
import { AppV2Controller } from './app-v2.controller';
import { MiddlewareController } from './middleware.controller';
import { MultipleMiddlewareVersionController } from './multiple-middleware.controller';
import { MultipleVersionController } from './multiple.controller';
import { VersionNeutralMiddlewareController } from './neutral-middleware.controller';
import { VersionNeutralController } from './neutral.controller';
import { NoVersioningController } from './no-versioning.controller';
import { OverridePartialController } from './override-partial.controller';
import { VersionNeutralController } from './neutral.controller';
import { OverrideController } from './override.controller';
import { OverridePartialController } from './override-partial.controller';
@Module({
imports: [],
@@ -20,19 +17,6 @@ import { OverrideController } from './override.controller';
VersionNeutralController,
OverrideController,
OverridePartialController,
MiddlewareController,
MultipleMiddlewareVersionController,
VersionNeutralMiddlewareController,
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res) => res.end('Hello from middleware function!'))
.forRoutes(
MiddlewareController,
MultipleMiddlewareVersionController,
VersionNeutralMiddlewareController,
);
}
}
export class AppModule {}

View File

@@ -1,18 +0,0 @@
import { Controller, Get, Version } from '@nestjs/common';
@Controller({
path: 'middleware',
version: '1',
})
export class MiddlewareController {
@Get('/')
hello() {
return 'Hello from "MiddlewareController"!';
}
@Version('2')
@Get('/override')
hellov2() {
return 'Hello from "MiddlewareController"!';
}
}

View File

@@ -1,12 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller({
version: ['1', '2'],
path: 'middleware',
})
export class MultipleMiddlewareVersionController {
@Get('/multiple')
multiple() {
return 'Multiple Versions 1 or 2';
}
}

View File

@@ -1,12 +0,0 @@
import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common';
@Controller({
path: 'middleware',
version: VERSION_NEUTRAL,
})
export class VersionNeutralMiddlewareController {
@Get('/neutral')
neutral() {
return 'Neutral';
}
}

View File

@@ -66,21 +66,5 @@ describe('WebSocketGateway', () => {
);
});
it(`should be able to get the pattern in an interceptor`, async () => {
app = await createNestApp(ApplicationGateway);
await app.listen(3000);
ws = io('http://localhost:8080');
ws.emit('getClient', {
test: 'test',
});
await new Promise<void>(resolve =>
ws.on('popClient', data => {
expect(data.path).to.be.eql('getClient');
resolve();
}),
);
});
afterEach(() => app.close());
});

View File

@@ -194,30 +194,6 @@ describe('WebSocketGateway (WsAdapter)', () => {
});
});
it('should let the execution context have a getPattern() method on getClient()', async () => {
app = await createNestApp(ApplicationGateway);
await app.listen(3000);
ws = new WebSocket('ws://localhost:8080');
await new Promise(resolve => ws.on('open', resolve));
ws.send(
JSON.stringify({
event: 'getClient',
data: {
test: 'test',
},
}),
);
await new Promise<void>(resolve =>
ws.on('message', data => {
expect(JSON.parse(data).data.path).to.be.eql('getClient');
ws.close();
resolve();
}),
);
});
afterEach(async function () {
await app.close();
});

View File

@@ -1,10 +1,8 @@
import { UseInterceptors } from '@nestjs/common';
import {
MessageBody,
SubscribeMessage,
WebSocketGateway,
} from '@nestjs/websockets';
import { RequestInterceptor } from './request.interceptor';
@WebSocketGateway(8080)
export class ApplicationGateway {
@@ -15,13 +13,4 @@ export class ApplicationGateway {
data,
};
}
@UseInterceptors(RequestInterceptor)
@SubscribeMessage('getClient')
getPathCalled(client, data) {
return {
event: 'popClient',
data: { ...data, path: client.pattern },
};
}
}

View File

@@ -1,11 +0,0 @@
import { CallHandler, ExecutionContext, Injectable } from '@nestjs/common';
@Injectable()
export class RequestInterceptor {
intercept(context: ExecutionContext, next: CallHandler) {
const client = context.switchToWs().getClient();
const pattern = context.switchToWs().getPattern();
client.pattern = pattern;
return next.handle();
}
}

View File

@@ -1,6 +1,4 @@
import { UseInterceptors } from '@nestjs/common';
import { SubscribeMessage, WebSocketGateway } from '@nestjs/websockets';
import { RequestInterceptor } from './request.interceptor';
@WebSocketGateway()
export class ServerGateway {
@@ -11,13 +9,4 @@ export class ServerGateway {
data,
};
}
@UseInterceptors(RequestInterceptor)
@SubscribeMessage('getClient')
getPathCalled(client, data) {
return {
event: 'popClient',
data: { ...data, path: client.pattern },
};
}
}

View File

@@ -3,5 +3,5 @@
"packages": [
"packages/*"
],
"version": "10.0.4"
"version": "9.4.2"
}

37383
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/core",
"version": "10.0.0",
"version": "9.4.0",
"description": "Modern, fast, powerful node.js web framework",
"homepage": "https://nestjs.com",
"repository": {
@@ -25,7 +25,7 @@
"move:node_modules": "gulp move:node_modules",
"build:samples": "gulp install:samples && npm run build && npm run move:samples && gulp build:samples && gulp test:samples && gulp test:e2e:samples",
"codechecks:benchmarks": "codechecks ./tools/benchmarks/check-benchmarks.ts",
"coverage": "nyc report --reporter=text-lcov | coveralls -v",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"format": "prettier \"**/*.ts\" \"packages/**/*.json\" --ignore-path ./.prettierignore --write && git status",
"postinstall": "opencollective",
"test": "mocha packages/**/*.spec.ts",
@@ -72,67 +72,67 @@
"path-to-regexp": "3.2.0",
"reflect-metadata": "0.1.13",
"rxjs": "7.8.1",
"socket.io": "4.7.0",
"tslib": "2.6.0",
"socket.io": "4.6.1",
"tslib": "2.5.2",
"uid": "2.0.2",
"uuid": "9.0.0"
},
"devDependencies": {
"@apollo/server": "4.7.5",
"@apollo/server": "4.7.1",
"@codechecks/client": "0.1.12",
"@commitlint/cli": "17.6.6",
"@commitlint/config-angular": "17.6.6",
"@fastify/cors": "8.3.0",
"@commitlint/cli": "17.6.3",
"@commitlint/config-angular": "17.6.3",
"@fastify/cors": "8.2.1",
"@fastify/formbody": "7.4.0",
"@fastify/middie": "8.3.0",
"@fastify/multipart": "7.7.0",
"@fastify/static": "6.10.2",
"@fastify/multipart": "7.6.0",
"@fastify/static": "6.10.1",
"@fastify/view": "7.4.1",
"@grpc/grpc-js": "1.8.17",
"@grpc/grpc-js": "1.8.14",
"@grpc/proto-loader": "0.7.7",
"@nestjs/apollo": "12.0.4",
"@nestjs/graphql": "12.0.3",
"@nestjs/mongoose": "10.0.0",
"@nestjs/typeorm": "10.0.0",
"@nestjs/apollo": "11.0.5",
"@nestjs/graphql": "11.0.5",
"@nestjs/mongoose": "9.2.2",
"@nestjs/typeorm": "9.0.1",
"@types/amqplib": "0.10.1",
"@types/bytes": "3.1.1",
"@types/chai": "4.3.5",
"@types/chai-as-promised": "7.1.5",
"@types/cors": "2.8.13",
"@types/express": "4.17.17",
"@types/gulp": "4.0.12",
"@types/gulp": "4.0.10",
"@types/http-errors": "2.0.1",
"@types/mocha": "10.0.1",
"@types/node": "20.3.2",
"@types/node": "20.2.3",
"@types/sinon": "10.0.15",
"@types/supertest": "2.0.12",
"@types/ws": "8.5.5",
"@typescript-eslint/eslint-plugin": "5.60.0",
"@typescript-eslint/parser": "5.60.1",
"@types/ws": "8.5.4",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"amqp-connection-manager": "4.1.13",
"amqplib": "0.10.3",
"artillery": "1.7.9",
"body-parser": "1.20.2",
"bytes": "3.1.2",
"cache-manager": "5.2.3",
"cache-manager": "5.2.1",
"cache-manager-redis-store": "3.0.1",
"chai": "4.3.7",
"chai-as-promised": "7.1.1",
"clang-format": "1.8.0",
"commitlint-circle": "1.0.0",
"concurrently": "8.2.0",
"conventional-changelog": "4.0.0",
"core-js": "3.31.0",
"concurrently": "8.0.1",
"conventional-changelog": "3.1.25",
"core-js": "3.30.2",
"coveralls": "3.1.1",
"delete-empty": "3.0.0",
"engine.io-client": "6.5.0",
"eslint": "8.43.0",
"engine.io-client": "6.4.0",
"eslint": "7.32.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.27.5",
"eventsource": "2.0.2",
"fancy-log": "2.0.0",
"fastify": "4.18.0",
"graphql": "16.7.1",
"fastify": "4.17.0",
"graphql": "16.6.0",
"graphql-tools": "9.0.0",
"gulp": "4.0.2",
"gulp-clang-format": "1.0.27",
@@ -148,35 +148,35 @@
"kafkajs": "2.2.4",
"lerna": "2.11.0",
"lerna-changelog": "2.2.0",
"light-my-request": "5.10.0",
"light-my-request": "5.9.1",
"lint-staged": "13.2.2",
"markdown-table": "2.0.0",
"merge-graphql-schemas": "1.7.8",
"mocha": "10.2.0",
"mongoose": "7.3.1",
"mongoose": "7.2.0",
"mqtt": "4.3.7",
"multer": "1.4.4",
"mysql2": "3.4.1",
"nats": "2.15.1",
"mysql2": "3.3.1",
"nats": "2.13.1",
"nodemon": "2.0.22",
"nyc": "15.1.0",
"prettier": "2.8.8",
"redis": "4.6.7",
"redis": "4.6.6",
"rxjs-compat": "6.6.7",
"sinon": "15.2.0",
"sinon": "15.1.0",
"sinon-chai": "3.7.0",
"socket.io-client": "4.7.0",
"socket.io-client": "4.6.1",
"subscriptions-transport-ws": "0.11.0",
"supertest": "6.3.3",
"ts-morph": "19.0.0",
"ts-morph": "18.0.0",
"ts-node": "10.9.1",
"typeorm": "0.3.17",
"typescript": "5.1.5",
"typeorm": "0.3.16",
"typescript": "5.0.4",
"wrk": "1.2.1",
"ws": "8.13.0"
},
"engines": {
"node": ">= 16"
"node": ">= 12.9.0"
},
"collective": {
"type": "opencollective",

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -5,8 +5,7 @@ import {
import { isObject, isString } from '../utils/shared.utils';
export interface HttpExceptionOptions {
/** original cause of the error */
cause?: unknown;
cause?: Error;
description?: string;
}
@@ -69,13 +68,14 @@ export class HttpException extends Error {
this.initCause();
}
public cause: unknown;
public cause: Error | undefined;
/**
* Configures error chaining support
*
* @see https://nodejs.org/en/blog/release/v16.9.0/#error-cause
* @see https://github.com/microsoft/TypeScript/issues/45167
* See:
* - https://nodejs.org/en/blog/release/v16.9.0/#error-cause
* - https://github.com/microsoft/TypeScript/issues/45167
*/
public initCause(): void {
if (this.options?.cause) {

View File

@@ -31,10 +31,6 @@ export interface WsArgumentsHost {
* Returns the client object.
*/
getClient<T = any>(): T;
/**
* Returns the pattern for the event
*/
getPattern(): string;
}
/**

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/common",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
"author": "Kamil Mysliwiec",
"homepage": "https://nestjs.com",
@@ -19,7 +19,7 @@
"license": "MIT",
"dependencies": {
"iterare": "1.2.1",
"tslib": "2.6.0",
"tslib": "2.5.2",
"uid": "2.0.2"
},
"peerDependencies": {

View File

@@ -21,13 +21,12 @@ export class FileTypeValidator extends FileValidator<FileTypeValidatorOptions> {
return `Validation failed (expected type is ${this.validationOptions.fileType})`;
}
isValid<TFile extends IFile = any>(file?: TFile): boolean {
isValid<TFile extends IFile = any>(file: TFile): boolean {
if (!this.validationOptions) {
return true;
}
return (
!!file &&
'mimetype' in file &&
!!file.mimetype.match(this.validationOptions.fileType)
);

View File

@@ -26,8 +26,8 @@ export class MaxFileSizeValidator extends FileValidator<MaxFileSizeValidatorOpti
return `Validation failed (expected size is less than ${this.validationOptions.maxSize})`;
}
public isValid<TFile extends IFile = any>(file?: TFile): boolean {
if (!this.validationOptions || !file) {
public isValid<TFile extends IFile = any>(file: TFile): boolean {
if (!this.validationOptions) {
return true;
}

View File

@@ -9,7 +9,6 @@ import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isNil } from '../utils/shared.utils';
/**
* @publicApi
@@ -17,7 +16,6 @@ import { isNil } from '../utils/shared.utils';
export interface ParseBoolPipeOptions {
errorHttpStatusCode?: ErrorHttpStatusCode;
exceptionFactory?: (error: string) => any;
optional?: boolean;
}
/**
@@ -33,7 +31,7 @@ export class ParseBoolPipe
{
protected exceptionFactory: (error: string) => any;
constructor(@Optional() protected readonly options?: ParseBoolPipeOptions) {
constructor(@Optional() options?: ParseBoolPipeOptions) {
options = options || {};
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
options;
@@ -53,9 +51,6 @@ export class ParseBoolPipe
value: string | boolean,
metadata: ArgumentMetadata,
): Promise<boolean> {
if (isNil(value) && this.options.optional) {
return value;
}
if (this.isTrue(value)) {
return true;
}

View File

@@ -1,17 +1,14 @@
import { ArgumentMetadata, HttpStatus } from '../index';
import { Injectable, Optional } from '../decorators/core';
import { ArgumentMetadata, HttpStatus, Injectable, Optional } from '../index';
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isNil } from '../utils/shared.utils';
/**
* @publicApi
*/
export interface ParseEnumPipeOptions {
optional?: boolean;
errorHttpStatusCode?: ErrorHttpStatusCode;
exceptionFactory?: (error: string) => any;
}
@@ -26,9 +23,10 @@ export interface ParseEnumPipeOptions {
@Injectable()
export class ParseEnumPipe<T = any> implements PipeTransform<T> {
protected exceptionFactory: (error: string) => any;
constructor(
protected readonly enumType: T,
@Optional() protected readonly options?: ParseEnumPipeOptions,
@Optional() options?: ParseEnumPipeOptions,
) {
if (!enumType) {
throw new Error(
@@ -52,9 +50,6 @@ export class ParseEnumPipe<T = any> implements PipeTransform<T> {
* @param metadata contains metadata about the currently processed route argument
*/
async transform(value: T, metadata: ArgumentMetadata): Promise<T> {
if (isNil(value) && this.options.optional) {
return value;
}
if (!this.isEnum(value)) {
throw this.exceptionFactory(
'Validation failed (enum string is expected)',

View File

@@ -5,7 +5,6 @@ import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isNil } from '../utils/shared.utils';
/**
* @publicApi
@@ -13,7 +12,6 @@ import { isNil } from '../utils/shared.utils';
export interface ParseFloatPipeOptions {
errorHttpStatusCode?: ErrorHttpStatusCode;
exceptionFactory?: (error: string) => any;
optional?: boolean;
}
/**
@@ -27,7 +25,7 @@ export interface ParseFloatPipeOptions {
export class ParseFloatPipe implements PipeTransform<string> {
protected exceptionFactory: (error: string) => any;
constructor(@Optional() protected readonly options?: ParseFloatPipeOptions) {
constructor(@Optional() options?: ParseFloatPipeOptions) {
options = options || {};
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
options;
@@ -45,9 +43,6 @@ export class ParseFloatPipe implements PipeTransform<string> {
* @param metadata contains metadata about the currently processed route argument
*/
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
if (isNil(value) && this.options.optional) {
return value;
}
if (!this.isNumeric(value)) {
throw this.exceptionFactory(
'Validation failed (numeric string is expected)',

View File

@@ -9,7 +9,6 @@ import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isNil } from '../utils/shared.utils';
/**
* @publicApi
@@ -17,7 +16,6 @@ import { isNil } from '../utils/shared.utils';
export interface ParseIntPipeOptions {
errorHttpStatusCode?: ErrorHttpStatusCode;
exceptionFactory?: (error: string) => any;
optional?: boolean;
}
/**
@@ -31,7 +29,7 @@ export interface ParseIntPipeOptions {
export class ParseIntPipe implements PipeTransform<string> {
protected exceptionFactory: (error: string) => any;
constructor(@Optional() protected readonly options?: ParseIntPipeOptions) {
constructor(@Optional() options?: ParseIntPipeOptions) {
options = options || {};
const { exceptionFactory, errorHttpStatusCode = HttpStatus.BAD_REQUEST } =
options;
@@ -49,9 +47,6 @@ export class ParseIntPipe implements PipeTransform<string> {
* @param metadata contains metadata about the currently processed route argument
*/
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
if (isNil(value) && this.options.optional) {
return value;
}
if (!this.isNumeric(value)) {
throw this.exceptionFactory(
'Validation failed (numeric string is expected)',

View File

@@ -9,7 +9,7 @@ import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isNil, isString } from '../utils/shared.utils';
import { isString } from '../utils/shared.utils';
/**
* @publicApi
@@ -18,7 +18,6 @@ export interface ParseUUIDPipeOptions {
version?: '3' | '4' | '5';
errorHttpStatusCode?: ErrorHttpStatusCode;
exceptionFactory?: (errors: string) => any;
optional?: boolean;
}
/**
@@ -39,7 +38,7 @@ export class ParseUUIDPipe implements PipeTransform<string> {
private readonly version: '3' | '4' | '5';
protected exceptionFactory: (errors: string) => any;
constructor(@Optional() protected readonly options?: ParseUUIDPipeOptions) {
constructor(@Optional() options?: ParseUUIDPipeOptions) {
options = options || {};
const {
exceptionFactory,
@@ -54,9 +53,6 @@ export class ParseUUIDPipe implements PipeTransform<string> {
}
async transform(value: string, metadata: ArgumentMetadata): Promise<string> {
if (isNil(value) && this.options.optional) {
return value;
}
if (!this.isUUID(value, this.version)) {
throw this.exceptionFactory(
`Validation failed (uuid${

View File

@@ -1,8 +1,8 @@
import { LogLevel } from '../logger.service';
const LOG_LEVEL_VALUES: Record<LogLevel, number> = {
verbose: 0,
debug: 1,
debug: 0,
verbose: 1,
log: 2,
warn: 3,
error: 4,

View File

@@ -72,14 +72,6 @@ describe('FileTypeValidator', () => {
expect(fileTypeValidator.isValid(requestFile)).to.equal(false);
});
it('should return false when no file provided', () => {
const fileTypeValidator = new FileTypeValidator({
fileType: 'image/jpeg',
});
expect(fileTypeValidator.isValid()).to.equal(false);
});
});
describe('buildErrorMessage', () => {

View File

@@ -40,14 +40,6 @@ describe('MaxFileSizeValidator', () => {
expect(maxFileSizeValidator.isValid(requestFile)).to.equal(false);
});
it('should return true when no file provided', () => {
const maxFileSizeValidator = new MaxFileSizeValidator({
maxSize: oneKb,
});
expect(maxFileSizeValidator.isValid()).to.equal(true);
});
});
describe('buildErrorMessage', () => {

View File

@@ -18,12 +18,6 @@ describe('ParseBoolPipe', () => {
expect(await target.transform(false, {} as ArgumentMetadata)).to.be
.false;
});
it('should not throw an error if the value is undefined/null and optional is true', async () => {
const target = new ParseBoolPipe({ optional: true });
const value = await target.transform(undefined, {} as ArgumentMetadata);
expect(value).to.equal(undefined);
});
});
describe('when validation fails', () => {
it('should throw an error', async () => {

View File

@@ -14,7 +14,6 @@ describe('ParseEnumPipe', () => {
Up = 'UP',
}
let target: ParseEnumPipe;
beforeEach(() => {
target = new ParseEnumPipe(Direction, {
exceptionFactory: (error: any) => new CustomTestError(),
@@ -27,12 +26,6 @@ describe('ParseEnumPipe', () => {
Direction.Up,
);
});
it('should not throw an error if enumType is undefined/null and optional is true', async () => {
const target = new ParseEnumPipe('DOWN', { optional: true });
const value = await target.transform(undefined, {} as ArgumentMetadata);
expect(value).to.equal(undefined);
});
});
describe('when validation fails', () => {
it('should throw an error', async () => {
@@ -40,16 +33,6 @@ describe('ParseEnumPipe', () => {
target.transform('DOWN', {} as ArgumentMetadata),
).to.be.rejectedWith(CustomTestError);
});
it('should throw an error if enumType is wrong and optional is true', async () => {
target = new ParseEnumPipe(Direction, {
exceptionFactory: (error: any) => new CustomTestError(),
optional: true,
});
return expect(
target.transform('DOWN', {} as ArgumentMetadata),
).to.be.rejectedWith(CustomTestError);
});
});
});
describe('constructor', () => {

View File

@@ -25,11 +25,6 @@ describe('ParseFloatPipe', () => {
parseFloat(num),
);
});
it('should not throw an error if the value is undefined/null and optional is true', async () => {
const target = new ParseFloatPipe({ optional: true });
const value = await target.transform(undefined, {} as ArgumentMetadata);
expect(value).to.equal(undefined);
});
});
describe('when validation fails', () => {
it('should throw an error', async () => {

View File

@@ -30,11 +30,6 @@ describe('ParseIntPipe', () => {
-3,
);
});
it('should not throw an error if the value is undefined/null and optional is true', async () => {
const target = new ParseIntPipe({ optional: true });
const value = await target.transform(undefined, {} as ArgumentMetadata);
expect(value).to.equal(undefined);
});
});
describe('when validation fails', () => {
it('should throw an error', async () => {

View File

@@ -41,11 +41,6 @@ describe('ParseUUIDPipe', () => {
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
});
it('should not throw an error if the value is undefined/null and optional is true', async () => {
const target = new ParseUUIDPipe({ optional: true });
const value = await target.transform(undefined, {} as ArgumentMetadata);
expect(value).to.equal(undefined);
});
});
describe('when validation fails', () => {

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -59,7 +59,6 @@ export class ExecutionContextHost implements ExecutionContext {
return Object.assign(this, {
getClient: () => this.getArgByIndex(0),
getData: () => this.getArgByIndex(1),
getPattern: () => this.getArgByIndex(this.getArgs().length - 1),
});
}
}

View File

@@ -5,8 +5,7 @@ import {
Controller,
} from '@nestjs/common/interfaces';
import { isEmpty } from '@nestjs/common/utils/shared.utils';
import { AsyncResource } from 'async_hooks';
import { Observable, defer, from as fromPromise } from 'rxjs';
import { defer, from as fromPromise, Observable } from 'rxjs';
import { mergeAll, switchMap } from 'rxjs/operators';
import { ExecutionContextHost } from '../helpers/execution-context-host';
@@ -27,7 +26,7 @@ export class InterceptorsConsumer {
const nextFn = async (i = 0) => {
if (i >= interceptors.length) {
return defer(AsyncResource.bind(() => this.transformDeferred(next)));
return this.transformDeferred(next);
}
const handler: CallHandler = {
handle: () => fromPromise(nextFn(i + 1)).pipe(mergeAll()),

View File

@@ -9,7 +9,7 @@ export class MetadataScanner {
private readonly cachedScannedPrototypes: Map<object, string[]> = new Map();
/**
* @deprecated
* @deprecated
* @see {@link getAllMethodNames}
* @see getAllMethodNames
*/
@@ -62,7 +62,7 @@ export class MetadataScanner {
}
/**
* @deprecated
* @deprecated
* @see {@link getAllMethodNames}
* @see getAllMethodNames
*/

View File

@@ -9,7 +9,6 @@ import {
MiddlewareConfiguration,
RouteInfo,
} from '@nestjs/common/interfaces/middleware';
import { stripEndSlash } from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import { RouteInfoPathExtractor } from './route-info-path-extractor';
import { RoutesMapper } from './routes-mapper';
@@ -101,27 +100,21 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
const routesWithRegex = routes
.filter(route => route.path.includes(':'))
.map(route => ({
method: route.method,
path: route.path,
regex: new RegExp(
'^(' + route.path.replace(regexMatchParams, wildcard) + ')$',
'g',
),
}));
return routes.filter(route => {
const isOverlapped = (item: { regex: RegExp } & RouteInfo): boolean => {
if (route.method !== item.method) {
return false;
}
const normalizedRoutePath = stripEndSlash(route.path);
return (
normalizedRoutePath !== item.path &&
item.regex.test(normalizedRoutePath)
);
const isOverlapped = (v: { path: string; regex: RegExp }) => {
return route.path !== v.path && route.path.match(v.regex);
};
const routeMatch = routesWithRegex.find(isOverlapped);
return routeMatch === undefined;
if (routeMatch === undefined) {
return route;
}
});
}
};

View File

@@ -66,7 +66,7 @@ export class MiddlewareModule<
config,
appRef,
);
this.routesMapper = new RoutesMapper(container, config);
this.routesMapper = new RoutesMapper(container);
this.resolver = new MiddlewareResolver(middlewareContainer, injector);
this.routeInfoPathExtractor = new RouteInfoPathExtractor(config);
this.injector = injector;

View File

@@ -1,48 +1,35 @@
import {
MODULE_PATH,
PATH_METADATA,
VERSION_METADATA,
} from '@nestjs/common/constants';
import {
RouteInfo,
Type,
VERSION_NEUTRAL,
VersionValue,
} from '@nestjs/common/interfaces';
import { MODULE_PATH, PATH_METADATA } from '@nestjs/common/constants';
import { RouteInfo, Type } from '@nestjs/common/interfaces';
import {
addLeadingSlash,
isString,
isUndefined,
} from '@nestjs/common/utils/shared.utils';
import { ApplicationConfig } from '../application-config';
import { NestContainer } from '../injector/container';
import { Module } from '../injector/module';
import { MetadataScanner } from '../metadata-scanner';
import { PathsExplorer, RouteDefinition } from '../router/paths-explorer';
import { PathsExplorer } from '../router/paths-explorer';
import { targetModulesByContainer } from '../router/router-module';
export class RoutesMapper {
private readonly pathsExplorer: PathsExplorer;
constructor(
private readonly container: NestContainer,
private readonly applicationConfig: ApplicationConfig,
) {
constructor(private readonly container: NestContainer) {
this.pathsExplorer = new PathsExplorer(new MetadataScanner());
}
public mapRouteToRouteInfo(
controllerOrRoute: Type<any> | RouteInfo | string,
route: Type<any> | RouteInfo | string,
): RouteInfo[] {
if (isString(controllerOrRoute)) {
return this.getRouteInfoFromPath(controllerOrRoute);
if (isString(route)) {
return this.getRouteInfoFromPath(route);
}
const routePathOrPaths = this.getRoutePath(controllerOrRoute);
if (this.isRouteInfo(routePathOrPaths, controllerOrRoute)) {
return this.getRouteInfoFromObject(controllerOrRoute);
const routePathOrPaths = this.getRoutePath(route);
if (this.isRouteInfo(routePathOrPaths, route)) {
return this.getRouteInfoFromObject(route);
}
return this.getRouteInfoFromController(controllerOrRoute, routePathOrPaths);
return this.getRouteInfoFromController(route, routePathOrPaths);
}
private getRouteInfoFromPath(routePath: string): RouteInfo[] {
@@ -75,47 +62,33 @@ export class RoutesMapper {
Object.create(controller),
controller.prototype,
);
const controllerVersion = this.getVersionMetadata(controller);
const versioningConfig = this.applicationConfig.getVersioning();
const moduleRef = this.getHostModuleOfController(controller);
const modulePath = this.getModulePath(moduleRef?.metatype);
const concatPaths = <T>(acc: T[], currentValue: T[]) =>
acc.concat(currentValue);
const toUndefinedIfNeural = (version: VersionValue) =>
version === VERSION_NEUTRAL ? undefined : version;
const toRouteInfo = (item: RouteDefinition, prefix: string) =>
item.path
?.map(p => {
let endpointPath = modulePath ?? '';
endpointPath += this.normalizeGlobalPath(prefix) + addLeadingSlash(p);
const routeInfo: RouteInfo = {
path: endpointPath,
method: item.requestMethod,
};
const version = item.version ?? controllerVersion;
if (version && versioningConfig) {
if (typeof version !== 'string' && Array.isArray(version)) {
return version.map(v => ({
...routeInfo,
version: toUndefinedIfNeural(v),
}));
}
routeInfo.version = toUndefinedIfNeural(version);
}
return routeInfo;
})
.flat() as RouteInfo[];
return []
.concat(routePath)
.map(routePath =>
controllerPaths
.map(item => toRouteInfo(item, routePath))
.map(item =>
item.path?.map(p => {
let path = modulePath ?? '';
path += this.normalizeGlobalPath(routePath) + addLeadingSlash(p);
const routeInfo: RouteInfo = {
path,
method: item.requestMethod,
};
if (item.version) {
routeInfo.version = item.version;
}
return routeInfo;
}),
)
.reduce(concatPaths, []),
)
.reduce(concatPaths, []);
@@ -168,16 +141,4 @@ export class RoutesMapper {
);
return modulePath ?? Reflect.getMetadata(MODULE_PATH, metatype);
}
private getVersionMetadata(
metatype: Type<unknown> | Function,
): VersionValue | undefined {
const versioningConfig = this.applicationConfig.getVersioning();
if (versioningConfig) {
return (
Reflect.getMetadata(VERSION_METADATA, metatype) ??
versioningConfig.defaultVersion
);
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/core",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -32,17 +32,17 @@
"fast-safe-stringify": "2.1.1",
"iterare": "1.2.1",
"path-to-regexp": "3.2.0",
"tslib": "2.6.0",
"tslib": "2.5.2",
"uid": "2.0.2"
},
"devDependencies": {
"@nestjs/common": "10.0.4"
"@nestjs/common": "9.4.2"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/microservices": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/microservices": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/websockets": "^9.0.0",
"reflect-metadata": "^0.1.12",
"rxjs": "^7.1.0"
},

View File

@@ -16,7 +16,7 @@ describe('Error Messages', () => {
it('should display class', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, CatService). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -39,7 +39,7 @@ describe('Error Messages', () => {
it('should display the provide token', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, MY_TOKEN). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -60,7 +60,7 @@ describe('Error Messages', () => {
it('should display the function name', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, CatFunction). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -81,7 +81,7 @@ describe('Error Messages', () => {
it('should use "+" if unknown dependency name', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, +). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -102,7 +102,7 @@ describe('Error Messages', () => {
it('should display the module name', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatService (?, MY_TOKEN). Please make sure that the argument dependency at index [0] is available in the TestModule context.
Potential solutions:
- Is TestModule a valid NestJS module?
- If dependency is a provider, is it part of the current TestModule?
@@ -136,7 +136,7 @@ describe('Error Messages', () => {
it('should display the symbol name of the provider', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the Symbol(CatProvider) (?). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -157,7 +157,7 @@ describe('Error Messages', () => {
it('should display the symbol dependency of the provider', () => {
const expectedResult =
stringCleaner(`Nest can't resolve dependencies of the CatProvider (?, Symbol(DogProvider)). Please make sure that the argument dependency at index [0] is available in the current context.
Potential solutions:
- If dependency is a provider, is it part of the current Module?
- If dependency is exported from a separate @Module, is that module imported within Module?
@@ -201,7 +201,7 @@ Scope [AppModule -> CatsModule]`);
it('should display the module name with the invalid index and scope', () => {
const expectedMessage =
stringCleaner(`Nest cannot create the CatsModule instance.
Received an unexpected value at index [0] of the CatsModule "imports" array.
Received an unexpected value at index [0] of the CatsModule "imports" array.
Scope [AppModule -> CatsModule]`);

View File

@@ -1,7 +1,7 @@
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
import { AsyncLocalStorage } from 'async_hooks';
import { expect } from 'chai';
import { Observable, lastValueFrom, of, retry } from 'rxjs';
import { Observable, lastValueFrom, of } from 'rxjs';
import * as sinon from 'sinon';
import { InterceptorsConsumer } from '../../interceptors/interceptors-consumer';
@@ -85,8 +85,8 @@ describe('InterceptorsConsumer', () => {
});
});
describe('when AsyncLocalStorage is used', () => {
it('should allow an interceptor to set values in AsyncLocalStorage that are accesible from the controller', async () => {
describe('AsyncLocalStorage', () => {
it('Allows an interceptor to set values in AsyncLocalStorage that are accesible from the controller', async () => {
const storage = new AsyncLocalStorage<Record<string, any>>();
class StorageInterceptor implements NestInterceptor {
intercept(
@@ -96,9 +96,7 @@ describe('InterceptorsConsumer', () => {
return storage.run({ value: 'hello' }, () => next.handle());
}
}
const next = () => {
return Promise.resolve(storage.getStore().value);
};
const next = () => Promise.resolve(storage.getStore().value);
const intercepted = await consumer.intercept(
[new StorageInterceptor()],
null,
@@ -110,35 +108,6 @@ describe('InterceptorsConsumer', () => {
expect(result).to.equal('hello');
});
});
describe('when retrying is enabled', () => {
it('should retry a specified amount of times', async () => {
let count = 0;
const next = () => {
count++;
if (count < 3) {
return Promise.reject(new Error('count not reached'));
}
return Promise.resolve(count);
};
class RetryInterceptor implements NestInterceptor {
intercept(
_context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
return next.handle().pipe(retry(4));
}
}
const intercepted = await consumer.intercept(
[new RetryInterceptor()],
null,
{ constructor: null },
null,
next,
);
expect(await transformToResult(intercepted)).to.equal(3);
});
});
});
describe('createContext', () => {
it('should return execution context object', () => {

View File

@@ -1,18 +1,5 @@
import { expect } from 'chai';
import {
Controller,
Delete,
Get,
Head,
Options,
Patch,
Post,
Put,
RequestMethod,
Version,
VersioningType,
} from '../../../common';
import { MiddlewareConfigProxy } from '../../../common/interfaces';
import { Controller, Get, RequestMethod, Version } from '../../../common';
import { ApplicationConfig } from '../../application-config';
import { NestContainer } from '../../injector/container';
import { MiddlewareBuilder } from '../../middleware/builder';
@@ -26,26 +13,23 @@ describe('MiddlewareBuilder', () => {
beforeEach(() => {
const container = new NestContainer();
const appConfig = new ApplicationConfig();
appConfig.enableVersioning({ type: VersioningType.URI });
builder = new MiddlewareBuilder(
new RoutesMapper(container, appConfig),
new RoutesMapper(container),
new NoopHttpAdapter({}),
new RouteInfoPathExtractor(appConfig),
);
});
describe('apply', () => {
let configProxy;
beforeEach(() => {
configProxy = builder.apply([]);
});
it('should return configuration proxy', () => {
const configProxy = builder.apply([]);
const metatype = (MiddlewareBuilder as any).ConfigProxy;
expect(configProxy instanceof metatype).to.be.true;
});
describe('configuration proxy', () => {
describe('when "forRoutes()" called', () => {
let configProxy: MiddlewareConfigProxy;
beforeEach(() => {
configProxy = builder.apply([]);
});
@Controller('path')
class Test {
@Get('route')
@@ -56,7 +40,6 @@ describe('MiddlewareBuilder', () => {
public getAllVersioned() {}
}
const route = { path: '/test', method: RequestMethod.GET };
it('should store configuration passed as argument', () => {
configProxy.forRoutes(route, Test);
@@ -81,106 +64,6 @@ describe('MiddlewareBuilder', () => {
},
]);
});
@Controller('users')
class UsersController {
@Head('rsvp')
hRsvp() {}
@Options('rsvp')
oRsvp() {}
@Get('rsvp')
gRsvp() {}
@Post('rsvp')
pRsvp() {}
@Put('rsvp')
puRsvp() {}
@Patch('rsvp')
ptRsvp() {}
@Delete('rsvp')
dRsvp() {}
@Post()
create() {}
@Get()
findAll() {}
@Get(':id')
findOne() {}
@Patch(':id')
update() {}
@Delete(':id')
remove() {}
}
it('should remove overlapping routes', () => {
configProxy.forRoutes(UsersController);
expect(builder.build()).to.deep.equal([
{
middleware: [],
forRoutes: [
{
method: RequestMethod.HEAD,
path: '/users/rsvp',
},
{
method: RequestMethod.OPTIONS,
path: '/users/rsvp',
},
{
method: RequestMethod.POST,
path: '/users/rsvp',
},
{
method: RequestMethod.PUT,
path: '/users/rsvp',
},
{
method: RequestMethod.POST,
path: '/users/',
},
{
method: RequestMethod.GET,
path: '/users/',
},
{
method: RequestMethod.GET,
path: '/users/:id',
},
{
method: RequestMethod.PATCH,
path: '/users/:id',
},
{
method: RequestMethod.DELETE,
path: '/users/:id',
},
// Overlapping:
// {
// method: RequestMethod.GET,
// path: '/users/rsvp',
// },
// {
// method: RequestMethod.PATCH,
// path: '/users/rsvp',
// },
// {
// method: RequestMethod.DELETE,
// path: '/users/rsvp',
// },
],
},
]);
});
});
});
});

View File

@@ -1,13 +1,12 @@
import { Version } from '../../../common';
import { MiddlewareConfiguration } from '../../../common/interfaces';
import { expect } from 'chai';
import { Version, VersioningType } from '../../../common';
import { Controller } from '../../../common/decorators/core/controller.decorator';
import {
Get,
RequestMapping,
} from '../../../common/decorators/http/request-mapping.decorator';
import { RequestMethod } from '../../../common/enums/request-method.enum';
import { MiddlewareConfiguration } from '../../../common/interfaces';
import { ApplicationConfig } from '../../application-config';
import { NestContainer } from '../../injector/container';
import { RoutesMapper } from '../../middleware/routes-mapper';
@@ -27,9 +26,7 @@ describe('RoutesMapper', () => {
let mapper: RoutesMapper;
beforeEach(() => {
const appConfig = new ApplicationConfig();
appConfig.enableVersioning({ type: VersioningType.URI });
mapper = new RoutesMapper(new NestContainer(), appConfig);
mapper = new RoutesMapper(new NestContainer());
});
it('should map @Controller() to "ControllerMetadata" in forRoutes', () => {
@@ -84,47 +81,4 @@ describe('RoutesMapper', () => {
{ path: '/test2/another', method: RequestMethod.DELETE },
]);
});
@Controller({
version: '1',
path: 'versioned',
})
class VersionedController {
@Get()
hello() {
return 'Hello from "VersionedController"!';
}
@Version('2')
@Get('/override')
override() {
return 'Hello from "VersionedController"!';
}
}
@Controller({
version: ['1', '2'],
})
class MultipleVersionController {
@Get('multiple')
multiple() {
return 'Multiple Versions 1 or 2';
}
}
it('should map a versioned controller to the corresponding route info objects (single version)', () => {
expect(mapper.mapRouteToRouteInfo(VersionedController)).to.deep.equal([
{ path: '/versioned/', version: '1', method: RequestMethod.GET },
{ path: '/versioned/override', version: '2', method: RequestMethod.GET },
]);
});
it('should map a versioned controller to the corresponding route info objects (multiple versions)', () => {
expect(mapper.mapRouteToRouteInfo(MultipleVersionController)).to.deep.equal(
[
{ path: '/multiple', version: '1', method: RequestMethod.GET },
{ path: '/multiple', version: '2', method: RequestMethod.GET },
],
);
});
});

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -163,13 +163,12 @@ export class ClientKafka extends ClientProxy {
}
const consumerSubscribeOptions = this.options.subscribe || {};
if (this.responsePatterns.length > 0) {
await this.consumer.subscribe({
const subscribeTo = async (responsePattern: string) =>
this.consumer.subscribe({
topic: responsePattern,
...consumerSubscribeOptions,
topics: this.responsePatterns,
});
}
await Promise.all(this.responsePatterns.map(subscribeTo));
await this.consumer.run(
Object.assign(this.options.run || {}, {
@@ -300,12 +299,12 @@ export class ClientKafka extends ClientProxy {
const consumerAssignments: { [key: string]: number } = {};
// only need to set the minimum
Object.keys(data.payload.memberAssignment).forEach(topic => {
const memberPartitions = data.payload.memberAssignment[topic];
Object.keys(data.payload.memberAssignment).forEach(memberId => {
const minimumPartition = Math.min(
...data.payload.memberAssignment[memberId],
);
if (memberPartitions.length) {
consumerAssignments[topic] = Math.min(...memberPartitions);
}
consumerAssignments[memberId] = minimumPartition;
});
this.consumerAssignments = consumerAssignments;

View File

@@ -240,7 +240,7 @@ export interface KafkaOptions {
client?: KafkaConfig;
consumer?: ConsumerConfig;
run?: Omit<ConsumerRunConfig, 'eachBatch' | 'eachMessage'>;
subscribe?: Omit<ConsumerSubscribeTopics, 'topics'>;
subscribe?: Omit<ConsumerSubscribeTopics, 'topic'>;
producer?: ProducerConfig;
send?: Omit<ProducerRecord, 'topic' | 'messages'>;
serializer?: Serializer;

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/microservices",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@microservices)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -19,17 +19,17 @@
},
"dependencies": {
"iterare": "1.2.1",
"tslib": "2.6.0"
"tslib": "2.5.2"
},
"devDependencies": {
"@nestjs/common": "10.0.4",
"@nestjs/core": "10.0.4"
"@nestjs/common": "9.4.2",
"@nestjs/core": "9.4.2"
},
"peerDependencies": {
"@grpc/grpc-js": "*",
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/websockets": "^9.0.0",
"amqp-connection-manager": "*",
"amqplib": "*",
"cache-manager": "*",

View File

@@ -122,13 +122,12 @@ export class ServerKafka extends Server implements CustomTransportStrategy {
public async bindEvents(consumer: Consumer) {
const registeredPatterns = [...this.messageHandlers.keys()];
const consumerSubscribeOptions = this.options.subscribe || {};
if (registeredPatterns.length > 0) {
await this.consumer.subscribe({
const subscribeToPattern = async (pattern: string) =>
consumer.subscribe({
topic: pattern,
...consumerSubscribeOptions,
topics: registeredPatterns,
});
}
await Promise.all(registeredPatterns.map(subscribeToPattern));
const consumerRunOptions = Object.assign(this.options.run || {}, {
eachMessage: this.getMessageHandler(),

View File

@@ -67,7 +67,6 @@ export class ServerMqtt extends Server implements CustomTransportStrategy {
public bindEvents(mqttClient: MqttClient) {
mqttClient.on(MESSAGE_EVENT, this.getMessageHandler(mqttClient).bind(this));
const registeredPatterns = [...this.messageHandlers.keys()];
registeredPatterns.forEach(pattern => {
const { isEventHandler } = this.messageHandlers.get(pattern);

View File

@@ -395,34 +395,6 @@ describe('ClientKafka', () => {
},
);
});
it('should not update consumer assignments if there are no partitions assigned to consumer', async () => {
await client.connect();
const consumerAssignments: ConsumerGroupJoinEvent = {
id: 'id',
type: 'type',
timestamp: 1234567890,
payload: {
duration: 20,
groupId: 'group-id',
isLeader: true,
leaderId: 'member-1',
groupProtocol: 'RoundRobin',
memberId: 'member-1',
memberAssignment: {
'topic-a': [],
'topic-b': [3, 4, 5],
},
},
};
client['setConsumerAssignments'](consumerAssignments);
expect(client['consumerAssignments']).to.deep.eq({
'topic-b': 3,
});
});
});
describe('bindTopics', () => {
@@ -435,7 +407,7 @@ describe('ClientKafka', () => {
expect(subscribe.calledOnce).to.be.true;
expect(
subscribe.calledWith({
topics: [replyTopic],
topic: replyTopic,
}),
).to.be.true;
expect(run.calledOnce).to.be.true;
@@ -452,7 +424,7 @@ describe('ClientKafka', () => {
expect(subscribe.calledOnce).to.be.true;
expect(
subscribe.calledWith({
topics: [replyTopic],
topic: replyTopic,
fromBeginning: true,
}),
).to.be.true;

View File

@@ -190,7 +190,7 @@ describe('ServerKafka', () => {
expect(subscribe.called).to.be.true;
expect(
subscribe.calledWith({
topics: [pattern],
topic: pattern,
}),
).to.be.true;
@@ -214,7 +214,7 @@ describe('ServerKafka', () => {
expect(subscribe.called).to.be.true;
expect(
subscribe.calledWith({
topics: [pattern],
topic: pattern,
fromBeginning: true,
}),
).to.be.true;

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -16,11 +16,6 @@ import { transformException } from '../multer/multer.utils';
type MulterInstance = any;
/**
* @param localOptions
*
* @publicApi
*/
export function AnyFilesInterceptor(
localOptions?: MulterOptions,
): Type<NestInterceptor> {

View File

@@ -19,11 +19,6 @@ import { transformException } from '../multer/multer.utils';
type MulterInstance = any;
/**
* @param uploadFields
* @param localOptions
* @publicApi
*/
export function FileFieldsInterceptor(
uploadFields: MulterField[],
localOptions?: MulterOptions,

View File

@@ -16,12 +16,6 @@ import { transformException } from '../multer/multer.utils';
type MulterInstance = any;
/**
* @param fieldName
* @param localOptions
*
* @publicApi
*/
export function FileInterceptor(
fieldName: string,
localOptions?: MulterOptions,

View File

@@ -16,14 +16,6 @@ import { transformException } from '../multer/multer.utils';
type MulterInstance = any;
/**
*
* @param fieldName
* @param maxCount
* @param localOptions
*
* @publicApi
*/
export function FilesInterceptor(
fieldName: string,
maxCount?: number,

View File

@@ -2,4 +2,3 @@ export * from './any-files.interceptor';
export * from './file-fields.interceptor';
export * from './file.interceptor';
export * from './files.interceptor';
export * from './no-files.interceptor';

View File

@@ -1,61 +0,0 @@
import {
CallHandler,
ExecutionContext,
Inject,
mixin,
NestInterceptor,
Optional,
Type,
} from '@nestjs/common';
import * as multer from 'multer';
import { Observable } from 'rxjs';
import { MULTER_MODULE_OPTIONS } from '../files.constants';
import { MulterModuleOptions } from '../interfaces';
import { MulterOptions } from '../interfaces/multer-options.interface';
import { transformException } from '../multer/multer.utils';
type MulterInstance = any;
/**
*
* @param localOptions
* @publicApi
*/
export function NoFilesInterceptor(
localOptions?: MulterOptions,
): Type<NestInterceptor> {
class MixinInterceptor implements NestInterceptor {
protected multer: MulterInstance;
constructor(
@Optional()
@Inject(MULTER_MODULE_OPTIONS)
options: MulterModuleOptions = {},
) {
this.multer = (multer as any)({
...options,
...localOptions,
});
}
async intercept(
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const ctx = context.switchToHttp();
await new Promise<void>((resolve, reject) =>
this.multer.none()(ctx.getRequest(), ctx.getResponse(), (err: any) => {
if (err) {
const error = transformException(err);
return reject(error);
}
resolve();
}),
);
return next.handle();
}
}
const Interceptor = mixin(MixinInterceptor);
return Interceptor;
}

View File

@@ -3,17 +3,10 @@ import { MulterOptions } from './multer-options.interface';
export type MulterModuleOptions = MulterOptions;
/**
* @publicApi
*/
export interface MulterOptionsFactory {
createMulterOptions(): Promise<MulterModuleOptions> | MulterModuleOptions;
}
/**
* @publicApi
*/
export interface MulterModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<MulterOptionsFactory>;

View File

@@ -1,7 +1,5 @@
/**
* @see https://github.com/expressjs/multer
*
* @publicApi
*/
export interface MulterOptions {
dest?: string;
@@ -57,9 +55,6 @@ export interface MulterOptions {
): void;
}
/**
* @publicApi
*/
export interface MulterField {
/** The field name. */
name: string;

View File

@@ -8,9 +8,6 @@ import {
} from './interfaces/files-upload-module.interface';
import { MULTER_MODULE_ID } from './multer.constants';
/**
* @publicApi
*/
@Module({})
export class MulterModule {
static register(options: MulterModuleOptions = {}): DynamicModule {

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-express",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-express)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -22,14 +22,14 @@
"cors": "2.8.5",
"express": "4.18.2",
"multer": "1.4.4-lts.1",
"tslib": "2.6.0"
"tslib": "2.5.2"
},
"devDependencies": {
"@nestjs/common": "10.0.4",
"@nestjs/core": "10.0.4"
"@nestjs/common": "9.4.2",
"@nestjs/core": "9.4.2"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0"
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0"
}
}

View File

@@ -1,44 +0,0 @@
import { CallHandler } from '@nestjs/common';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { expect } from 'chai';
import { of } from 'rxjs';
import * as sinon from 'sinon';
import { NoFilesInterceptor } from '../../../multer/interceptors/no-files.interceptor';
describe('NoFilesInterceptor', () => {
it('should return metatype with expected structure', async () => {
const targetClass = NoFilesInterceptor();
expect(targetClass.prototype.intercept).to.not.be.undefined;
});
describe('intercept', () => {
let handler: CallHandler;
beforeEach(() => {
handler = {
handle: () => of('test'),
};
});
it('should call none() with expected params', async () => {
const target = new (NoFilesInterceptor())();
const callback = (req, res, next) => next();
const noneSpy = sinon
.stub((target as any).multer, 'none')
.returns(callback);
await target.intercept(new ExecutionContextHost([]), handler);
expect(noneSpy.called).to.be.true;
});
it('should transform exception', async () => {
const target = new (NoFilesInterceptor())();
const err = {};
const callback = (req, res, next) => next(err);
(target as any).multer = {
none: () => callback,
};
(target.intercept(new ExecutionContextHost([]), handler) as any).catch(
error => expect(error).to.not.be.undefined,
);
});
});
});

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-fastify",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-fastify)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -18,19 +18,19 @@
"access": "public"
},
"dependencies": {
"@fastify/cors": "8.3.0",
"@fastify/cors": "8.2.1",
"@fastify/formbody": "7.4.0",
"@fastify/middie": "8.3.0",
"fastify": "4.18.0",
"light-my-request": "5.10.0",
"fastify": "4.17.0",
"light-my-request": "5.9.1",
"path-to-regexp": "3.2.0",
"tslib": "2.6.0"
"tslib": "2.5.2"
},
"peerDependencies": {
"@fastify/static": "^6.0.0",
"@fastify/view": "^7.0.0",
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0"
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0"
},
"peerDependenciesMeta": {
"@fastify/static": {

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-socket.io",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-socket.io)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -18,12 +18,12 @@
"access": "public"
},
"dependencies": {
"socket.io": "4.7.0",
"tslib": "2.6.0"
"socket.io": "4.6.1",
"tslib": "2.5.2"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/websockets": "^9.0.0",
"rxjs": "^7.1.0"
}
}

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-ws",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-ws)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -18,12 +18,12 @@
"access": "public"
},
"dependencies": {
"tslib": "2.6.0",
"tslib": "2.5.2",
"ws": "8.13.0"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/websockets": "^9.0.0",
"rxjs": "^7.1.0"
}
}

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/testing",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@testing)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -18,13 +18,13 @@
"access": "public"
},
"dependencies": {
"tslib": "2.6.0"
"tslib": "2.5.2"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/microservices": "^10.0.0",
"@nestjs/platform-express": "^10.0.0"
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/microservices": "^9.0.0",
"@nestjs/platform-express": "^9.0.0"
},
"peerDependenciesMeta": {
"@nestjs/microservices": {

View File

@@ -12,7 +12,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"useUnknownInCatchVariables": false,
"target": "ES2021",
"target": "es2017",
"sourceMap": false,
"allowJs": false,
"strict": true,

View File

@@ -136,8 +136,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/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/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td>
</tr></table>
## Backers

View File

@@ -23,7 +23,7 @@ import {
InterceptorsContextCreator,
} from '@nestjs/core/interceptors';
import { PipesConsumer, PipesContextCreator } from '@nestjs/core/pipes';
import { MESSAGE_METADATA, PARAM_ARGS_METADATA } from '../constants';
import { PARAM_ARGS_METADATA } from '../constants';
import { WsException } from '../errors/ws-exception';
import { WsParamsFactory } from '../factories/ws-params-factory';
import { ExceptionFiltersContext } from './exception-filters-context';
@@ -66,6 +66,7 @@ export class WsContextCreator {
methodName,
contextType,
);
const exceptionHandler = this.exceptionFiltersContext.create(
instance,
callback,
@@ -109,8 +110,6 @@ export class WsContextCreator {
};
return this.wsProxy.create(async (...args: unknown[]) => {
args.push(this.reflectCallbackPattern(callback));
const initialArgs = this.contextUtils.createNullArray(argsLength);
fnCanActivate && (await fnCanActivate(args));
@@ -132,10 +131,6 @@ export class WsContextCreator {
return Reflect.getMetadata(PARAMTYPES_METADATA, instance, callback.name);
}
public reflectCallbackPattern(callback: (...args: any[]) => any): string {
return Reflect.getMetadata(MESSAGE_METADATA, callback);
}
public createGuardsFn<TContext extends string = ContextType>(
guards: any[],
instance: Controller,

View File

@@ -2,7 +2,7 @@ import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.int
/**
* External interface
* @see https://github.com/socketio/socket.io/blob/master/lib/index.ts
* @see https://github.com/socketio/socket.io/blob/master/lib/index.ts
* @publicApi
*/
export interface GatewayMetadata {

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/websockets",
"version": "10.0.4",
"version": "9.4.2",
"description": "Nest - modern, fast, powerful node.js web framework (@websockets)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -15,16 +15,16 @@
"dependencies": {
"iterare": "1.2.1",
"object-hash": "3.0.0",
"tslib": "2.6.0"
"tslib": "2.5.2"
},
"devDependencies": {
"@nestjs/common": "10.0.4",
"@nestjs/core": "10.0.4"
"@nestjs/common": "9.4.2",
"@nestjs/core": "9.4.2"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-socket.io": "^10.0.0",
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-socket.io": "^9.0.0",
"reflect-metadata": "^0.1.12",
"rxjs": "^7.1.0"
},

View File

@@ -4,8 +4,8 @@
[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 href="http://nodejs.cn" target="blank">Node.js</a> 框架,深受 <a href="https://angular.cn" target="_blank">Angular</a> 的启发。</p>
<p align="center">用于构建高效且可扩展的服务器端应用程序的渐进式 <a href="http://nodejs.cn" target="blank">Node.js</a> 框架,深受 <a href="https://angular.cn" target="_blank">Angular</a> 的启发。</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>
@@ -46,7 +46,7 @@ QQ群277386223
QQ群336289049
QQ群489719517
> 注意:这个交流群不是 NestJS 官方支持和维护的,只是为了方便国内开发者交流而建立的。
> 注意:这个交流群不是 NestJS 官方支持和维护的,只是为了方便国内开发者交流而建立的。
> Note: This channel is not officially supported and maintained by the NestJS org members.
## 赞助商

File diff suppressed because it is too large Load Diff

View File

@@ -19,36 +19,36 @@
"test:e2e": "jest --config ./e2e/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "10.0.3",
"@nestjs/core": "10.0.3",
"@nestjs/platform-express": "10.0.3",
"@nestjs/common": "9.3.12",
"@nestjs/core": "9.3.12",
"@nestjs/platform-express": "9.3.12",
"class-transformer": "0.5.1",
"class-validator": "0.14.0",
"reflect-metadata": "0.1.13",
"rimraf": "5.0.1",
"rxjs": "7.8.1"
"rimraf": "4.4.1",
"rxjs": "7.5.5"
},
"devDependencies": {
"@nestjs/cli": "10.0.5",
"@nestjs/schematics": "10.0.1",
"@nestjs/testing": "10.0.3",
"@types/express": "4.17.17",
"@types/jest": "29.5.2",
"@types/node": "20.3.1",
"@nestjs/cli": "9.4.0",
"@nestjs/schematics": "9.0.4",
"@nestjs/testing": "9.3.12",
"@types/express": "4.17.13",
"@types/jest": "28.1.4",
"@types/node": "18.0.3",
"@types/supertest": "2.0.12",
"jest": "29.5.0",
"prettier": "2.8.8",
"supertest": "6.3.3",
"ts-jest": "29.1.0",
"ts-loader": "9.4.3",
"ts-node": "10.9.1",
"jest": "28.1.2",
"prettier": "2.7.1",
"supertest": "6.2.4",
"ts-jest": "28.0.8",
"ts-loader": "9.3.1",
"ts-node": "10.8.2",
"tsconfig-paths": "4.2.0",
"@typescript-eslint/eslint-plugin": "5.60.0",
"@typescript-eslint/parser": "5.60.0",
"eslint": "8.42.0",
"@typescript-eslint/eslint-plugin": "5.56.0",
"@typescript-eslint/parser": "5.56.0",
"eslint": "8.19.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.27.5",
"typescript": "5.1.3"
"eslint-plugin-import": "2.26.0",
"typescript": "4.9.5"
},
"jest": {
"moduleFileExtensions": [

File diff suppressed because it is too large Load Diff

View File

@@ -19,42 +19,42 @@
"test:e2e": "jest --config ./e2e/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "10.0.3",
"@nestjs/core": "10.0.3",
"@nestjs/platform-express": "10.0.3",
"@nestjs/platform-socket.io": "10.0.3",
"@nestjs/websockets": "10.0.3",
"@socket.io/redis-adapter": "8.2.1",
"@nestjs/common": "9.3.12",
"@nestjs/core": "9.3.12",
"@nestjs/platform-express": "9.3.12",
"@nestjs/platform-socket.io": "9.3.12",
"@nestjs/websockets": "9.3.12",
"@socket.io/redis-adapter": "7.2.0",
"class-transformer": "0.5.1",
"class-validator": "0.14.0",
"reflect-metadata": "0.1.13",
"rimraf": "5.0.1",
"rxjs": "7.8.1",
"socket.io": "4.7.0"
"rimraf": "4.4.1",
"rxjs": "7.5.5",
"socket.io": "4.6.1"
},
"devDependencies": {
"@nestjs/cli": "10.0.5",
"@nestjs/schematics": "10.0.1",
"@nestjs/testing": "10.0.3",
"@types/express": "4.17.17",
"@types/jest": "29.5.2",
"@types/node": "20.3.1",
"@nestjs/cli": "9.4.0",
"@nestjs/schematics": "9.0.4",
"@nestjs/testing": "9.3.12",
"@types/express": "4.17.13",
"@types/jest": "28.1.4",
"@types/node": "18.0.3",
"@types/supertest": "2.0.12",
"@types/ws": "8.5.5",
"@typescript-eslint/eslint-plugin": "5.60.0",
"@typescript-eslint/parser": "5.60.0",
"eslint": "8.42.0",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.56.0",
"@typescript-eslint/parser": "5.56.0",
"eslint": "8.19.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.27.5",
"jest": "29.5.0",
"prettier": "2.8.8",
"redis": "4.6.7",
"supertest": "6.3.3",
"ts-jest": "29.1.0",
"ts-loader": "9.4.3",
"ts-node": "10.9.1",
"eslint-plugin-import": "2.26.0",
"jest": "28.1.2",
"prettier": "2.7.1",
"redis": "4.6.5",
"supertest": "6.2.4",
"ts-jest": "28.0.8",
"ts-loader": "9.3.1",
"ts-node": "10.8.2",
"tsconfig-paths": "4.2.0",
"typescript": "5.1.3"
"typescript": "4.9.5"
},
"jest": {
"moduleFileExtensions": [

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More