/
githubmirror
/
nest
Обзор
Документация
Войти
/
githubmirror
/
nest
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
packages/core/middleware/builder.ts
142 строки
4 KB
dipakchaudhari12717
fix(core): drop the g flag from middleware overlap regexes
18 июл 2026, 17:28
18 июл 2026, 17:28
6b0c9b2
Код
Авторство
О чём код?
import { HttpServer, MiddlewareConsumer, Type, } from '@nestjs/common/interfaces'; import { MiddlewareConfigProxy, 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'; import { filterMiddleware } from './utils'; export class MiddlewareBuilder implements MiddlewareConsumer { private readonly middlewareCollection = new Set<MiddlewareConfiguration>(); constructor( private readonly routesMapper: RoutesMapper, private readonly httpAdapter: HttpServer, private readonly routeInfoPathExtractor: RouteInfoPathExtractor, ) {} public apply( ...middleware: Array<Type<any> | Function | Array<Type<any> | Function>> ): MiddlewareConfigProxy { return new MiddlewareBuilder.ConfigProxy( this, middleware.flat(), this.routeInfoPathExtractor, ); } public build(): MiddlewareConfiguration[] { return [...this.middlewareCollection]; } public getHttpAdapter(): HttpServer { return this.httpAdapter; } private static readonly ConfigProxy = class implements MiddlewareConfigProxy { private excludedRoutes: RouteInfo[] = []; constructor( private readonly builder: MiddlewareBuilder, private readonly middleware: Array<Type<any> | Function>, private routeInfoPathExtractor: RouteInfoPathExtractor, ) {} public getExcludedRoutes(): RouteInfo[] { return this.excludedRoutes; } public exclude( ...routes: Array<string | RouteInfo> ): MiddlewareConfigProxy { this.excludedRoutes = [ ...this.excludedRoutes, ...this.getRoutesFlatList(routes).reduce((excludedRoutes, route) => { for (const routePath of this.routeInfoPathExtractor.extractPathFrom( route, )) { excludedRoutes.push({ ...route, path: routePath, }); } return excludedRoutes; }, [] as RouteInfo[]), ]; return this; } public forRoutes( ...routes: Array<string | Type<any> | RouteInfo> ): MiddlewareConsumer { const { middlewareCollection } = this.builder; const flattedRoutes = this.getRoutesFlatList(routes); const forRoutes = this.removeOverlappedRoutes(flattedRoutes); const configuration = { middleware: filterMiddleware( this.middleware, this.excludedRoutes, this.builder.getHttpAdapter(), ), forRoutes, }; middlewareCollection.add(configuration); return this.builder; } private getRoutesFlatList( routes: Array<string | Type<any> | RouteInfo>, ): RouteInfo[] { const { routesMapper } = this.builder; return iterate(routes) .map(route => routesMapper.mapRouteToRouteInfo(route)) .flatten() .toArray(); } private removeOverlappedRoutes(routes: RouteInfo[]) { const regexMatchParams = /(:[^/]*)/g; const wildcard = '([^/]*)'; const routesWithRegex = routes .filter(route => route.path.includes(':')) .map(route => ({ method: route.method, path: route.path, // No `g` flag: each regex is reused across every route below, and a // global regex advances `lastIndex` on a match, so the next `test()` // would resume mid-string and fail the `^` anchor. The pattern is // anchored and only used with `test()`, so `g` buys nothing anyway. regex: new RegExp( '^(' + route.path.replace(regexMatchParams, wildcard) + ')$', ), })); 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 routeMatch = routesWithRegex.find(isOverlapped); return routeMatch === undefined; }); } }; }