@morojs/moro 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/config/config-sources.js +4 -0
- package/dist/core/config/config-sources.js.map +1 -1
- package/dist/core/config/config-validator.js +3 -0
- package/dist/core/config/config-validator.js.map +1 -1
- package/dist/core/config/file-loader.js +3 -1
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/schema.js +4 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/events/event-bus.js +1 -1
- package/dist/core/events/event-bus.js.map +1 -1
- package/dist/core/framework.d.ts +1 -1
- package/dist/core/framework.js +13 -7
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +55 -15
- package/dist/core/http/http-server.js +70 -146
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/index.d.ts +1 -1
- package/dist/core/http/index.js +1 -1
- package/dist/core/http/index.js.map +1 -1
- package/dist/core/http/uws-http-server.d.ts +4 -22
- package/dist/core/http/uws-http-server.js +43 -208
- package/dist/core/http/uws-http-server.js.map +1 -1
- package/dist/core/networking/adapters/uws-adapter.d.ts +1 -1
- package/dist/core/networking/adapters/uws-adapter.js +1 -1
- package/dist/core/pooling/object-pool-manager.d.ts +140 -0
- package/dist/core/pooling/object-pool-manager.js +502 -0
- package/dist/core/pooling/object-pool-manager.js.map +1 -0
- package/dist/core/routing/app-integration.d.ts +12 -10
- package/dist/core/routing/app-integration.js +43 -74
- package/dist/core/routing/app-integration.js.map +1 -1
- package/dist/core/routing/index.d.ts +15 -29
- package/dist/core/routing/index.js +43 -390
- package/dist/core/routing/index.js.map +1 -1
- package/dist/core/routing/path-matcher.d.ts +67 -0
- package/dist/core/routing/path-matcher.js +182 -0
- package/dist/core/routing/path-matcher.js.map +1 -0
- package/dist/core/{http → routing}/router.d.ts +21 -9
- package/dist/core/routing/router.js +68 -0
- package/dist/core/routing/router.js.map +1 -0
- package/dist/core/routing/unified-router.d.ts +148 -0
- package/dist/core/routing/unified-router.js +684 -0
- package/dist/core/routing/unified-router.js.map +1 -0
- package/dist/moro.d.ts +10 -7
- package/dist/moro.js +90 -41
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +3 -0
- package/package.json +1 -1
- package/src/core/config/config-sources.ts +4 -0
- package/src/core/config/config-validator.ts +3 -0
- package/src/core/config/file-loader.ts +4 -1
- package/src/core/config/schema.ts +4 -1
- package/src/core/events/event-bus.ts +1 -1
- package/src/core/framework.ts +14 -9
- package/src/core/http/http-server.ts +76 -161
- package/src/core/http/index.ts +1 -1
- package/src/core/http/uws-http-server.ts +43 -246
- package/src/core/networking/adapters/uws-adapter.ts +1 -1
- package/src/core/pooling/object-pool-manager.ts +630 -0
- package/src/core/routing/app-integration.ts +57 -109
- package/src/core/routing/index.ts +62 -473
- package/src/core/routing/path-matcher.ts +222 -0
- package/src/core/routing/router.ts +97 -0
- package/src/core/routing/unified-router.ts +870 -0
- package/src/moro.ts +107 -57
- package/src/types/config.ts +3 -0
- package/dist/core/http/router.js +0 -183
- package/dist/core/http/router.js.map +0 -1
- package/src/core/http/router.ts +0 -230
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Shared Path Matching Utility with Caching
|
|
2
|
+
// Replaces 6 different pathToRegex implementations across the codebase
|
|
3
|
+
|
|
4
|
+
export interface CompiledPath {
|
|
5
|
+
pattern: RegExp | null;
|
|
6
|
+
paramNames: string[];
|
|
7
|
+
isStatic: boolean;
|
|
8
|
+
path: string;
|
|
9
|
+
segments: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MatchResult {
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* LRU Cache implementation for compiled paths
|
|
18
|
+
*/
|
|
19
|
+
class LRUCache<K, V> {
|
|
20
|
+
private cache = new Map<K, V>();
|
|
21
|
+
private readonly maxSize: number;
|
|
22
|
+
|
|
23
|
+
constructor(maxSize: number = 1000) {
|
|
24
|
+
this.maxSize = maxSize;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get(key: K): V | undefined {
|
|
28
|
+
const value = this.cache.get(key);
|
|
29
|
+
if (value !== undefined) {
|
|
30
|
+
// Move to end (most recently used)
|
|
31
|
+
this.cache.delete(key);
|
|
32
|
+
this.cache.set(key, value);
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set(key: K, value: V): void {
|
|
38
|
+
// Remove if exists (to update position)
|
|
39
|
+
if (this.cache.has(key)) {
|
|
40
|
+
this.cache.delete(key);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Add to end
|
|
44
|
+
this.cache.set(key, value);
|
|
45
|
+
|
|
46
|
+
// Evict oldest if over capacity
|
|
47
|
+
if (this.cache.size > this.maxSize) {
|
|
48
|
+
const firstKey = this.cache.keys().next().value;
|
|
49
|
+
if (firstKey !== undefined) {
|
|
50
|
+
this.cache.delete(firstKey);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
has(key: K): boolean {
|
|
56
|
+
return this.cache.has(key);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
clear(): void {
|
|
60
|
+
this.cache.clear();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get size(): number {
|
|
64
|
+
return this.cache.size;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* PathMatcher - Single source of truth for path pattern compilation and matching
|
|
70
|
+
*/
|
|
71
|
+
export class PathMatcher {
|
|
72
|
+
private static readonly cache = new LRUCache<string, CompiledPath>(1000);
|
|
73
|
+
private static compilationCount = 0;
|
|
74
|
+
private static cacheHits = 0;
|
|
75
|
+
private static cacheMisses = 0;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Compile a path pattern into an efficient matching structure
|
|
79
|
+
* Results are cached for performance
|
|
80
|
+
*/
|
|
81
|
+
static compile(path: string): CompiledPath {
|
|
82
|
+
// Check cache first
|
|
83
|
+
const cached = this.cache.get(path);
|
|
84
|
+
if (cached) {
|
|
85
|
+
this.cacheHits++;
|
|
86
|
+
return cached;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.cacheMisses++;
|
|
90
|
+
this.compilationCount++;
|
|
91
|
+
|
|
92
|
+
// Compile the pattern
|
|
93
|
+
const compiled = this.compileInternal(path);
|
|
94
|
+
this.cache.set(path, compiled);
|
|
95
|
+
return compiled;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Internal compilation logic
|
|
100
|
+
*/
|
|
101
|
+
private static compileInternal(path: string): CompiledPath {
|
|
102
|
+
const paramNames: string[] = [];
|
|
103
|
+
const isStatic = !path.includes(':') && !path.includes('*');
|
|
104
|
+
|
|
105
|
+
// Calculate segment count for optimization
|
|
106
|
+
const segments = path.split('/').filter(s => s.length > 0).length;
|
|
107
|
+
|
|
108
|
+
if (isStatic) {
|
|
109
|
+
// No regex needed for static routes
|
|
110
|
+
return {
|
|
111
|
+
pattern: null,
|
|
112
|
+
paramNames: [],
|
|
113
|
+
isStatic: true,
|
|
114
|
+
path,
|
|
115
|
+
segments,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Convert parameterized routes to regex
|
|
120
|
+
// Match :paramName and capture the parameter name
|
|
121
|
+
const regexPath = path
|
|
122
|
+
.replace(/\/:([^/]+)/g, (match, paramName) => {
|
|
123
|
+
paramNames.push(paramName);
|
|
124
|
+
return '/([^/]+)';
|
|
125
|
+
})
|
|
126
|
+
.replace(/\//g, '\\/');
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
pattern: new RegExp(`^${regexPath}$`),
|
|
130
|
+
paramNames,
|
|
131
|
+
isStatic: false,
|
|
132
|
+
path,
|
|
133
|
+
segments,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Match a request path against a compiled pattern
|
|
139
|
+
* Returns match result with extracted parameters, or null if no match
|
|
140
|
+
*/
|
|
141
|
+
static match(compiledPath: CompiledPath, requestPath: string): MatchResult | null {
|
|
142
|
+
// Path for static routes - simple string comparison
|
|
143
|
+
if (compiledPath.isStatic) {
|
|
144
|
+
return compiledPath.path === requestPath ? { params: {} } : null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Dynamic route - use regex matching
|
|
148
|
+
if (!compiledPath.pattern) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const matches = requestPath.match(compiledPath.pattern);
|
|
153
|
+
if (!matches) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Extract parameters
|
|
158
|
+
const params: Record<string, string> = {};
|
|
159
|
+
compiledPath.paramNames.forEach((name, index) => {
|
|
160
|
+
params[name] = matches[index + 1];
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return { params };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Compile and match in one operation (convenience method)
|
|
168
|
+
*/
|
|
169
|
+
static compileAndMatch(pathPattern: string, requestPath: string): MatchResult | null {
|
|
170
|
+
const compiled = this.compile(pathPattern);
|
|
171
|
+
return this.match(compiled, requestPath);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if a path pattern is static (no parameters)
|
|
176
|
+
*/
|
|
177
|
+
static isStatic(path: string): boolean {
|
|
178
|
+
return !path.includes(':') && !path.includes('*');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get performance statistics
|
|
183
|
+
*/
|
|
184
|
+
static getStats() {
|
|
185
|
+
const totalRequests = this.cacheHits + this.cacheMisses;
|
|
186
|
+
return {
|
|
187
|
+
cacheSize: this.cache.size,
|
|
188
|
+
cacheHits: this.cacheHits,
|
|
189
|
+
cacheMisses: this.cacheMisses,
|
|
190
|
+
hitRate: totalRequests > 0 ? this.cacheHits / totalRequests : 0,
|
|
191
|
+
compilationCount: this.compilationCount,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Clear the cache (useful for testing)
|
|
197
|
+
*/
|
|
198
|
+
static clearCache(): void {
|
|
199
|
+
this.cache.clear();
|
|
200
|
+
this.cacheHits = 0;
|
|
201
|
+
this.cacheMisses = 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Pre-compile multiple paths (cache warming)
|
|
206
|
+
*/
|
|
207
|
+
static precompile(paths: string[]): void {
|
|
208
|
+
paths.forEach(path => this.compile(path));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Legacy compatibility function - maps to PathMatcher.compile
|
|
214
|
+
* @deprecated Use PathMatcher.compile instead
|
|
215
|
+
*/
|
|
216
|
+
export function pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
|
|
217
|
+
const compiled = PathMatcher.compile(path);
|
|
218
|
+
return {
|
|
219
|
+
pattern: compiled.pattern || new RegExp(`^${path.replace(/\//g, '\\/')}$`),
|
|
220
|
+
paramNames: compiled.paramNames,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/core/routing/router.ts
|
|
2
|
+
// FACADE: This class now delegates to UnifiedRouter for actual routing
|
|
3
|
+
// Maintains backward compatibility while using optimized implementation
|
|
4
|
+
import {
|
|
5
|
+
HttpRequest,
|
|
6
|
+
HttpResponse,
|
|
7
|
+
HttpHandler,
|
|
8
|
+
Middleware,
|
|
9
|
+
RouteDefinition,
|
|
10
|
+
} from '../../types/http.js';
|
|
11
|
+
import { createFrameworkLogger } from '../logger/index.js';
|
|
12
|
+
import { UnifiedRouter } from './unified-router.js';
|
|
13
|
+
|
|
14
|
+
export class Router {
|
|
15
|
+
private logger = createFrameworkLogger('Router');
|
|
16
|
+
|
|
17
|
+
// Delegate to shared UnifiedRouter singleton for actual routing
|
|
18
|
+
private unifiedRouter = UnifiedRouter.getInstance();
|
|
19
|
+
|
|
20
|
+
// Maintain route definitions for backward compatibility (getRoutes())
|
|
21
|
+
private routes: RouteDefinition[] = [];
|
|
22
|
+
|
|
23
|
+
get(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
|
|
24
|
+
this.addRoute('GET', path, handlers);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
post(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
|
|
28
|
+
this.addRoute('POST', path, handlers);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
put(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
|
|
32
|
+
this.addRoute('PUT', path, handlers);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
delete(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
|
|
36
|
+
this.addRoute('DELETE', path, handlers);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
patch(path: string, ...handlers: (Middleware | HttpHandler)[]): void {
|
|
40
|
+
this.addRoute('PATCH', path, handlers);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private addRoute(method: string, path: string, handlers: (Middleware | HttpHandler)[]): void {
|
|
44
|
+
const handler = handlers.pop() as HttpHandler;
|
|
45
|
+
const middleware = handlers as Middleware[];
|
|
46
|
+
|
|
47
|
+
// Delegate to UnifiedRouter for actual routing
|
|
48
|
+
this.unifiedRouter.addRoute(method as any, path, handler, middleware);
|
|
49
|
+
|
|
50
|
+
// Keep route definition for backward compatibility (getRoutes())
|
|
51
|
+
const route: RouteDefinition = {
|
|
52
|
+
method,
|
|
53
|
+
path,
|
|
54
|
+
pattern: new RegExp(''), // Not used since we delegate
|
|
55
|
+
paramNames: [],
|
|
56
|
+
handler,
|
|
57
|
+
middleware,
|
|
58
|
+
};
|
|
59
|
+
this.routes.push(route);
|
|
60
|
+
|
|
61
|
+
this.logger.debug(`Delegated route to UnifiedRouter: ${method} ${path}`, 'Facade');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async handle(req: HttpRequest, res: HttpResponse, basePath: string = ''): Promise<boolean> {
|
|
65
|
+
// Adjust path for basePath
|
|
66
|
+
let path = req.path.startsWith(basePath) ? req.path.substring(basePath.length) : req.path;
|
|
67
|
+
if (path === '' || path === undefined) {
|
|
68
|
+
path = '/';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.logger.debug(
|
|
72
|
+
`Router delegating to UnifiedRouter: originalPath="${req.path}", basePath="${basePath}", processedPath="${path}"`,
|
|
73
|
+
'Facade'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Temporarily adjust request path for processing
|
|
77
|
+
const originalPath = req.path;
|
|
78
|
+
req.path = path;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Delegate to UnifiedRouter for actual routing
|
|
82
|
+
return await this.unifiedRouter.handleRequest(req, res);
|
|
83
|
+
} finally {
|
|
84
|
+
// Restore original path
|
|
85
|
+
req.path = originalPath;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getRoutes(): RouteDefinition[] {
|
|
90
|
+
return [...this.routes];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Performance statistics for monitoring (delegates to UnifiedRouter)
|
|
94
|
+
getPerformanceStats() {
|
|
95
|
+
return this.unifiedRouter.getStats();
|
|
96
|
+
}
|
|
97
|
+
}
|