chore: resolve conflicts

This commit is contained in:
Kamil Myśliwiec
2025-01-07 12:24:10 +01:00
30 changed files with 937 additions and 306 deletions

View File

@@ -50,6 +50,16 @@ class TestController {
return RETURN_VALUE;
}
@Get('legacy-wildcard/overview')
testLegacyWildcard() {
return RETURN_VALUE;
}
@Get('splat-wildcard/overview')
testSplatWildcard() {
return RETURN_VALUE;
}
@Get('overview/:id')
overviewById() {
return RETURN_VALUE;
@@ -64,10 +74,17 @@ class TestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => res.end(MIDDLEWARE_VALUE))
.exclude('test', 'overview/:id', 'wildcard/(.*)', {
path: 'middleware',
method: RequestMethod.POST,
})
.exclude(
'test',
'overview/:id',
'wildcard/*',
'legacy-wildcard/(.*)',
'splat-wildcard/*splat',
{
path: 'middleware',
method: RequestMethod.POST,
},
)
.forRoutes('*');
}
}
@@ -126,6 +143,18 @@ describe('Exclude middleware (fastify)', () => {
.expect(200, RETURN_VALUE);
});
it(`should exclude "/legacy-wildcard/overview" endpoint (by wildcard, legacy syntax)`, () => {
return request(app.getHttpServer())
.get('/legacy-wildcard/overview')
.expect(200, RETURN_VALUE);
});
it(`should exclude "/splat-wildcard/overview" endpoint (by wildcard, new syntax)`, () => {
return request(app.getHttpServer())
.get('/splat-wildcard/overview')
.expect(200, RETURN_VALUE);
});
afterEach(async () => {
await app.close();
});

View File

