@stacksjs/bun-router 0.0.17 → 0.0.18
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/cli.js +1 -1
- package/dist/container/service-provider.d.ts +3 -3
- package/dist/development/performance-profiler.d.ts +3 -3
- package/dist/development/route-debugger.d.ts +1 -1
- package/dist/development/route-inspector.d.ts +2 -2
- package/dist/observability/integration.d.ts +1 -1
- package/dist/response/macros.d.ts +1 -1
- package/dist/router/index.d.ts +7 -7
- package/dist/router/router.d.ts +0 -32
- package/dist/testing/performance-testing.d.ts +2 -2
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/websocket-testing.d.ts +1 -1
- package/package.json +2 -3
- package/src/auth.ts +0 -489
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -786
- package/src/container/contextual-binding.ts +0 -606
- package/src/container/decorators.ts +0 -368
- package/src/container/index.ts +0 -49
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -733
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -623
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -718
- package/src/index.ts +0 -66
- package/src/middleware/auth.ts +0 -220
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -142
- package/src/middleware/csrf.ts +0 -166
- package/src/middleware/ddos_protection.ts +0 -279
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -275
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -453
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -540
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -945
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -251
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -638
- package/src/middleware/request_tracer.ts +0 -653
- package/src/middleware/response_cache.ts +0 -778
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -109
- package/src/request/enhanced-request.ts +0 -876
- package/src/request/macros.ts +0 -822
- package/src/response/macros.ts +0 -665
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -697
- package/src/router/file-streaming.ts +0 -383
- package/src/router/fluent-routing.ts +0 -866
- package/src/router/group-organization.ts +0 -229
- package/src/router/handler-resolver.ts +0 -327
- package/src/router/http-methods.ts +0 -407
- package/src/router/index.ts +0 -193
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -234
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -302
- package/src/router/route-building.ts +0 -221
- package/src/router/route-compiler.ts +0 -691
- package/src/router/route-matching.ts +0 -368
- package/src/router/route-trie.ts +0 -450
- package/src/router/router.ts +0 -1616
- package/src/router/server.ts +0 -684
- package/src/router/validation-integration.ts +0 -449
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -448
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -422
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -372
- package/src/types.ts +0 -2123
- package/src/url.ts +0 -131
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -395
- package/src/validation/validator.ts +0 -688
- package/src/websocket/clustering.ts +0 -769
package/src/router/route-trie.ts
DELETED
|
@@ -1,450 +0,0 @@
|
|
|
1
|
-
import type { HTTPMethod, Route } from '../types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Route segment types for the trie
|
|
5
|
-
*/
|
|
6
|
-
export type RouteSegmentType = 'static' | 'parameter' | 'wildcard' | 'optional'
|
|
7
|
-
|
|
8
|
-
export interface RouteSegment {
|
|
9
|
-
type: RouteSegmentType
|
|
10
|
-
value: string
|
|
11
|
-
paramName?: string
|
|
12
|
-
pattern?: RegExp
|
|
13
|
-
optional?: boolean
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface CompiledRoute {
|
|
17
|
-
route: Route
|
|
18
|
-
segments: RouteSegment[]
|
|
19
|
-
paramNames: string[]
|
|
20
|
-
priority: number
|
|
21
|
-
staticScore: number
|
|
22
|
-
dynamicScore: number
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Trie node for efficient route matching
|
|
27
|
-
*/
|
|
28
|
-
export class TrieNode {
|
|
29
|
-
children: Map<string, TrieNode> = new Map()
|
|
30
|
-
paramChild: TrieNode | null = null
|
|
31
|
-
wildcardChild: TrieNode | null = null
|
|
32
|
-
routes: Map<HTTPMethod, CompiledRoute> = new Map()
|
|
33
|
-
paramName: string | null = null
|
|
34
|
-
pattern: RegExp | null = null
|
|
35
|
-
isEndpoint: boolean = false
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Add a route to this node
|
|
39
|
-
*/
|
|
40
|
-
addRoute(method: HTTPMethod, compiledRoute: CompiledRoute): void {
|
|
41
|
-
this.routes.set(method, compiledRoute)
|
|
42
|
-
this.isEndpoint = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get route for a specific HTTP method
|
|
47
|
-
*/
|
|
48
|
-
getRoute(method: HTTPMethod): CompiledRoute | undefined {
|
|
49
|
-
return this.routes.get(method)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Get all routes at this node
|
|
54
|
-
*/
|
|
55
|
-
getAllRoutes(): CompiledRoute[] {
|
|
56
|
-
return Array.from(this.routes.values())
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Route matching result with extracted parameters
|
|
62
|
-
*/
|
|
63
|
-
export interface RouteMatch {
|
|
64
|
-
route: Route
|
|
65
|
-
params: Record<string, string>
|
|
66
|
-
score: number
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* High-performance route trie for O(log n) route matching
|
|
71
|
-
*/
|
|
72
|
-
export class RouteTrie {
|
|
73
|
-
private root: TrieNode = new TrieNode()
|
|
74
|
-
private compiledRoutes: Map<string, CompiledRoute> = new Map()
|
|
75
|
-
private methodTries: Map<HTTPMethod, TrieNode> = new Map()
|
|
76
|
-
|
|
77
|
-
constructor() {
|
|
78
|
-
// Initialize method-specific tries for faster filtering
|
|
79
|
-
const methods: HTTPMethod[] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT']
|
|
80
|
-
for (const method of methods) {
|
|
81
|
-
this.methodTries.set(method, new TrieNode())
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Compile a route pattern into segments
|
|
87
|
-
*/
|
|
88
|
-
compileRoute(route: Route): CompiledRoute {
|
|
89
|
-
// Include constraints in the cache key if present
|
|
90
|
-
let cacheKey = `${route.method}:${route.path}`
|
|
91
|
-
if (route.constraints && !Array.isArray(route.constraints)) {
|
|
92
|
-
const constraintsRecord = route.constraints as Record<string, string>
|
|
93
|
-
const constraintKeys = Object.keys(constraintsRecord).sort()
|
|
94
|
-
const constraintString = constraintKeys
|
|
95
|
-
.map(key => `${key}:${constraintsRecord[key]}`)
|
|
96
|
-
.join('|')
|
|
97
|
-
cacheKey = `${route.method}:${route.path}#${constraintString}`
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (this.compiledRoutes.has(cacheKey)) {
|
|
101
|
-
return this.compiledRoutes.get(cacheKey)!
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const segments = this.parsePathSegments(route.path)
|
|
105
|
-
const paramNames = segments
|
|
106
|
-
.filter(seg => seg.type === 'parameter')
|
|
107
|
-
.map(seg => seg.paramName!)
|
|
108
|
-
|
|
109
|
-
// Apply constraints to segments if they exist
|
|
110
|
-
if (route.constraints && !Array.isArray(route.constraints)) {
|
|
111
|
-
const constraintsRecord = route.constraints as Record<string, string>
|
|
112
|
-
for (const segment of segments) {
|
|
113
|
-
if (segment.type === 'parameter' && segment.paramName
|
|
114
|
-
&& constraintsRecord[segment.paramName]) {
|
|
115
|
-
segment.pattern = new RegExp(`^${constraintsRecord[segment.paramName]}$`)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const priority = this.calculateRoutePriority(segments)
|
|
121
|
-
const staticScore = segments.filter(seg => seg.type === 'static').length
|
|
122
|
-
const dynamicScore = segments.filter(seg => seg.type !== 'static').length
|
|
123
|
-
|
|
124
|
-
const compiled: CompiledRoute = {
|
|
125
|
-
route,
|
|
126
|
-
segments,
|
|
127
|
-
paramNames,
|
|
128
|
-
priority,
|
|
129
|
-
staticScore,
|
|
130
|
-
dynamicScore,
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
this.compiledRoutes.set(cacheKey, compiled)
|
|
134
|
-
return compiled
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Parse path into segments
|
|
139
|
-
*/
|
|
140
|
-
private parsePathSegments(path: string): RouteSegment[] {
|
|
141
|
-
const segments: RouteSegment[] = []
|
|
142
|
-
const parts = path.split('/').filter(part => part.length > 0)
|
|
143
|
-
|
|
144
|
-
for (const part of parts) {
|
|
145
|
-
if (part === '*') {
|
|
146
|
-
segments.push({
|
|
147
|
-
type: 'wildcard',
|
|
148
|
-
value: '*',
|
|
149
|
-
})
|
|
150
|
-
}
|
|
151
|
-
else if (part.startsWith('{') && part.endsWith('}')) {
|
|
152
|
-
const paramPart = part.slice(1, -1)
|
|
153
|
-
const [paramName, pattern] = paramPart.split(':')
|
|
154
|
-
|
|
155
|
-
segments.push({
|
|
156
|
-
type: 'parameter',
|
|
157
|
-
value: part,
|
|
158
|
-
paramName: paramName.replace('?', ''),
|
|
159
|
-
pattern: pattern ? new RegExp(`^${pattern}$`) : undefined,
|
|
160
|
-
optional: paramPart.endsWith('?'),
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
else if (part.includes('{')) {
|
|
164
|
-
// Mixed static/dynamic segment like "user-{id}"
|
|
165
|
-
const regex = part.replace(/\{([^}:]+)(?::([^}]+))?\}/g, (_match, _name, pattern) => {
|
|
166
|
-
return pattern ? `(${pattern})` : '([^/]+)'
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
const paramNames = Array.from(part.matchAll(/\{([^}:]+)(?::([^}]+))?\}/g))
|
|
170
|
-
.map(match => match[1].replace('?', ''))
|
|
171
|
-
|
|
172
|
-
segments.push({
|
|
173
|
-
type: 'parameter',
|
|
174
|
-
value: part,
|
|
175
|
-
paramName: paramNames[0], // For simplicity, use first param name
|
|
176
|
-
pattern: new RegExp(`^${regex}$`),
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
segments.push({
|
|
181
|
-
type: 'static',
|
|
182
|
-
value: part,
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return segments
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Calculate route priority (higher = more specific)
|
|
192
|
-
*/
|
|
193
|
-
private calculateRoutePriority(segments: RouteSegment[]): number {
|
|
194
|
-
let priority = 0
|
|
195
|
-
|
|
196
|
-
for (let i = 0; i < segments.length; i++) {
|
|
197
|
-
const segment = segments[i]
|
|
198
|
-
const positionWeight = segments.length - i // Earlier segments have higher weight
|
|
199
|
-
|
|
200
|
-
switch (segment.type) {
|
|
201
|
-
case 'static':
|
|
202
|
-
priority += positionWeight * 1000
|
|
203
|
-
break
|
|
204
|
-
case 'parameter':
|
|
205
|
-
if (segment.pattern) {
|
|
206
|
-
priority += positionWeight * 100 // Constrained params have higher priority
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
priority += positionWeight * 10
|
|
210
|
-
}
|
|
211
|
-
break
|
|
212
|
-
case 'wildcard':
|
|
213
|
-
priority += positionWeight * 1 // Wildcards have lowest priority
|
|
214
|
-
break
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return priority
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Add a route to the trie
|
|
223
|
-
*/
|
|
224
|
-
addRoute(route: Route): void {
|
|
225
|
-
const compiled = this.compileRoute(route)
|
|
226
|
-
|
|
227
|
-
// Add to main trie
|
|
228
|
-
this.insertRoute(this.root, compiled, 0)
|
|
229
|
-
|
|
230
|
-
// Add to method-specific trie for faster filtering
|
|
231
|
-
const methodTrie = this.methodTries.get(route.method as HTTPMethod)
|
|
232
|
-
if (methodTrie) {
|
|
233
|
-
this.insertRoute(methodTrie, compiled, 0)
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Insert compiled route into trie
|
|
239
|
-
*/
|
|
240
|
-
private insertRoute(node: TrieNode, compiled: CompiledRoute, segmentIndex: number): void {
|
|
241
|
-
if (segmentIndex >= compiled.segments.length) {
|
|
242
|
-
node.addRoute(compiled.route.method as HTTPMethod, compiled)
|
|
243
|
-
return
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const segment = compiled.segments[segmentIndex]
|
|
247
|
-
|
|
248
|
-
switch (segment.type) {
|
|
249
|
-
case 'static':
|
|
250
|
-
if (!node.children.has(segment.value)) {
|
|
251
|
-
node.children.set(segment.value, new TrieNode())
|
|
252
|
-
}
|
|
253
|
-
this.insertRoute(node.children.get(segment.value)!, compiled, segmentIndex + 1)
|
|
254
|
-
break
|
|
255
|
-
|
|
256
|
-
case 'parameter':
|
|
257
|
-
if (!node.paramChild) {
|
|
258
|
-
node.paramChild = new TrieNode()
|
|
259
|
-
node.paramChild.paramName = segment.paramName!
|
|
260
|
-
node.paramChild.pattern = segment.pattern || null
|
|
261
|
-
}
|
|
262
|
-
this.insertRoute(node.paramChild, compiled, segmentIndex + 1)
|
|
263
|
-
break
|
|
264
|
-
|
|
265
|
-
case 'wildcard':
|
|
266
|
-
if (!node.wildcardChild) {
|
|
267
|
-
node.wildcardChild = new TrieNode()
|
|
268
|
-
}
|
|
269
|
-
this.insertRoute(node.wildcardChild, compiled, segmentIndex + 1)
|
|
270
|
-
break
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Match a path against the trie
|
|
276
|
-
*/
|
|
277
|
-
match(path: string, method: HTTPMethod): RouteMatch | null {
|
|
278
|
-
const segments = path.split('/').filter(seg => seg.length > 0)
|
|
279
|
-
|
|
280
|
-
// Try method-specific trie first for better performance
|
|
281
|
-
const methodTrie = this.methodTries.get(method)
|
|
282
|
-
if (methodTrie) {
|
|
283
|
-
const result = this.matchSegments(methodTrie, segments, 0, {}, method)
|
|
284
|
-
if (result)
|
|
285
|
-
return result
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Fallback to main trie
|
|
289
|
-
return this.matchSegments(this.root, segments, 0, {}, method)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Recursively match path segments.
|
|
294
|
-
*
|
|
295
|
-
* Candidates are tried in specificity order — static, then parameter,
|
|
296
|
-
* then wildcard — and the first full match wins. This both fixes the
|
|
297
|
-
* old behavior (where a wildcard sibling could shadow a static route)
|
|
298
|
-
* and avoids allocating candidate arrays and params copies per segment:
|
|
299
|
-
* `params` is mutated in place and rolled back on backtrack.
|
|
300
|
-
*/
|
|
301
|
-
private matchSegments(
|
|
302
|
-
node: TrieNode,
|
|
303
|
-
segments: string[],
|
|
304
|
-
segmentIndex: number,
|
|
305
|
-
params: Record<string, string>,
|
|
306
|
-
method: HTTPMethod,
|
|
307
|
-
): RouteMatch | null {
|
|
308
|
-
// If we've matched all segments, check for route
|
|
309
|
-
if (segmentIndex >= segments.length) {
|
|
310
|
-
const compiled = node.getRoute(method)
|
|
311
|
-
if (compiled) {
|
|
312
|
-
return {
|
|
313
|
-
route: compiled.route,
|
|
314
|
-
params,
|
|
315
|
-
score: compiled.priority,
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return null
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const segment = segments[segmentIndex]
|
|
322
|
-
|
|
323
|
-
// Try static match first (highest priority)
|
|
324
|
-
const staticChild = node.children.get(segment)
|
|
325
|
-
if (staticChild) {
|
|
326
|
-
const match = this.matchSegments(staticChild, segments, segmentIndex + 1, params, method)
|
|
327
|
-
if (match)
|
|
328
|
-
return match
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Try parameter match
|
|
332
|
-
const paramNode = node.paramChild
|
|
333
|
-
if (paramNode && paramNode.paramName
|
|
334
|
-
&& (!paramNode.pattern || paramNode.pattern.test(segment))) {
|
|
335
|
-
const previous = params[paramNode.paramName]
|
|
336
|
-
params[paramNode.paramName] = segment
|
|
337
|
-
const match = this.matchSegments(paramNode, segments, segmentIndex + 1, params, method)
|
|
338
|
-
if (match)
|
|
339
|
-
return match
|
|
340
|
-
// Backtrack: restore params before trying lower-priority branches
|
|
341
|
-
if (previous === undefined)
|
|
342
|
-
delete params[paramNode.paramName]
|
|
343
|
-
else
|
|
344
|
-
params[paramNode.paramName] = previous
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Try wildcard match (lowest priority) — consumes all remaining segments
|
|
348
|
-
if (node.wildcardChild) {
|
|
349
|
-
const compiled = node.wildcardChild.getRoute(method)
|
|
350
|
-
if (compiled) {
|
|
351
|
-
params.wildcard = segments.slice(segmentIndex).join('/')
|
|
352
|
-
return {
|
|
353
|
-
route: compiled.route,
|
|
354
|
-
params,
|
|
355
|
-
score: compiled.priority,
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return null
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get all routes for debugging
|
|
365
|
-
*/
|
|
366
|
-
getAllRoutes(): CompiledRoute[] {
|
|
367
|
-
return Array.from(this.compiledRoutes.values())
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Remove a route from the trie
|
|
372
|
-
*/
|
|
373
|
-
removeRoute(route: Route): void {
|
|
374
|
-
// Generate the cache key for this route
|
|
375
|
-
let cacheKey = `${route.method}:${route.path}`
|
|
376
|
-
if (route.constraints && !Array.isArray(route.constraints)) {
|
|
377
|
-
const constraintsRecord = route.constraints as Record<string, string>
|
|
378
|
-
const constraintKeys = Object.keys(constraintsRecord).sort()
|
|
379
|
-
const constraintString = constraintKeys
|
|
380
|
-
.map(key => `${key}:${constraintsRecord[key]}`)
|
|
381
|
-
.join('|')
|
|
382
|
-
cacheKey = `${route.method}:${route.path}#${constraintString}`
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Remove from compiled routes cache
|
|
386
|
-
this.compiledRoutes.delete(cacheKey)
|
|
387
|
-
|
|
388
|
-
// We don't actually need to remove nodes from the trie structure
|
|
389
|
-
// since we'll be adding the updated route back immediately
|
|
390
|
-
// This is a performance optimization to avoid complex trie node removal
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Clear the trie
|
|
395
|
-
*/
|
|
396
|
-
clear(): void {
|
|
397
|
-
this.root = new TrieNode()
|
|
398
|
-
this.compiledRoutes.clear()
|
|
399
|
-
|
|
400
|
-
for (const [method] of this.methodTries) {
|
|
401
|
-
this.methodTries.set(method, new TrieNode())
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Get trie statistics for debugging
|
|
407
|
-
*/
|
|
408
|
-
getStats(): {
|
|
409
|
-
totalRoutes: number
|
|
410
|
-
totalNodes: number
|
|
411
|
-
averageDepth: number
|
|
412
|
-
methodDistribution: Record<string, number>
|
|
413
|
-
} {
|
|
414
|
-
const stats = {
|
|
415
|
-
totalRoutes: this.compiledRoutes.size,
|
|
416
|
-
totalNodes: this.countNodes(this.root),
|
|
417
|
-
averageDepth: 0,
|
|
418
|
-
methodDistribution: {} as Record<string, number>,
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Calculate method distribution
|
|
422
|
-
for (const compiled of this.compiledRoutes.values()) {
|
|
423
|
-
const method = compiled.route.method
|
|
424
|
-
stats.methodDistribution[method] = (stats.methodDistribution[method] || 0) + 1
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return stats
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Count total nodes in trie
|
|
432
|
-
*/
|
|
433
|
-
private countNodes(node: TrieNode): number {
|
|
434
|
-
let count = 1
|
|
435
|
-
|
|
436
|
-
for (const child of node.children.values()) {
|
|
437
|
-
count += this.countNodes(child)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (node.paramChild) {
|
|
441
|
-
count += this.countNodes(node.paramChild)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (node.wildcardChild) {
|
|
445
|
-
count += this.countNodes(node.wildcardChild)
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return count
|
|
449
|
-
}
|
|
450
|
-
}
|