@@ -41,6 +41,16 @@ class TestController {
return RETURN_VALUE;
}
@Get('legacy-wildcard/overview')
testLegacyWildcard() {
return RETURN_VALUE;
}
@Get('splat-wildcard/overview')
testSplatWildcard() {
return RETURN_VALUE;
}
@Get('overview/:id')
overviewById() {
return RETURN_VALUE;
@@ -60,12 +70,19 @@ class TestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => res.send(MIDDLEWARE_VALUE))
.exclude('test', 'overview/:id', 'wildcard/(.*)', {
path: 'middleware',
method: RequestMethod.POST,
})
.exclude(
'test',
'overview/:id',
'wildcard/*',
'legacy-wildcard/(.*)',
'splat-wildcard/*splat',
{
path: 'middleware',
method: RequestMethod.POST,
},
)
.exclude('multiple/exclude')
.forRoutes('*');
.forRoutes('*path');
}
}
@@ -116,6 +133,18 @@ describe('Exclude middleware', () => {
.expect(200, RETURN_VALUE);
});
it(`should exclude "/legacy-wildcard/overview" endpoint (by wildcard, legacy syntax)`, () => {
return request(app.getHttpServer())
.get('/legacy-wildcard/overview')
.expect(200, RETURN_VALUE);
});
it(`should exclude "/splat-wildcard/overview" endpoint (by wildcard, new syntax)`, () => {
return request(app.getHttpServer())
.get('/splat-wildcard/overview')
.expect(200, RETURN_VALUE);
});
it(`should exclude "/multiple/exclude" endpoint`, () => {
return request(app.getHttpServer())
.get('/multiple/exclude')

View File

@@ -3,10 +3,11 @@ import { ExpressAdapter } from '@nestjs/platform-express';
import { Test } from '@nestjs/testing';
import * as express from 'express';
import * as request from 'supertest';
import { App } from 'supertest/types';
import { AppModule } from '../src/app.module';
describe('Hello world (express instance)', () => {
let server;
let server: App;
let app: INestApplication;
beforeEach(async () => {

View File

@@ -8,9 +8,9 @@ import {
RequestMethod,
} from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Response } from 'express';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { Response } from 'express';
const INCLUDED_VALUE = 'test_included';
const RETURN_VALUE = 'test';

View File

@@ -15,9 +15,9 @@ import {
} from '@nestjs/platform-fastify';
import { Test } from '@nestjs/testing';
import { expect } from 'chai';
import { AppModule } from '../src/app.module';
import * as request from 'supertest';
import { FastifyRequest } from 'fastify';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
describe('Middleware (FastifyAdapter)', () => {
let app: NestFastifyApplication;
@@ -37,6 +37,11 @@ describe('Middleware (FastifyAdapter)', () => {
return RETURN_VALUE;
}
@Get('legacy_style_wildcard/wildcard_nested')
legacy_style_wildcard() {
return RETURN_VALUE;
}
@Get('test')
test() {
return RETURN_VALUE;
@@ -76,9 +81,13 @@ describe('Middleware (FastifyAdapter)', () => {
.apply((req, res, next) => res.end(INCLUDED_VALUE))
.forRoutes({ path: 'tests/included', method: RequestMethod.POST })
.apply((req, res, next) => res.end(REQ_URL_VALUE))
.forRoutes('req/url/(.*)')
.forRoutes('req/url/*')
.apply((req, res, next) => res.end(WILDCARD_VALUE))
.forRoutes('express_style_wildcard/*', 'tests/(.*)')
.forRoutes(
'express_style_wildcard/*',
'tests/*path',
'legacy_style_wildcard/(.*)',
)
.apply((req, res, next) => res.end(QUERY_VALUE))
.forRoutes('query')
.apply((req, res, next) => next())
@@ -87,7 +96,7 @@ describe('Middleware (FastifyAdapter)', () => {
.forRoutes(TestController)
.apply((req, res, next) => res.end(RETURN_VALUE))
.exclude({ path: QUERY_VALUE, method: -1 as any })
.forRoutes('(.*)');
.forRoutes('*');
}
}
@@ -101,7 +110,7 @@ describe('Middleware (FastifyAdapter)', () => {
await app.init();
});
it(`forRoutes((.*))`, () => {
it(`forRoutes(*)`, () => {
return app
.inject({
method: 'GET',
@@ -143,7 +152,7 @@ describe('Middleware (FastifyAdapter)', () => {
.then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE));
});
it(`forRoutes(tests/(.*))`, () => {
it(`forRoutes(tests/*path)`, () => {
return app
.inject({
method: 'GET',
@@ -161,6 +170,15 @@ describe('Middleware (FastifyAdapter)', () => {
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
});
it(`forRoutes(legacy_style_wildcard/*)`, () => {
return app
.inject({
method: 'GET',
url: '/legacy_style_wildcard/wildcard_nested',
})
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
});
it(`forRoutes(req/url/)`, () => {
const reqUrl = '/test';
return app

View File

@@ -26,6 +26,11 @@ class TestController {
return RETURN_VALUE;
}
@Get('legacy-wildcard/overview')
legacyWildcard() {
return RETURN_VALUE;
}
@Get('exclude')
exclude() {
return EXCLUDE_VALUE;
@@ -40,7 +45,7 @@ class TestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req, res, next) => res.send(WILDCARD_VALUE))
.forRoutes('tests/*')
.forRoutes('tests/*path', 'legacy-wildcard/*')
.apply((req, res, next) => res.send(SCOPED_VALUE))
.exclude('exclude')
.forRoutes(TestController)
@@ -86,6 +91,13 @@ describe('Middleware', () => {
.expect(200, WILDCARD_VALUE);
});
it(`forRoutes(legacy-wildcard/*)`, async () => {
app = await createApp();
return request(app.getHttpServer())
.get('/legacy-wildcard/overview')
.expect(200, WILDCARD_VALUE);
});
afterEach(async () => {
await app.close();
});

View File

@@ -127,7 +127,7 @@ describe('Global prefix', () => {
await request(server)
.get('/api/test/params')
.expect(200, { '0': 'params', tenantId: 'test' });
.expect(200, { tenantId: 'test', path: ['params'] });
});
it(`should execute middleware only once`, async () => {

View File

@@ -16,24 +16,30 @@ export class AppModule {
.apply((req, res, next) => res.status(201).end(MIDDLEWARE_VALUE))
.forRoutes({ path: MIDDLEWARE_VALUE, method: RequestMethod.POST })
.apply((req, res, next) => res.end(MIDDLEWARE_PARAM_VALUE))
.forRoutes({ path: MIDDLEWARE_VALUE + '/*', method: RequestMethod.GET })
.forRoutes({
path: MIDDLEWARE_VALUE + '/*path',
method: RequestMethod.GET,
})
.apply((req, res, next) => res.status(201).end(MIDDLEWARE_PARAM_VALUE))
.forRoutes({ path: MIDDLEWARE_VALUE + '/*', method: RequestMethod.POST })
.forRoutes({
path: MIDDLEWARE_VALUE + '/*path',
method: RequestMethod.POST,
})
.apply((req, res, next) => {
req.extras = { data: 'Data attached in middleware' };
next();
})
.forRoutes({ path: '*', method: RequestMethod.GET })
.forRoutes({ path: '*path', method: RequestMethod.GET })
.apply((req, res, next) => {
req.middlewareParams = req.params;
next();
})
.forRoutes({ path: '*', method: RequestMethod.GET })
.forRoutes({ path: '*path', method: RequestMethod.GET })
.apply((req, res, next) => {
this.count += 1;
req.count = this.count;
next();
})
.forRoutes('*');
.forRoutes('*path');
}
}

View File

@@ -1,7 +1,5 @@
{
"lerna": "2.4.0",
"packages": [
"packages/*"
],
"version": "10.4.15"
"packages": ["packages/*"],
"version": "11.0.0-next.1"
}

795
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -64,12 +64,12 @@
"class-transformer": "0.5.1",
"class-validator": "0.14.1",
"cors": "2.8.5",
"express": "4.21.2",
"express": "5.0.1",
"fast-json-stringify": "6.0.0",
"fast-safe-stringify": "2.1.1",
"iterare": "1.2.1",
"object-hash": "3.0.0",
"path-to-regexp": "3.3.0",
"path-to-regexp": "8.2.0",
"reflect-metadata": "0.2.2",
"rxjs": "7.8.1",
"socket.io": "4.8.1",
@@ -84,10 +84,10 @@
"@commitlint/config-angular": "19.6.0",
"@eslint/eslintrc": "3.2.0",
"@eslint/js": "9.15.0",
"@fastify/cors": "9.0.1",
"@fastify/formbody": "7.4.0",
"@fastify/middie": "8.3.3",
"@fastify/multipart": "8.3.0",
"@fastify/cors": "10.0.1",
"@fastify/formbody": "8.0.1",
"@fastify/middie": "9.0.2",
"@fastify/multipart": "9.0.1",
"@fastify/static": "7.0.4",
"@fastify/view": "9.1.0",
"@grpc/grpc-js": "1.12.4",
@@ -102,7 +102,7 @@
"@types/chai-as-promised": "7.1.8",
"@types/cors": "2.8.17",
"@types/eslint__js": "8.42.3",
"@types/express": "4.17.21",
"@types/express": "5.0.0",
"@types/gulp": "4.0.17",
"@types/http-errors": "2.0.4",
"@types/mocha": "10.0.10",

View File

@@ -102,4 +102,5 @@ export interface HttpServer<
version: VersionValue,
versioningOptions: VersioningOptions,
): (req: TRequest, res: TResponse, next: () => void) => Function;
normalizePath?(path: string): string;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/common",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
"author": "Kamil Mysliwiec",
"homepage": "https://nestjs.com",

View File

@@ -143,6 +143,10 @@ export abstract class AbstractHttpAdapter<
return this.instance as T;
}
public normalizePath(path: string): string {
return path;
}
abstract close();
abstract initHttpServer(options: NestApplicationOptions);
abstract useStaticAssets(...args: any[]);
@@ -158,11 +162,7 @@ export abstract class AbstractHttpAdapter<
abstract setErrorHandler(handler: Function, prefix?: string);
abstract setNotFoundHandler(handler: Function, prefix?: string);
abstract isHeadersSent(response: any);
// TODO remove optional signature (v11)
abstract getHeader?(response: any, name: string);
abstract setHeader(response: any, name: string, value: string);
// TODO remove optional signature (v11)
abstract appendHeader?(response: any, name: string, value: string);
abstract registerParserMiddleware(prefix?: string, rawBody?: boolean);
abstract enableCors(
options: CorsOptions | CorsOptionsDelegate<TRequest>,

View File

@@ -71,7 +71,9 @@ export class RouteInfoPathExtractor {
}
private isAWildcard(path: string): boolean {
return ['*', '/*', '/*/', '(.*)', '/(.*)'].includes(path);
return ['*', '/*', '/*/', '*path', '/*path', '(.*)', '/(.*)'].includes(
path,
);
}
private extractNonWildcardPathsFrom({

View File

@@ -6,27 +6,38 @@ import {
isString,
} from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import * as pathToRegexp from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp';
import { uid } from 'uid';
import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface';
import { LegacyRouteConverter } from '../router/legacy-route-converter';
import { isRouteExcluded } from '../router/utils';
export const mapToExcludeRoute = (
routes: (string | RouteInfo)[],
): ExcludeRouteMetadata[] => {
return routes.map(route => {
if (isString(route)) {
const originalPath = isString(route) ? route : route.path;
const path = LegacyRouteConverter.tryConvert(originalPath);
try {
if (isString(route)) {
return {
path,
requestMethod: RequestMethod.ALL,
pathRegex: pathToRegexp(addLeadingSlash(path)).regexp,
};
}
return {
path: route,
requestMethod: RequestMethod.ALL,
pathRegex: pathToRegexp(addLeadingSlash(route)),
path,
requestMethod: route.method,
pathRegex: pathToRegexp(addLeadingSlash(path)).regexp,
};
} catch (e) {
if (e instanceof TypeError) {
LegacyRouteConverter.printError(originalPath);
}
throw e;
}
return {
path: route.path,
requestMethod: route.method,
pathRegex: pathToRegexp(addLeadingSlash(route.path)),
};
});
};

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/core",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@core)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -31,12 +31,12 @@
"@nuxtjs/opencollective": "0.3.2",
"fast-safe-stringify": "2.1.1",
"iterare": "1.2.1",
"path-to-regexp": "3.3.0",
"path-to-regexp": "8.2.0",
"tslib": "2.8.1",
"uid": "2.0.2"
},
"devDependencies": {
"@nestjs/common": "10.4.15"
"@nestjs/common": "^11.0.0-next.1"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",

View File

@@ -0,0 +1,53 @@
import { Logger } from '@nestjs/common';
const UNSUPPORTED_PATH_MESSAGE = (text: TemplateStringsArray, route: string) =>
`Unsupported route path: "${route}". In previous versions, the symbols ?, *, and + were used to denote optional or repeating path parameters. The latest version of "path-to-regexp" now requires the use of named parameters. For example, instead of using a route like /users/* to capture all routes starting with "/users", you should use /users/*path. For more details, refer to the migration guide.`;
export class LegacyRouteConverter {
private static readonly logger = new Logger(LegacyRouteConverter.name);
/**
* Convert legacy routes to the new format (syntax).
* path-to-regexp used by Express>=v5 and @fastify/middie>=v9 no longer support unnamed wildcards.
* This method attempts to convert the old syntax to the new one, and logs an error if it fails.
* @param route The route to convert.
* @returns The converted route, or the original route if it cannot be converted.
*/
static tryConvert(route: string): string {
// Normalize path to eliminate additional conditions.
const routeWithLeadingSlash = route.startsWith('/') ? route : `/${route}`;
const normalizedRoute = route.endsWith('/')
? routeWithLeadingSlash
: `${routeWithLeadingSlash}/`;
if (normalizedRoute.endsWith('/(.*)/')) {
// Skip printing warning for the "all" wildcard.
if (normalizedRoute !== '/(.*)/') {
this.printWarning(route);
}
return route.replace('(.*)', '{*path}');
}
if (normalizedRoute.endsWith('/*/')) {
// Skip printing warning for the "all" wildcard.
if (normalizedRoute !== '/*/') {
this.printWarning(route);
}
return route.replace('*', '{*path}');
}
if (normalizedRoute.endsWith('/+/')) {
this.printWarning(route);
return route.replace('/+', '/*path');
}
return route;
}
static printError(route: string): void {
this.logger.error(UNSUPPORTED_PATH_MESSAGE`${route}`);
}
static printWarning(route: string): void {
this.logger.warn(
UNSUPPORTED_PATH_MESSAGE`${route}` + ' Attempting to convert...',
);
}
}

View File

@@ -10,7 +10,7 @@ import {
addLeadingSlash,
isUndefined,
} from '@nestjs/common/utils/shared.utils';
import * as pathToRegexp from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp';
import { ApplicationConfig } from '../application-config';
import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception';
import { GuardsConsumer, GuardsContextCreator } from '../guards';
@@ -231,7 +231,10 @@ export class RouterExplorer {
};
this.copyMetadataToCallback(targetCallback, routeHandler);
routerMethodRef(path, routeHandler);
const normalizedPath = router.normalizePath
? router.normalizePath(path)
: path;
routerMethodRef(normalizedPath, routeHandler);
this.graphInspector.insertEntrypointDefinition<HttpEntrypointMetadata>(
entrypointDefinition,
@@ -270,9 +273,19 @@ export class RouterExplorer {
const httpAdapterRef = this.container.getHttpAdapterRef();
const hosts = Array.isArray(host) ? host : [host];
const hostRegExps = hosts.map((host: string | RegExp) => {
const keys: any[] = [];
const regexp = pathToRegexp(host, keys);
return { regexp, keys };
if (typeof host === 'string') {
try {
return pathToRegexp(host);
} catch (e) {
if (e instanceof TypeError) {
this.logger.error(
`Unsupported host "${host}" syntax. In past releases, ?, *, and + were used to denote optional or repeating path parameters. The latest version of "path-to-regexp" now requires the use of named parameters. For example, instead of using a route like /users/* to capture all routes starting with "/users", you should use /users/*path. Please see the migration guide for more information.`,
);
}
throw e;
}
}
return { regexp: host, keys: [] };
});
const unsupportedFilteringErrorMessage = Array.isArray(host)

View File

@@ -1,6 +1,7 @@
import { RequestMethod, Type } from '@nestjs/common';
import { RequestMethod } from '@nestjs/common';
import { addLeadingSlash } from '@nestjs/common/utils/shared.utils';
import { expect } from 'chai';
import { pathToRegexp } from 'path-to-regexp';
import * as sinon from 'sinon';
import {
assignToken,
@@ -11,7 +12,6 @@ import {
mapToExcludeRoute,
} from '../../middleware/utils';
import { NoopHttpAdapter } from '../utils/noop-adapter.spec';
import * as pathToRegexp from 'path-to-regexp';
describe('middleware utils', () => {
const noopAdapter = new NoopHttpAdapter({});
@@ -30,12 +30,12 @@ describe('middleware utils', () => {
{
path: stringRoute,
requestMethod: RequestMethod.ALL,
pathRegex: pathToRegexp(addLeadingSlash(stringRoute)),
pathRegex: pathToRegexp(addLeadingSlash(stringRoute)).regexp,
},
{
path: routeInfo.path,
requestMethod: routeInfo.method,
pathRegex: pathToRegexp(addLeadingSlash(routeInfo.path)),
pathRegex: pathToRegexp(addLeadingSlash(routeInfo.path)).regexp,
},
]);
});
@@ -121,7 +121,20 @@ describe('middleware utils', () => {
sinon.stub(adapter, 'getRequestMethod').callsFake(() => 'GET');
sinon.stub(adapter, 'getRequestUrl').callsFake(() => '/cats/3');
});
describe('when route is excluded', () => {
describe('when route is excluded (new syntax *path)', () => {
const path = '/cats/*path';
const excludedRoutes = mapToExcludeRoute([
{
path,
method: RequestMethod.GET,
},
]);
it('should return true', () => {
expect(isMiddlewareRouteExcluded({}, excludedRoutes, adapter)).to.be
.true;
});
});
describe('when route is excluded (legacy syntax (.*))', () => {
const path = '/cats/(.*)';
const excludedRoutes = mapToExcludeRoute([
{

View File

@@ -1,6 +1,6 @@
import { RequestMethod, VersioningType, VERSION_NEUTRAL } from '@nestjs/common';
import { RequestMethod, VERSION_NEUTRAL, VersioningType } from '@nestjs/common';
import { expect } from 'chai';
import * as pathToRegexp from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp';
import * as sinon from 'sinon';
import { ApplicationConfig } from '../../application-config';
import { RoutePathFactory } from '../../router/route-path-factory';
@@ -248,7 +248,7 @@ describe('RoutePathFactory', () => {
exclude: [
{
path: '/random',
pathRegex: pathToRegexp('/random'),
pathRegex: pathToRegexp('/random').regexp,
requestMethod: RequestMethod.ALL,
},
],
@@ -267,7 +267,7 @@ describe('RoutePathFactory', () => {
exclude: [
{
path: '/cats',
pathRegex: pathToRegexp('/cats'),
pathRegex: pathToRegexp('/cats').regexp,
requestMethod: RequestMethod.ALL,
},
],
@@ -286,7 +286,7 @@ describe('RoutePathFactory', () => {
exclude: [
{
path: '/cats',
pathRegex: pathToRegexp('/cats'),
pathRegex: pathToRegexp('/cats').regexp,
requestMethod: RequestMethod.GET,
},
],

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/microservices",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@microservices)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -22,8 +22,8 @@
"tslib": "2.8.1"
},
"devDependencies": {
"@nestjs/common": "10.4.15",
"@nestjs/core": "10.4.15"
"@nestjs/common": "^11.0.0-next.1",
"@nestjs/core": "^11.0.0-next.1"
},
"peerDependencies": {
"@grpc/grpc-js": "*",

View File

@@ -23,6 +23,7 @@ import {
} from '@nestjs/common/utils/shared.utils';
import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter';
import { RouterMethodFactory } from '@nestjs/core/helpers/router-method-factory';
import { LegacyRouteConverter } from '@nestjs/core/router/legacy-route-converter';
import * as bodyparser from 'body-parser';
import {
json as bodyParserJson,
@@ -143,7 +144,7 @@ export class ExpressAdapter extends AbstractHttpAdapter<
return response.headersSent;
}
public getHeader?(response: any, name: string) {
public getHeader(response: any, name: string) {
return response.get(name);
}
@@ -151,10 +152,22 @@ export class ExpressAdapter extends AbstractHttpAdapter<
return response.set(name, value);
}
public appendHeader?(response: any, name: string, value: string) {
public appendHeader(response: any, name: string, value: string) {
return response.append(name, value);
}
public normalizePath(path: string): string {
try {
const convertedPath = LegacyRouteConverter.tryConvert(path);
return convertedPath;
} catch (e) {
if (e instanceof TypeError) {
LegacyRouteConverter.printError(path);
}
throw e;
}
}
public listen(port: string | number, callback?: () => void): Server;
public listen(
port: string | number,
@@ -224,9 +237,19 @@ export class ExpressAdapter extends AbstractHttpAdapter<
public createMiddlewareFactory(
requestMethod: RequestMethod,
): (path: string, callback: Function) => any {
return this.routerMethodFactory
.get(this.instance, requestMethod)
.bind(this.instance);
return (path: string, callback: Function) => {
try {
const convertedPath = LegacyRouteConverter.tryConvert(path);
return this.routerMethodFactory
.get(this.instance, requestMethod)
.call(this.instance, convertedPath, callback);
} catch (e) {
if (e instanceof TypeError) {
LegacyRouteConverter.printError(path);
}
throw e;
}
};
}
public initHttpServer(options: NestApplicationOptions) {

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-express",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-express)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -20,13 +20,13 @@
"dependencies": {
"body-parser": "1.20.3",
"cors": "2.8.5",
"express": "4.21.2",
"express": "5.0.1",
"multer": "1.4.4-lts.1",
"tslib": "2.8.1"
},
"devDependencies": {
"@nestjs/common": "10.4.15",
"@nestjs/core": "10.4.15"
"@nestjs/common": "^11.0.0-next.1",
"@nestjs/core": "^11.0.0-next.1"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",

View File

@@ -17,6 +17,7 @@ import {
import { loadPackage } from '@nestjs/common/utils/load-package.util';
import { isString, isUndefined } from '@nestjs/common/utils/shared.utils';
import { AbstractHttpAdapter } from '@nestjs/core/adapters/http-adapter';
import { LegacyRouteConverter } from '@nestjs/core/router/legacy-route-converter';
import {
FastifyBaseLogger,
FastifyBodyParser,
@@ -49,7 +50,7 @@ import {
Chain as LightMyRequestChain,
Response as LightMyRequestResponse,
} from 'light-my-request';
import * as pathToRegexp from 'path-to-regexp';
import { pathToRegexp } from 'path-to-regexp';
// `querystring` is used internally in fastify for registering urlencoded body parser.
import { parse as querystringParse } from 'querystring';
import {
@@ -142,6 +143,7 @@ export class FastifyAdapter<
TRawResponse
> = FastifyInstance<TServer, TRawRequest, TRawResponse>,
> extends AbstractHttpAdapter<TServer, TRequest, TReply> {
protected readonly logger = new Logger(FastifyAdapter.name);
protected readonly instance: TInstance;
protected _pathPrefix?: string;
@@ -622,39 +624,46 @@ export class FastifyAdapter<
const hasEndOfStringCharacter = path.endsWith('$');
path = hasEndOfStringCharacter ? path.slice(0, -1) : path;
let normalizedPath = path.endsWith('/*')
? `${path.slice(0, -1)}(.*)`
: path;
let normalizedPath = LegacyRouteConverter.tryConvert(path);
// Fallback to "(.*)" to support plugins like GraphQL
normalizedPath = normalizedPath === '/(.*)' ? '(.*)' : normalizedPath;
// Fallback to "*path" to support plugins like GraphQL
normalizedPath = normalizedPath === '/*path' ? '*path' : normalizedPath;
// Normalize the path to support the prefix if it set in application
normalizedPath =
this._pathPrefix && !normalizedPath.startsWith(this._pathPrefix)
? `${this._pathPrefix}${normalizedPath}(.*)`
? `${this._pathPrefix}${normalizedPath}*path`
: normalizedPath;
let re = pathToRegexp(normalizedPath);
re = hasEndOfStringCharacter ? new RegExp(re.source + '$', re.flags) : re;
try {
let { regexp: re } = pathToRegexp(normalizedPath);
re = hasEndOfStringCharacter
? new RegExp(re.source + '$', re.flags)
: re;
// The following type assertion is valid as we use import('@fastify/middie') rather than require('@fastify/middie')
// ref https://github.com/fastify/middie/pull/55
this.instance.use(
normalizedPath,
(req: any, res: any, next: Function) => {
const queryParamsIndex = req.originalUrl.indexOf('?');
const pathname =
queryParamsIndex >= 0
? req.originalUrl.slice(0, queryParamsIndex)
: req.originalUrl;
// The following type assertion is valid as we use import('@fastify/middie') rather than require('@fastify/middie')
// ref https://github.com/fastify/middie/pull/55
this.instance.use(
normalizedPath,
(req: any, res: any, next: Function) => {
const queryParamsIndex = req.originalUrl.indexOf('?');
const pathname =
queryParamsIndex >= 0
? req.originalUrl.slice(0, queryParamsIndex)
: req.originalUrl;
if (!re.exec(pathname + '/') && normalizedPath) {
return next();
}
return callback(req, res, next);
},
);
if (!re.exec(pathname + '/') && normalizedPath) {
return next();
}
return callback(req, res, next);
},
);
} catch (e) {
if (e instanceof TypeError) {
LegacyRouteConverter.printError(path);
}
throw e;
}
};
}

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-fastify",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-fastify)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -23,7 +23,7 @@
"@fastify/middie": "9.0.2",
"fastify": "5.2.1",
"light-my-request": "6.3.0",
"path-to-regexp": "3.3.0",
"path-to-regexp": "8.2.0",
"tslib": "2.8.1"
},
"peerDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-socket.io",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-socket.io)",
"author": "Kamil Mysliwiec",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/platform-ws",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@platform-ws)",
"author": "Kamil Mysliwiec",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/testing",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@testing)",
"author": "Kamil Mysliwiec",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@nestjs/websockets",
"version": "10.4.15",
"version": "11.0.0-next.1",
"description": "Nest - modern, fast, powerful node.js web framework (@websockets)",
"author": "Kamil Mysliwiec",
"license": "MIT",
@@ -18,8 +18,8 @@
"tslib": "2.8.1"
},
"devDependencies": {
"@nestjs/common": "10.4.15",
"@nestjs/core": "10.4.15"
"@nestjs/common": "^11.0.0-next.1",
"@nestjs/core": "^11.0.0-next.1"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",